Merge "Add finish icon scale animation for downloading apps" into tm-qpr-dev
diff --git a/quickstep/res/values-sw600dp-land/dimens.xml b/quickstep/res/values-sw600dp-land/dimens.xml
index dc10c24..4ee388a 100644
--- a/quickstep/res/values-sw600dp-land/dimens.xml
+++ b/quickstep/res/values-sw600dp-land/dimens.xml
@@ -15,9 +15,6 @@
 */
 -->
 <resources>
-    <!--  Overview actions  -->
-    <dimen name="overview_actions_top_margin">12dp</dimen>
-
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">48dp</dimen>
 
diff --git a/quickstep/res/values-sw600dp/dimens.xml b/quickstep/res/values-sw600dp/dimens.xml
index 5899814..daf1f63 100644
--- a/quickstep/res/values-sw600dp/dimens.xml
+++ b/quickstep/res/values-sw600dp/dimens.xml
@@ -33,6 +33,8 @@
     <dimen name="overview_page_spacing">36dp</dimen>
     <!--  The space to the left and to the right of the "Clear all" button  -->
     <dimen name="overview_grid_side_margin">64dp</dimen>
+    <!--  Overview actions  -->
+    <dimen name="overview_actions_top_margin">24dp</dimen>
 
     <!-- All Set page -->
     <dimen name="allset_page_margin_horizontal">120dp</dimen>
diff --git a/quickstep/res/values-sw720dp-land/dimens.xml b/quickstep/res/values-sw720dp-land/dimens.xml
deleted file mode 100644
index 02d1189..0000000
--- a/quickstep/res/values-sw720dp-land/dimens.xml
+++ /dev/null
@@ -1,20 +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.
-*/
--->
-<resources>
-    <!--  Overview actions  -->
-    <dimen name="overview_actions_top_margin">20dp</dimen>
-</resources>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 668567e..aa9e272 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -1560,7 +1560,8 @@
             RemoteAnimationTarget[] wallpaperTargets,
             boolean fromUnlock,
             RectF startRect,
-            float startWindowCornerRadius) {
+            float startWindowCornerRadius,
+            boolean fromPredictiveBack) {
         AnimatorSet anim = null;
         RectFSpringAnim rectFSpringAnim = null;
 
@@ -1594,7 +1595,11 @@
                 rectFSpringAnim = getClosingWindowAnimators(
                         anim, appTargets, launcherView, velocity, startRect,
                         startWindowCornerRadius);
-                if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
+                if (mLauncher.isInState(LauncherState.ALL_APPS)) {
+                    // Skip scaling all apps, otherwise FloatingIconView will get wrong
+                    // layout bounds.
+                    skipAllAppsScale = true;
+                } else if (!fromPredictiveBack) {
                     anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y,
                             true /* animateOverviewScrim */, launcherView).getAnimators());
 
@@ -1606,10 +1611,6 @@
 
                     // We play StaggeredWorkspaceAnim as a part of the closing window animation.
                     playWorkspaceReveal = false;
-                } else {
-                    // Skip scaling all apps, otherwise FloatingIconView will get wrong
-                    // layout bounds.
-                    skipAllAppsScale = true;
                 }
             } else {
                 anim.play(getFallbackClosingWindowAnimators(appTargets));
@@ -1686,7 +1687,8 @@
                     new RectF(getWindowTargetBounds(appTargets, getRotationChange(appTargets)));
             Pair<RectFSpringAnim, AnimatorSet> pair = createWallpaperOpenAnimations(
                     appTargets, wallpaperTargets, mFromUnlock, windowTargetBounds,
-                    QuickStepContract.getWindowCornerRadius(mLauncher));
+                    QuickStepContract.getWindowCornerRadius(mLauncher),
+                    false /* fromPredictiveBack */);
 
             mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
             result.setAnimation(pair.second, mLauncher);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 731eea7..4e795d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -598,9 +598,6 @@
     }
 
     public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
-        if (!isUserSetupComplete()) {
-            return;
-        }
         mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
                 .updateValue(darkIntensity);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 7a75661..4ad3858 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -160,7 +160,6 @@
 
         mIconAlignment.finishAnimation();
 
-        Log.d("b/260135164", "onDestroy - updateIconAlphaForHome(1)");
         mLauncher.getHotseat().setIconsAlpha(1f);
         mLauncher.getStateManager().removeStateListener(mStateListener);
 
@@ -405,8 +404,6 @@
                 public void onAnimationEnd(Animator animation) {
                     if (isInStashedState && committed) {
                         // Reset hotseat alpha to default
-                        Log.d("b/260135164",
-                                "playStateTransitionAnim#onAnimationEnd - setIconsAlpha(1)");
                         mLauncher.getHotseat().setIconsAlpha(1);
                     }
                 }
@@ -455,9 +452,6 @@
          * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
          * should not be visible at the same time.
          */
-        Log.d("b/260135164",
-                "updateIconAlphaForHome - setIconsAlpha(" + (hotseatVisible ? 1 : 0)
-                        + "), isTaskbarPresent: " + mLauncher.getDeviceProfile().isTaskbarPresent);
         mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
         mLauncher.getHotseat().setQsbAlpha(
                 mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 86e1911..98c45d5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -32,7 +32,6 @@
 import android.os.Handler;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.util.Log;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -227,10 +226,6 @@
         mActivity = activity;
         UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
                 getUnfoldTransitionProgressProviderForActivity(activity);
-        if (unfoldTransitionProgressProvider == null) {
-            Log.e("b/261320823", "UnfoldTransitionProgressProvider null in setActivity. "
-                    + "Unfold animation for launcher will not work.");
-        }
         mUnfoldProgressProvider.setSourceProvider(unfoldTransitionProgressProvider);
 
         if (mTaskbarActivityContext != null) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index cb2d77a..a7651b6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -43,7 +43,6 @@
 import static com.android.launcher3.testing.shared.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
 import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
-import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
 
@@ -63,7 +62,6 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.SystemProperties;
-import android.util.Log;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.RemoteAnimationTarget;
@@ -751,7 +749,7 @@
                             getSystemService(SensorManager.class),
                             getMainThreadHandler(),
                             getMainExecutor(),
-                            /* backgroundExecutor= */ THREAD_POOL_EXECUTOR,
+                            /* backgroundExecutor= */ UI_HELPER_EXECUTOR,
                             /* tracingTagPrefix= */ "launcher",
                             WindowManagerGlobal.getWindowManagerService()
                     );
@@ -768,7 +766,6 @@
                     mUnfoldTransitionProgressProvider,
                     mRotationChangeProvider
             );
-            Log.d("b/261320823", "initUnfoldTransitionProgressProvider completed");
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index a2adbd7..45f6742 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -369,10 +369,13 @@
                 .getDimensionPixelSize(ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
                         ? R.dimen.taskbar_app_window_threshold_v2
                         : R.dimen.taskbar_app_window_threshold);
-        mTaskbarHomeOverviewThreshold = res.getDimensionPixelSize(
-                ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
-                        ? R.dimen.taskbar_home_overview_threshold_v2
-                        : R.dimen.taskbar_home_overview_threshold);
+        boolean swipeWillNotShowTaskbar = mTaskbarAlreadyOpen;
+        mTaskbarHomeOverviewThreshold = swipeWillNotShowTaskbar
+                ? 0
+                : res.getDimensionPixelSize(
+                        ENABLE_TASKBAR_REVISED_THRESHOLDS.get()
+                                ? R.dimen.taskbar_home_overview_threshold_v2
+                                : R.dimen.taskbar_home_overview_threshold);
         mTaskbarCatchUpThreshold = res.getDimensionPixelSize(R.dimen.taskbar_catch_up_threshold);
     }
 
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 3edbbdf..03042c9 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -290,7 +290,8 @@
                     new RemoteAnimationTarget[0],
                     false /* fromUnlock */,
                     mCurrentRect,
-                    cornerRadius);
+                    cornerRadius,
+                    mBackInProgress /* fromPredictiveBack */);
         startTransitionAnimations(pair.first, pair.second);
         mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
     }
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index f8b6966..4c66dbb 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -321,9 +321,9 @@
         if (enable && !mInOverview && !TestProtocol.sDisableSensorRotation) {
             // Clear any previous state from sensor manager
             mSensorRotation = mCurrentAppRotation;
-            mOrientationListener.enable();
+            UI_HELPER_EXECUTOR.execute(mOrientationListener::enable);
         } else {
-            mOrientationListener.disable();
+            UI_HELPER_EXECUTOR.execute(mOrientationListener::disable);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
index 5efc45e..3d5c143 100644
--- a/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
+++ b/quickstep/src/com/android/quickstep/logging/SettingsChangeLogger.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep.logging;
 
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
 import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
 import static com.android.launcher3.LauncherPrefs.getPrefs;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED;
@@ -39,6 +40,7 @@
 import android.util.Xml;
 
 import com.android.launcher3.AutoInstallsLayout;
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
@@ -178,7 +180,7 @@
                 logger::log);
 
         SharedPreferences prefs = getPrefs(mContext);
-        logger.log(prefs.getBoolean(KEY_THEMED_ICONS, false)
+        logger.log(LauncherPrefs.get(mContext).get(THEMED_ICONS)
                 ? LAUNCHER_THEMED_ICON_ENABLED
                 : LAUNCHER_THEMED_ICON_DISABLED);
 
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index ad54a70..cd5edab 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -124,7 +124,7 @@
                 for (int i = hotseatIcons.getChildCount() - 1; i >= 0; i--) {
                     View child = hotseatIcons.getChildAt(i);
                     CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
-                    addStaggeredAnimationForView(child, lp.cellY + 1, totalRows, duration);
+                    addStaggeredAnimationForView(child, lp.getCellY() + 1, totalRows, duration);
                 }
             } else {
                 final int hotseatRow, qsbRow;
@@ -194,7 +194,7 @@
         for (int i = itemsContainer.getChildCount() - 1; i >= 0; i--) {
             View child = itemsContainer.getChildAt(i);
             CellLayoutLayoutParams lp = ((CellLayoutLayoutParams) child.getLayoutParams());
-            addStaggeredAnimationForView(child, lp.cellY + lp.cellVSpan, totalRows, duration);
+            addStaggeredAnimationForView(child, lp.getCellY() + lp.cellVSpan, totalRows, duration);
         }
 
         mAnimators.addListener(new AnimatorListenerAdapter() {
diff --git a/res/values/id.xml b/res/values/id.xml
index 9fc0ff8..52a7e98 100644
--- a/res/values/id.xml
+++ b/res/values/id.xml
@@ -37,4 +37,6 @@
     <item type="id" name="quick_settings_button" />
     <item type="id" name="notifications_button" />
     <item type="id" name="cache_entry_tag_id" />
+
+    <item type="id" name="saved_clip_children_tag_id" />
 </resources>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 76a91c0..85bd2d3 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -268,8 +268,10 @@
 
         CellLayoutLayoutParams lp = (CellLayoutLayoutParams) mWidgetView.getLayoutParams();
         ItemInfo widgetInfo = (ItemInfo) mWidgetView.getTag();
-        lp.cellX = lp.tmpCellX = widgetInfo.cellX;
-        lp.cellY = lp.tmpCellY = widgetInfo.cellY;
+        lp.setCellX(widgetInfo.cellX);
+        lp.setTmpCellX(widgetInfo.cellX);
+        lp.setCellY(widgetInfo.cellY);
+        lp.setTmpCellY(widgetInfo.cellY);
         lp.cellHSpan = widgetInfo.spanX;
         lp.cellVSpan = widgetInfo.spanY;
         lp.isLockedToGrid = true;
@@ -425,8 +427,8 @@
 
         int spanX = lp.cellHSpan;
         int spanY = lp.cellVSpan;
-        int cellX = lp.useTmpCoords ? lp.tmpCellX : lp.cellX;
-        int cellY = lp.useTmpCoords ? lp.tmpCellY : lp.cellY;
+        int cellX = lp.useTmpCoords ? lp.getTmpCellX() : lp.getCellX();
+        int cellY = lp.useTmpCoords ? lp.getTmpCellY() : lp.getCellY();
 
         // For each border, we bound the resizing based on the minimum width, and the maximum
         // expandability.
@@ -467,8 +469,8 @@
                         mLauncher.getString(R.string.widget_resized, spanX, spanY));
             }
 
-            lp.tmpCellX = cellX;
-            lp.tmpCellY = cellY;
+            lp.setTmpCellX(cellX);
+            lp.setTmpCellY(cellY);
             lp.cellHSpan = spanX;
             lp.cellVSpan = spanY;
             mRunningVInc += vSpanDelta;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ecfd230..05b225c 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -623,8 +623,8 @@
                 if (alpha <= 0) continue;
 
                 mVisualizeGridPaint.setAlpha(255);
-                int x = mDragOutlines[i].cellX;
-                int y = mDragOutlines[i].cellY;
+                int x = mDragOutlines[i].getCellX();
+                int y = mDragOutlines[i].getCellY();
                 int spanX = mDragOutlines[i].cellHSpan;
                 int spanY = mDragOutlines[i].cellVSpan;
 
@@ -764,7 +764,8 @@
 
         // Generate an id for each view, this assumes we have at most 256x256 cells
         // per workspace screen
-        if (lp.cellX >= 0 && lp.cellX <= mCountX - 1 && lp.cellY >= 0 && lp.cellY <= mCountY - 1) {
+        if (lp.getCellX() >= 0 && lp.getCellX() <= mCountX - 1
+                && lp.getCellY() >= 0 && lp.getCellY() <= mCountY - 1) {
             // If the horizontal or vertical span is set to -1, it is taken to
             // mean that it spans the extent of the CellLayout
             if (lp.cellHSpan < 0) lp.cellHSpan = mCountX;
@@ -1072,7 +1073,7 @@
 
             if (adjustOccupied) {
                 GridOccupancy occupied = permanent ? mOccupied : mTmpOccupied;
-                occupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+                occupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false);
                 occupied.markCells(cellX, cellY, lp.cellHSpan, lp.cellVSpan, true);
             }
 
@@ -1083,11 +1084,11 @@
             final int oldY = lp.y;
             lp.isLockedToGrid = true;
             if (permanent) {
-                lp.cellX = info.cellX = cellX;
-                lp.cellY = info.cellY = cellY;
+                lp.setCellX(info.cellX = cellX);
+                lp.setCellY(info.cellY = cellY);
             } else {
-                lp.tmpCellX = cellX;
-                lp.tmpCellY = cellY;
+                lp.setTmpCellX(cellX);
+                lp.setTmpCellY(cellY);
             }
             clc.setupLp(child);
             final int newX = lp.x;
@@ -1167,8 +1168,8 @@
             mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
 
             CellLayoutLayoutParams cell = mDragOutlines[mDragOutlineCurrent];
-            cell.cellX = cellX;
-            cell.cellY = cellY;
+            cell.setCellX(cellX);
+            cell.setCellY(cellY);
             cell.cellHSpan = spanX;
             cell.cellVSpan = spanY;
 
@@ -1385,8 +1386,8 @@
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
             CellAndSpan c = solution.map.get(child);
             if (c != null) {
-                lp.tmpCellX = c.cellX;
-                lp.tmpCellY = c.cellY;
+                lp.setTmpCellX(c.cellX);
+                lp.setTmpCellY(c.cellY);
                 lp.cellHSpan = c.spanX;
                 lp.cellVSpan = c.spanY;
                 mTmpOccupied.markCells(c, true);
@@ -1433,7 +1434,7 @@
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
             if (c != null && !skip && (child instanceof Reorderable)) {
                 ReorderPreviewAnimation rha = new ReorderPreviewAnimation((Reorderable) child,
-                        mode, lp.cellX, lp.cellY, c.cellX, c.cellY, c.spanX, c.spanY);
+                        mode, lp.getCellX(), lp.getCellY(), c.cellX, c.cellY, c.spanX, c.spanY);
                 rha.animate();
             }
         }
@@ -1626,12 +1627,14 @@
             // We do a null check here because the item info can be null in the case of the
             // AllApps button in the hotseat.
             if (info != null && child != dragView) {
-                final boolean requiresDbUpdate = (info.cellX != lp.tmpCellX
-                        || info.cellY != lp.tmpCellY || info.spanX != lp.cellHSpan
+                final boolean requiresDbUpdate = (info.cellX != lp.getTmpCellX()
+                        || info.cellY != lp.getTmpCellY() || info.spanX != lp.cellHSpan
                         || info.spanY != lp.cellVSpan);
 
-                info.cellX = lp.cellX = lp.tmpCellX;
-                info.cellY = lp.cellY = lp.tmpCellY;
+                lp.setCellX(lp.getTmpCellX());
+                info.cellX = lp.getTmpCellX();
+                info.cellY = lp.getTmpCellY();
+                lp.setCellY(lp.getTmpCellY());
                 info.spanX = lp.cellHSpan;
                 info.spanY = lp.cellVSpan;
 
@@ -1697,7 +1700,8 @@
             if (child == dragView) continue;
             CellLayoutLayoutParams
                     lp = (CellLayoutLayoutParams) child.getLayoutParams();
-            r1.set(lp.cellX, lp.cellY, lp.cellX + lp.cellHSpan, lp.cellY + lp.cellVSpan);
+            r1.set(lp.getCellX(), lp.getCellY(), lp.getCellX() + lp.cellHSpan,
+                    lp.getCellY() + lp.cellVSpan);
             if (Rect.intersects(r0, r1)) {
                 mIntersectingViews.add(child);
                 if (boundingRect != null) {
@@ -1723,11 +1727,11 @@
                 View child = mShortcutsAndWidgets.getChildAt(i);
                 CellLayoutLayoutParams
                         lp = (CellLayoutLayoutParams) child.getLayoutParams();
-                if (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY) {
-                    lp.tmpCellX = lp.cellX;
-                    lp.tmpCellY = lp.cellY;
-                    animateChildToPosition(child, lp.cellX, lp.cellY, REORDER_ANIMATION_DURATION,
-                            0, false, false);
+                if (lp.getTmpCellX() != lp.getCellX() || lp.getTmpCellY() != lp.getCellY()) {
+                    lp.setTmpCellX(lp.getCellX());
+                    lp.setTmpCellY(lp.getCellY());
+                    animateChildToPosition(child, lp.getCellX(), lp.getCellY(),
+                            REORDER_ANIMATION_DURATION, 0, false, false);
                 }
             }
             setItemPlacementDirty(false);
@@ -2449,9 +2453,9 @@
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
             CellAndSpan c;
             if (temp) {
-                c = new CellAndSpan(lp.tmpCellX, lp.tmpCellY, lp.cellHSpan, lp.cellVSpan);
+                c = new CellAndSpan(lp.getTmpCellX(), lp.getTmpCellY(), lp.cellHSpan, lp.cellVSpan);
             } else {
-                c = new CellAndSpan(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan);
+                c = new CellAndSpan(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan);
             }
             solution.add(child, c);
         }
@@ -2794,7 +2798,7 @@
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         CellLayoutLayoutParams
                 lp = (CellLayoutLayoutParams) view.getLayoutParams();
-        mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, true);
+        mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, true);
     }
 
     public void markCellsAsUnoccupiedForView(View view) {
@@ -2807,7 +2811,7 @@
         if (view == null || view.getParent() != mShortcutsAndWidgets) return;
         CellLayoutLayoutParams
                 lp = (CellLayoutLayoutParams) view.getLayoutParams();
-        mOccupied.markCells(lp.cellX, lp.cellY, lp.cellHSpan, lp.cellVSpan, false);
+        mOccupied.markCells(lp.getCellX(), lp.getCellY(), lp.cellHSpan, lp.cellVSpan, false);
     }
 
     public int getDesiredWidth() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4965936..3461601 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -18,7 +18,8 @@
 
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED;
 
-import static com.android.launcher3.LauncherPrefs.getDevicePrefs;
+import static com.android.launcher3.LauncherPrefs.ICON_STATE;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
@@ -55,7 +56,7 @@
 public class LauncherAppState implements SafeCloseable {
 
     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
-    private static final String KEY_ICON_STATE = "pref_icon_shape_path";
+    public static final String KEY_ICON_STATE = "pref_icon_shape_path";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -117,10 +118,9 @@
                 observer, MODEL_EXECUTOR.getHandler());
         mOnTerminateCallback.add(iconChangeTracker::close);
         MODEL_EXECUTOR.execute(observer::verifyIconChanged);
-        SharedPreferences prefs = LauncherPrefs.getPrefs(mContext);
-        prefs.registerOnSharedPreferenceChangeListener(observer);
+        LauncherPrefs.get(context).addListener(observer, THEMED_ICONS);
         mOnTerminateCallback.add(
-                () -> prefs.unregisterOnSharedPreferenceChangeListener(observer));
+                () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS));
 
         InstallSessionTracker installSessionTracker =
                 InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
@@ -207,12 +207,12 @@
         public void onSystemIconStateChanged(String iconState) {
             IconShape.init(mContext);
             refreshAndReloadLauncher();
-            getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
+            LauncherPrefs.get(mContext).put(ICON_STATE, iconState);
         }
 
         void verifyIconChanged() {
             String iconState = mIconProvider.getSystemIconState();
-            if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
+            if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) {
                 onSystemIconStateChanged(iconState);
             }
         }
diff --git a/src/com/android/launcher3/LauncherPrefs.kt b/src/com/android/launcher3/LauncherPrefs.kt
index 0d6ed04..1fb2dce 100644
--- a/src/com/android/launcher3/LauncherPrefs.kt
+++ b/src/com/android/launcher3/LauncherPrefs.kt
@@ -2,24 +2,255 @@
 
 import android.content.Context
 import android.content.SharedPreferences
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import androidx.annotation.VisibleForTesting
+import com.android.launcher3.allapps.WorkProfileManager
+import com.android.launcher3.model.DeviceGridState
+import com.android.launcher3.pm.InstallSessionHelper
+import com.android.launcher3.provider.RestoreDbTask
+import com.android.launcher3.util.MainThreadInitializedObject
+import com.android.launcher3.util.Themes
 
-object LauncherPrefs {
+/**
+ * Use same context for shared preferences, so that we use a single cached instance
+ * TODO(b/262721340): Replace all direct SharedPreference refs with LauncherPrefs / Item methods.
+ */
+class LauncherPrefs(private val context: Context) {
 
-    @JvmStatic
-    fun getPrefs(context: Context): SharedPreferences {
-        // Use application context for shared preferences, so that we use a single cached instance
-        return context.applicationContext.getSharedPreferences(
-            LauncherFiles.SHARED_PREFERENCES_KEY,
-            Context.MODE_PRIVATE
-        )
+    /**
+     * Retrieves the value for an [Item] from [SharedPreferences]. It handles method typing via the
+     * default value type, and will throw an error if the type of the item provided is not a
+     * `String`, `Boolean`, `Float`, `Int`, `Long`, or `Set<String>`.
+     */
+    @Suppress("IMPLICIT_CAST_TO_ANY", "UNCHECKED_CAST")
+    fun <T : Any> get(item: Item<T>): T {
+        val sp = context.getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
+
+        return when (item.defaultValue::class.java) {
+            String::class.java -> sp.getString(item.sharedPrefKey, item.defaultValue as String)
+            Boolean::class.java,
+            java.lang.Boolean::class.java ->
+                sp.getBoolean(item.sharedPrefKey, item.defaultValue as Boolean)
+            Int::class.java,
+            java.lang.Integer::class.java -> sp.getInt(item.sharedPrefKey, item.defaultValue as Int)
+            Float::class.java,
+            java.lang.Float::class.java ->
+                sp.getFloat(item.sharedPrefKey, item.defaultValue as Float)
+            Long::class.java,
+            java.lang.Long::class.java -> sp.getLong(item.sharedPrefKey, item.defaultValue as Long)
+            Set::class.java -> sp.getStringSet(item.sharedPrefKey, item.defaultValue as Set<String>)
+            else ->
+                throw IllegalArgumentException(
+                    "item type: ${item.defaultValue::class.java}" +
+                        " is not compatible with sharedPref methods"
+                )
+        }
+            as T
     }
 
-    @JvmStatic
-    fun getDevicePrefs(context: Context): SharedPreferences {
-        // Use application context for shared preferences, so that we use a single cached instance
-        return context.applicationContext.getSharedPreferences(
-            LauncherFiles.DEVICE_PREFERENCES_KEY,
-            Context.MODE_PRIVATE
-        )
+    /**
+     * Stores each of the values provided in `SharedPreferences` according to the configuration
+     * contained within the associated items provided. Internally, it uses apply, so the caller
+     * cannot assume that the values that have been put are immediately available for use.
+     *
+     * The forEach loop is necessary here since there is 1 `SharedPreference.Editor` returned from
+     * prepareToPutValue(itemsToValues) for every distinct `SharedPreferences` file present in the
+     * provided item configurations.
+     */
+    fun put(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
+        prepareToPutValues(itemsToValues).forEach { it.apply() }
+
+    /**
+     * Stores the value provided in `SharedPreferences` according to the item configuration provided
+     * It is asynchronous, so the caller can't assume that the value put is immediately available.
+     */
+    fun <T : Any> put(item: Item<T>, value: T): Unit =
+        context
+            .getSharedPreferences(item.sharedPrefFile, Context.MODE_PRIVATE)
+            .edit()
+            .putValue(item, value)
+            .apply()
+
+    /**
+     * Synchronously stores all the values provided according to their associated Item
+     * configuration.
+     */
+    fun putSync(vararg itemsToValues: Pair<Item<*>, Any>): Unit =
+        prepareToPutValues(itemsToValues).forEach { it.commit() }
+
+    /**
+     * Update each shared preference file with the item - value pairs provided. This method is
+     * optimized to avoid retrieving the same shared preference file multiple times.
+     *
+     * @return `List<SharedPreferences.Editor>` 1 for each distinct shared preference file among the
+     * items given as part of the itemsToValues parameter
+     */
+    private fun prepareToPutValues(
+        itemsToValues: Array<out Pair<Item<*>, Any>>
+    ): List<SharedPreferences.Editor> =
+        itemsToValues
+            .groupBy { it.first.sharedPrefFile }
+            .map { fileToItemValueList ->
+                context
+                    .getSharedPreferences(fileToItemValueList.key, Context.MODE_PRIVATE)
+                    .edit()
+                    .apply {
+                        fileToItemValueList.value.forEach { itemToValue ->
+                            putValue(itemToValue.first, itemToValue.second)
+                        }
+                    }
+            }
+
+    /**
+     * Handles adding values to `SharedPreferences` regardless of type. This method is especially
+     * helpful for updating `SharedPreferences` values for `List<<Item>Any>` that have multiple
+     * types of Item values.
+     */
+    @Suppress("UNCHECKED_CAST")
+    private fun SharedPreferences.Editor.putValue(
+        item: Item<*>,
+        value: Any
+    ): SharedPreferences.Editor =
+        when (value::class.java) {
+            String::class.java -> putString(item.sharedPrefKey, value as String)
+            Boolean::class.java,
+            java.lang.Boolean::class.java -> putBoolean(item.sharedPrefKey, value as Boolean)
+            Int::class.java,
+            java.lang.Integer::class.java -> putInt(item.sharedPrefKey, value as Int)
+            Float::class.java,
+            java.lang.Float::class.java -> putFloat(item.sharedPrefKey, value as Float)
+            Long::class.java,
+            java.lang.Long::class.java -> putLong(item.sharedPrefKey, value as Long)
+            Set::class.java -> putStringSet(item.sharedPrefKey, value as Set<String>)
+            else ->
+                throw IllegalArgumentException(
+                    "item type: " +
+                        "${item.defaultValue!!::class} is not compatible with sharedPref methods"
+                )
+        }
+
+    /**
+     * After calling this method, the listener will be notified of any future updates to the
+     * `SharedPreferences` files associated with the provided list of items. The listener will need
+     * to filter update notifications so they don't activate for non-relevant updates.
+     */
+    fun addListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
+        items
+            .map { it.sharedPrefFile }
+            .distinct()
+            .forEach {
+                context
+                    .getSharedPreferences(it, Context.MODE_PRIVATE)
+                    .registerOnSharedPreferenceChangeListener(listener)
+            }
     }
+
+    /**
+     * Stops the listener from getting notified of any more updates to any of the
+     * `SharedPreferences` files associated with any of the provided list of [Item].
+     */
+    fun removeListener(listener: OnSharedPreferenceChangeListener, vararg items: Item<*>) {
+        // If a listener is not registered to a SharedPreference, unregistering it does nothing
+        items
+            .map { it.sharedPrefFile }
+            .distinct()
+            .forEach {
+                context
+                    .getSharedPreferences(it, Context.MODE_PRIVATE)
+                    .unregisterOnSharedPreferenceChangeListener(listener)
+            }
+    }
+
+    /**
+     * Checks if all the provided [Item] have values stored in their corresponding
+     * `SharedPreferences` files.
+     */
+    fun has(vararg items: Item<*>): Boolean {
+        items
+            .groupBy { it.sharedPrefFile }
+            .forEach { (file, itemsSublist) ->
+                val prefs: SharedPreferences =
+                    context.getSharedPreferences(file, Context.MODE_PRIVATE)
+                if (!itemsSublist.none { !prefs.contains(it.sharedPrefKey) }) return false
+            }
+        return true
+    }
+
+    /**
+     * Asynchronously removes the [Item]'s value from its corresponding `SharedPreferences` file.
+     */
+    fun remove(vararg items: Item<*>) = prepareToRemove(items).forEach { it.apply() }
+
+    /** Synchronously removes the [Item]'s value from its corresponding `SharedPreferences` file. */
+    fun removeSync(vararg items: Item<*>) = prepareToRemove(items).forEach { it.commit() }
+
+    /**
+     * Creates `SharedPreferences.Editor` transactions for removing all the provided [Item] values
+     * from their respective `SharedPreferences` files. These returned `Editors` can then be
+     * committed or applied for synchronous or async behavior.
+     */
+    private fun prepareToRemove(items: Array<out Item<*>>): List<SharedPreferences.Editor> =
+        items
+            .groupBy { it.sharedPrefFile }
+            .map { (file, items) ->
+                context.getSharedPreferences(file, Context.MODE_PRIVATE).edit().also { editor ->
+                    items.forEach { item -> editor.remove(item.sharedPrefKey) }
+                }
+            }
+
+    companion object {
+        @JvmField var INSTANCE = MainThreadInitializedObject { LauncherPrefs(it) }
+
+        @JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
+
+        @JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "")
+        @JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false)
+        @JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
+        @JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
+        @JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "")
+        @JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1)
+        @JvmField
+        val DEVICE_TYPE =
+            backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
+        @JvmField val DB_FILE = backedUpItem(DeviceGridState.KEY_DB_FILE, "")
+        @JvmField
+        val RESTORE_DEVICE =
+            backedUpItem(RestoreDbTask.RESTORED_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE)
+        @JvmField val APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_IDS, "")
+        @JvmField val OLD_APP_WIDGET_IDS = backedUpItem(RestoreDbTask.APPWIDGET_OLD_IDS, "")
+
+        @VisibleForTesting
+        @JvmStatic
+        fun <T> backedUpItem(sharedPrefKey: String, defaultValue: T): Item<T> =
+            Item(sharedPrefKey, LauncherFiles.SHARED_PREFERENCES_KEY, defaultValue)
+
+        @VisibleForTesting
+        @JvmStatic
+        fun <T> nonRestorableItem(sharedPrefKey: String, defaultValue: T): Item<T> =
+            Item(sharedPrefKey, LauncherFiles.DEVICE_PREFERENCES_KEY, defaultValue)
+
+        @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
+        @JvmStatic
+        fun getPrefs(context: Context): SharedPreferences {
+            // Use application context for shared preferences, so we use single cached instance
+            return context.applicationContext.getSharedPreferences(
+                LauncherFiles.SHARED_PREFERENCES_KEY,
+                Context.MODE_PRIVATE
+            )
+        }
+
+        @Deprecated("Don't use shared preferences directly. Use other LauncherPref methods.")
+        @JvmStatic
+        fun getDevicePrefs(context: Context): SharedPreferences {
+            // Use application context for shared preferences, so we use a single cached instance
+            return context.applicationContext.getSharedPreferences(
+                LauncherFiles.DEVICE_PREFERENCES_KEY,
+                Context.MODE_PRIVATE
+            )
+        }
+    }
+}
+
+data class Item<T>(val sharedPrefKey: String, val sharedPrefFile: String, val defaultValue: T) {
+    fun to(value: T): Pair<Item<T>, T> = Pair(this, value)
 }
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 7a74d7e..5f39f7e 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -83,8 +83,8 @@
             View child = getChildAt(i);
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) child.getLayoutParams();
 
-            if ((lp.cellX <= cellX) && (cellX < lp.cellX + lp.cellHSpan)
-                    && (lp.cellY <= cellY) && (cellY < lp.cellY + lp.cellVSpan)) {
+            if ((lp.getCellX() <= cellX) && (cellX < lp.getCellX() + lp.cellHSpan)
+                    && (lp.getCellY() <= cellY) && (cellY < lp.getCellY() + lp.cellVSpan)) {
                 return child;
             }
         }
@@ -260,7 +260,7 @@
         lp.canReorder = false;
         if (mContainerType == HOTSEAT) {
             CellLayout cl = (CellLayout) getParent();
-            cl.setFolderLeaveBehindCell(lp.cellX, lp.cellY);
+            cl.setFolderLeaveBehindCell(lp.getCellX(), lp.getCellY());
         }
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2b9c135..460c658 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1757,7 +1757,8 @@
     boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
         if (dropOverView != null) {
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
-            if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
+            if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX()
+                    || lp.getTmpCellY() != lp.getCellY())) {
                 return false;
             }
         }
@@ -1792,7 +1793,8 @@
     boolean willAddToExistingUserFolder(ItemInfo dragInfo, View dropOverView) {
         if (dropOverView != null) {
             CellLayoutLayoutParams lp = (CellLayoutLayoutParams) dropOverView.getLayoutParams();
-            if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
+            if (lp.useTmpCoords && (lp.getTmpCellX() != lp.getCellX()
+                    || lp.getTmpCellY() != lp.getCellY())) {
                 return false;
             }
         }
@@ -2009,8 +2011,10 @@
 
                     // update the item's position after drop
                     CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
-                    lp.cellX = lp.tmpCellX = mTargetCell[0];
-                    lp.cellY = lp.tmpCellY = mTargetCell[1];
+                    lp.setTmpCellX(mTargetCell[0]);
+                    lp.setCellX(mTargetCell[0]);
+                    lp.setTmpCellY(mTargetCell[1]);
+                    lp.setCellY(mTargetCell[1]);
                     lp.cellHSpan = item.spanX;
                     lp.cellVSpan = item.spanY;
                     lp.isLockedToGrid = true;
@@ -2024,7 +2028,7 @@
                                 (LauncherAppWidgetHostView) cell, dropTargetLayout);
                     }
                     mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
-                            lp.cellX, lp.cellY, item.spanX, item.spanY);
+                            lp.getCellX(), lp.getCellY(), item.spanX, item.spanY);
                 } else {
                     if (!returnToOriginalCellToPreventShuffling) {
                         onNoCellFound(dropTargetLayout, d.dragInfo, d.logInstanceId);
@@ -2035,8 +2039,8 @@
 
                     // If we can't find a drop location, we return the item to its original position
                     CellLayoutLayoutParams lp = (CellLayoutLayoutParams) cell.getLayoutParams();
-                    mTargetCell[0] = lp.cellX;
-                    mTargetCell[1] = lp.cellY;
+                    mTargetCell[0] = lp.getCellX();
+                    mTargetCell[1] = lp.getCellY();
                     CellLayout layout = (CellLayout) cell.getParent().getParent();
                     layout.markCellsAsOccupiedForView(cell);
                 }
diff --git a/src/com/android/launcher3/WorkspaceLayoutManager.java b/src/com/android/launcher3/WorkspaceLayoutManager.java
index 91e12fa..bf448c9 100644
--- a/src/com/android/launcher3/WorkspaceLayoutManager.java
+++ b/src/com/android/launcher3/WorkspaceLayoutManager.java
@@ -117,8 +117,8 @@
             lp = new CellLayoutLayoutParams(x, y, spanX, spanY, screenId);
         } else {
             lp = (CellLayoutLayoutParams) genericLp;
-            lp.cellX = x;
-            lp.cellY = y;
+            lp.setCellX(x);
+            lp.setCellY(y);
             lp.cellHSpan = spanX;
             lp.cellVSpan = spanY;
         }
@@ -136,7 +136,8 @@
             // TODO: This branch occurs when the workspace is adding views
             // outside of the defined grid
             // maybe we should be deleting these items from the LauncherModel?
-            Log.e(TAG, "Failed to add to item at (" + lp.cellX + "," + lp.cellY + ") to CellLayout");
+            Log.e(TAG, "Failed to add to item at (" + lp.getCellX() + "," + lp.getCellY()
+                    + ") to CellLayout");
         }
 
         child.setHapticFeedbackEnabled(false);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 063b82e..3c316b8 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -259,7 +259,7 @@
             if (((host.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)
                     && layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY))
                     || !layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY)) {
-                lp.cellX --;
+                lp.setCellX(lp.getCellX() - 1);
                 info.cellX --;
             }
             lp.cellHSpan ++;
@@ -269,7 +269,7 @@
             info.spanX --;
         } else if (action == R.string.action_increase_height) {
             if (!layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1)) {
-                lp.cellY --;
+                lp.setCellY(lp.getCellY() - 1);
                 info.cellY --;
             }
             lp.cellVSpan ++;
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 63e6d13..112d47e 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -21,6 +21,7 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 
+import androidx.annotation.Px;
 import androidx.core.view.accessibility.AccessibilityEventCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityRecordCompat;
@@ -144,6 +145,19 @@
         }
 
         /**
+         * We need to extend all apps' RecyclerView's bottom by 5% of view height to ensure extra
+         * roll(s) of app icons is rendered at the bottom, so that they can fill the bottom gap
+         * created during predictive back's scale animation from all apps to home.
+         */
+        @Override
+        protected void calculateExtraLayoutSpace(RecyclerView.State state, int[] extraLayoutSpace) {
+            super.calculateExtraLayoutSpace(state, extraLayoutSpace);
+            @Px int extraSpacePx = (int) (getHeight()
+                    * (1 - AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE) / 2);
+            extraLayoutSpace[1] = Math.max(extraLayoutSpace[1], extraSpacePx);
+        }
+
+        /**
          * Returns the number of rows before {@param adapterPosition}, including this position
          * which should not be counted towards the collection info.
          */
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index d308fcb..8cb31fa 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -17,11 +17,15 @@
 
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import static com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SCROLLED_UP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_BEGIN;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_COLLAPSE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORK_FAB_BUTTON_EXTEND;
 import static com.android.launcher3.util.LogConfig.SEARCH_LOGGING;
 
 import android.content.Context;
@@ -275,17 +279,37 @@
     private void logCumulativeVerticalScroll() {
         ActivityContext context = ActivityContext.lookupContext(getContext());
         StatsLogManager mgr = context.getStatsLogManager();
-        ExtendedEditText editText = context.getAppsView().getSearchUiManager().getEditText();
+        ActivityAllAppsContainerView<?> appsView = context.getAppsView();
+        ExtendedEditText editText = appsView.getSearchUiManager().getEditText();
         ContainerInfo containerInfo = ContainerInfo.newBuilder().setSearchResultContainer(
                 SearchResultContainer
                         .newBuilder()
                         .setQueryLength((editText == null) ? -1 : editText.length())).build();
-
-        // mCumulativeVerticalScroll == 0 when user comes back to original position, we don't
-        // know the direction of scrolling.
-        mgr.logger().withContainerInfo(containerInfo).log(
-                mCumulativeVerticalScroll == 0 ? LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION
-                        : (mCumulativeVerticalScroll > 0) ? LAUNCHER_ALLAPPS_SCROLLED_DOWN
-                                : LAUNCHER_ALLAPPS_SCROLLED_UP);
+        if (mCumulativeVerticalScroll == 0) {
+            // mCumulativeVerticalScroll == 0 when user comes back to original position, we
+            // don't know the direction of scrolling.
+            mgr.logger().withContainerInfo(containerInfo).log(
+                    LAUNCHER_ALLAPPS_SCROLLED_UNKNOWN_DIRECTION);
+            return;
+        } else if (appsView.isSearching()) {
+            // In search results page
+            mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+                    ? LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN
+                    : LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP);
+            return;
+        } else if (appsView.mViewPager != null) {
+            int currentPage = appsView.mViewPager.getCurrentPage();
+            if (currentPage == BaseAllAppsContainerView.AdapterHolder.WORK) {
+                // In work A-Z list
+                mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+                        ? LAUNCHER_WORK_FAB_BUTTON_COLLAPSE
+                        : LAUNCHER_WORK_FAB_BUTTON_EXTEND);
+                return;
+            }
+        }
+        // In personal A-Z list
+        mgr.logger().withContainerInfo(containerInfo).log((mCumulativeVerticalScroll > 0)
+                ? LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN
+                : LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP);
     }
 }
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 24bfedb..6f6f86b 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -32,14 +32,18 @@
 import android.util.FloatProperty;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.FloatRange;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimatedFloat;
 import com.android.launcher3.anim.AnimatorListeners;
 import com.android.launcher3.anim.Interpolators;
@@ -66,7 +70,7 @@
         implements StateHandler<LauncherState>, OnDeviceProfileChangeListener {
     // This constant should match the second derivative of the animator interpolator.
     public static final float INTERP_COEFF = 1.7f;
-    private static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
+    public static final float SWIPE_ALL_APPS_TO_HOME_MIN_SCALE = 0.9f;
     private static final int REVERT_SWIPE_ALL_APPS_TO_HOME_ANIMATION_DURATION_MS = 200;
 
     public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
@@ -168,6 +172,8 @@
 
     private boolean mIsTablet;
 
+    private boolean mHasScaleEffect;
+
     public AllAppsTransitionController(Launcher l) {
         mLauncher = l;
         DeviceProfile dp = mLauncher.getDeviceProfile();
@@ -276,8 +282,18 @@
             rv.getScrollbar().setVisibility(scaleProgress < 1f ? View.INVISIBLE : View.VISIBLE);
         }
 
-        // TODO(b/264906511): We need to disable view clipping on all apps' parent views so
-        //  that the extra roll of app icons are displayed.
+        // Disable view clipping from all apps' RecyclerView up to all apps view during scale
+        // animation, and vice versa. The goal is to display extra roll(s) app icons (rendered in
+        // {@link AppsGridLayoutManager#calculateExtraLayoutSpace}) during scale animation.
+        boolean hasScaleEffect = scaleProgress < 1f;
+        if (hasScaleEffect != mHasScaleEffect) {
+            mHasScaleEffect = hasScaleEffect;
+            if (mHasScaleEffect) {
+                setClipChildrenOnViewTree(rv, mLauncher.getAppsView(), false);
+            } else {
+                restoreClipChildrenOnViewTree(rv, mLauncher.getAppsView());
+            }
+        }
     }
 
     private void animateAllAppsToNoScale() {
@@ -381,6 +397,79 @@
     }
 
     /**
+     * Recursively call {@link ViewGroup#setClipChildren(boolean)} from {@link View} to ts parent
+     * (direct or indirect) inclusive. This method will also save the old clipChildren value on each
+     * view with {@link View#setTag(int, Object)}, which can be restored in
+     * {@link #restoreClipChildrenOnViewTree(View, ViewParent)}.
+     *
+     * Note that if parent is null or not a parent of the view, this method will be applied all the
+     * way to root view.
+     *
+     * @param v child view
+     * @param parent direct or indirect parent of child view
+     * @param clipChildren whether we should clip children
+     */
+    private static void setClipChildrenOnViewTree(
+            @Nullable View v,
+            @Nullable ViewParent parent,
+            boolean clipChildren) {
+        if (v == null) {
+            return;
+        }
+
+        if (v instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) v;
+            boolean oldClipChildren = viewGroup.getClipChildren();
+            if (oldClipChildren != clipChildren) {
+                v.setTag(R.id.saved_clip_children_tag_id, oldClipChildren);
+                viewGroup.setClipChildren(clipChildren);
+            }
+        }
+
+        if (v == parent) {
+            return;
+        }
+
+        if (v.getParent() instanceof View) {
+            setClipChildrenOnViewTree((View) v.getParent(), parent, clipChildren);
+        }
+    }
+
+    /**
+     * Recursively call {@link ViewGroup#setClipChildren(boolean)} to restore clip children value
+     * set in {@link #setClipChildrenOnViewTree(View, ViewParent, boolean)} on view to its parent
+     * (direct or indirect) inclusive.
+     *
+     * Note that if parent is null or not a parent of the view, this method will be applied all the
+     * way to root view.
+     *
+     * @param v child view
+     * @param parent direct or indirect parent of child view
+     */
+    private static void restoreClipChildrenOnViewTree(
+            @Nullable View v, @Nullable ViewParent parent) {
+        if (v == null) {
+            return;
+        }
+        if (v instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) v;
+            Object viewTag = viewGroup.getTag(R.id.saved_clip_children_tag_id);
+            if (viewTag instanceof Boolean) {
+                viewGroup.setClipChildren((boolean) viewTag);
+                viewGroup.setTag(R.id.saved_clip_children_tag_id, null);
+            }
+        }
+
+        if (v == parent) {
+            return;
+        }
+
+        if (v.getParent() instanceof View) {
+            restoreClipChildrenOnViewTree((View) v.getParent(), parent);
+        }
+    }
+
+    /**
      * Updates the total scroll range but does not update the UI.
      */
     public void setShiftRange(float shiftRange) {
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 4878077..1c67691 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -57,7 +57,6 @@
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.InsettableFrameLayout;
-import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.search.DefaultSearchAdapterProvider;
@@ -161,10 +160,8 @@
                 R.dimen.dynamic_grid_cell_border_spacing);
         mHeaderProtectionColor = Themes.getAttrColor(context, R.attr.allappsHeaderProtectionColor);
 
-        mWorkManager = new WorkProfileManager(
-                mActivityContext.getSystemService(UserManager.class),
-                this, LauncherPrefs.getPrefs(mActivityContext),
-                mActivityContext.getStatsLogManager());
+        mWorkManager = new WorkProfileManager(mActivityContext.getSystemService(UserManager.class),
+                this, mActivityContext.getStatsLogManager());
         mAH = Arrays.asList(null, null, null);
         mNavBarScrimPaint = new Paint();
         mNavBarScrimPaint.setColor(Themes.getAttrColor(context, R.attr.allAppsNavBarScrimColor));
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index b3245ee..b4cdc96 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
 import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
 
 import android.content.Context;
@@ -85,8 +86,7 @@
     @Override
     public void onClick(View view) {
         startAnimation(mDismissAnim);
-        LauncherPrefs.getPrefs(getContext()).edit().putInt(WorkProfileManager.KEY_WORK_EDU_STEP,
-                1).apply();
+        LauncherPrefs.get(getContext()).put(WORK_EDU_STEP, 1);
     }
 
     @Override
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 11ce738..3f2f21d 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -203,12 +203,10 @@
 
     public void extend() {
         mTextView.setVisibility(VISIBLE);
-        mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_EXTEND);
     }
 
     public void shrink(){
         mTextView.setVisibility(GONE);
-        mStatsLogManager.logger().log(LAUNCHER_WORK_FAB_BUTTON_COLLAPSE);
     }
 
     public int getScrollThreshold() {
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index 279f0d3..fa03905 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.LauncherPrefs.WORK_EDU_STEP;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_DISABLED_CARD;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_WORK_EDU_CARD;
 import static com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder.MAIN;
@@ -26,7 +27,6 @@
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
-import android.content.SharedPreferences;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -40,6 +40,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.recyclerview.widget.RecyclerView;
 
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.BaseAllAppsAdapter.AdapterItem;
@@ -86,14 +87,12 @@
 
     @WorkProfileState
     private int mCurrentState;
-    private SharedPreferences mPreferences;
 
     public WorkProfileManager(
-            UserManager userManager, BaseAllAppsContainerView<?> allApps, SharedPreferences prefs,
+            UserManager userManager, BaseAllAppsContainerView<?> allApps,
             StatsLogManager statsLogManager) {
         mUserManager = userManager;
         mAllApps = allApps;
-        mPreferences = prefs;
         mMatcher = mAllApps.mPersonalMatcher.negate();
         mStatsLogManager = statsLogManager;
     }
@@ -225,7 +224,7 @@
     }
 
     private boolean isEduSeen() {
-        return mPreferences.getInt(KEY_WORK_EDU_STEP, 0) != 0;
+        return LauncherPrefs.get(mAllApps.getContext()).get(WORK_EDU_STEP) != 0;
     }
 
     private void onWorkFabClicked(View view) {
diff --git a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
index abd4682..726ef05 100644
--- a/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
+++ b/src/com/android/launcher3/celllayout/CellLayoutLayoutParams.java
@@ -29,29 +29,17 @@
  */
 public class CellLayoutLayoutParams extends ViewGroup.MarginLayoutParams {
 
-    public int screenId = -1;
+    private int mScreenId = -1;
 
-    /**
-     * Horizontal location of the item in the grid.
-     */
     @ViewDebug.ExportedProperty
-    public int cellX;
+    private int mCellX;
 
-    /**
-     * Vertical location of the item in the grid.
-     */
     @ViewDebug.ExportedProperty
-    public int cellY;
+    private int mCellY;
 
-    /**
-     * Temporary horizontal location of the item in the grid during reorder
-     */
-    public int tmpCellX;
+    private int mTmpCellX;
 
-    /**
-     * Temporary vertical location of the item in the grid during reorder
-     */
-    public int tmpCellY;
+    private int mTmpCellY;
 
     /**
      * Indicates that the temporary coordinates should be used to layout the items
@@ -105,24 +93,24 @@
 
     public CellLayoutLayoutParams(CellLayoutLayoutParams source) {
         super(source);
-        this.cellX = source.cellX;
-        this.cellY = source.cellY;
+        this.mCellX = source.getCellX();
+        this.mCellY = source.getCellY();
         this.cellHSpan = source.cellHSpan;
         this.cellVSpan = source.cellVSpan;
-        this.screenId = source.screenId;
-        this.tmpCellX = source.tmpCellX;
-        this.tmpCellY = source.tmpCellY;
+        this.mScreenId = source.getScreenId();
+        this.mTmpCellX = source.getTmpCellX();
+        this.mTmpCellY = source.getTmpCellY();
         this.useTmpCoords = source.useTmpCoords;
     }
 
     public CellLayoutLayoutParams(int cellX, int cellY, int cellHSpan, int cellVSpan,
             int screenId) {
         super(CellLayoutLayoutParams.MATCH_PARENT, CellLayoutLayoutParams.MATCH_PARENT);
-        this.cellX = cellX;
-        this.cellY = cellY;
+        this.mCellX = cellX;
+        this.mCellY = cellY;
         this.cellHSpan = cellHSpan;
         this.cellVSpan = cellVSpan;
-        this.screenId = screenId;
+        this.mScreenId = screenId;
     }
 
     /**
@@ -148,8 +136,8 @@
         if (isLockedToGrid) {
             final int myCellHSpan = cellHSpan;
             final int myCellVSpan = cellVSpan;
-            int myCellX = useTmpCoords ? tmpCellX : cellX;
-            int myCellY = useTmpCoords ? tmpCellY : cellY;
+            int myCellX = useTmpCoords ? getTmpCellX() : getCellX();
+            int myCellY = useTmpCoords ? getTmpCellY() : getCellY();
 
             if (invertHorizontally) {
                 myCellX = colCount - myCellX - cellHSpan;
@@ -179,14 +167,66 @@
      * Sets the position to the provided point
      */
     public void setCellXY(Point point) {
-        cellX = point.x;
-        cellY = point.y;
+        setCellX(point.x);
+        setCellY(point.y);
     }
 
     /**
      * @return the string representation of the position of the {@link CellLayoutLayoutParams}
      */
     public String toString() {
-        return "(" + this.cellX + ", " + this.cellY + ")";
+        return "(" + this.getCellX() + ", " + this.getCellY() + ")";
+    }
+
+    public int getScreenId() {
+        return mScreenId;
+    }
+
+    public void setScreenId(int screenId) {
+        this.mScreenId = screenId;
+    }
+
+    /**
+     * Horizontal location of the item in the grid.
+     */
+    public int getCellX() {
+        return mCellX;
+    }
+
+    public void setCellX(int cellX) {
+        this.mCellX = cellX;
+    }
+
+    /**
+     * Vertical location of the item in the grid.
+     */
+    public int getCellY() {
+        return mCellY;
+    }
+
+    public void setCellY(int cellY) {
+        this.mCellY = cellY;
+    }
+
+    /**
+     * Temporary horizontal location of the item in the grid during reorder
+     */
+    public int getTmpCellX() {
+        return mTmpCellX;
+    }
+
+    public void setTmpCellX(int tmpCellX) {
+        this.mTmpCellX = tmpCellX;
+    }
+
+    /**
+     * Temporary vertical location of the item in the grid during reorder
+     */
+    public int getTmpCellY() {
+        return mTmpCellY;
+    }
+
+    public void setTmpCellY(int tmpCellY) {
+        this.mTmpCellY = tmpCellY;
     }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 824ac4d..daf83d4 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -273,6 +273,10 @@
             "ENABLE_SEARCH_RESULT_BACKGROUND_DRAWABLES", false,
             "Enable option to replace decorator-based search result backgrounds with drawables");
 
+    public static final BooleanFlag ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION = new DeviceFlag(
+            "ENABLE_SEARCH_RESULT_LAUNCH_TRANSITION", false,
+            "Enable option to launch search results using the new standardized transitions");
+
     public static final BooleanFlag TWO_PREDICTED_ROWS_ALL_APPS_SEARCH = new DeviceFlag(
             "TWO_PREDICTED_ROWS_ALL_APPS_SEARCH", false,
             "Use 2 rows of app predictions in All Apps search zero-state");
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index dd00f07..60442f4 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -281,7 +281,7 @@
         CellLayoutLayoutParams lp = (CellLayoutLayoutParams) getLayoutParams();
         CellLayout cl = (CellLayout) getParent().getParent();
 
-        mBackground.animateToAccept(cl, lp.cellX, lp.cellY);
+        mBackground.animateToAccept(cl, lp.getCellX(), lp.getCellY());
         mOpenAlarm.setOnAlarmListener(mOnOpenListener);
         if (SPRING_LOADING_ENABLED &&
                 ((dragInfo instanceof WorkspaceItemFactory)
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 141388f..1ada95c 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -225,8 +225,8 @@
             textView.setLayoutParams(new CellLayoutLayoutParams(
                     item.cellX, item.cellY, item.spanX, item.spanY, item.screenId));
         } else {
-            lp.cellX = item.cellX;
-            lp.cellY = item.cellY;
+            lp.setCellX(item.cellX);
+            lp.setCellY(item.cellY);
             lp.cellHSpan = lp.cellVSpan = 1;
         }
         return textView;
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index feadafa..9426c22 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -1,8 +1,7 @@
 package com.android.launcher3.graphics;
 
-import static com.android.launcher3.LauncherPrefs.getPrefs;
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import static com.android.launcher3.util.Themes.KEY_THEMED_ICONS;
 import static com.android.launcher3.util.Themes.isThemedIconEnabled;
 
 import android.annotation.TargetApi;
@@ -25,6 +24,7 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile.GridOption;
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.Executors;
 
@@ -142,9 +142,8 @@
             }
             case ICON_THEMED:
             case SET_ICON_THEMED: {
-                getPrefs(getContext()).edit()
-                        .putBoolean(KEY_THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE))
-                        .apply();
+                LauncherPrefs.get(getContext())
+                        .put(THEMED_ICONS, values.getAsBoolean(BOOLEAN_VALUE));
                 return 1;
             }
             default:
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2159c6b..5f6df27 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -558,14 +558,11 @@
                 + "result page etc.")
         LAUNCHER_ALLAPPS_SCROLLED(985),
 
-        @UiEvent(doc = "User scrolled up on one of the all apps surfaces such as A-Z list, search "
-                + "result page etc.")
-        LAUNCHER_ALLAPPS_SCROLLED_UP(1229),
+        @UiEvent(doc = "User scrolled up on the all apps personal A-Z list.")
+        LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_UP(1287),
 
-        @UiEvent(doc =
-                "User scrolled down on one of the all apps surfaces such as A-Z list, search "
-                        + "result page etc.")
-        LAUNCHER_ALLAPPS_SCROLLED_DOWN(1230),
+        @UiEvent(doc = "User scrolled down on the all apps personal A-Z list.")
+        LAUNCHER_ALLAPPS_PERSONAL_SCROLLED_DOWN(1288),
 
         @UiEvent(doc = "User scrolled on one of the all apps surfaces such as A-Z list, search "
                 + "result page etc and we don't know the direction since user came back to "
@@ -626,11 +623,19 @@
         @UiEvent(doc = "User has invoked split to left half with a keyboard shortcut.")
         LAUNCHER_KEYBOARD_SHORTCUT_SPLIT_LEFT_TOP(1233),
 
-        @UiEvent(doc = "User has collapsed the work FAB button by swiping down")
+        @UiEvent(doc = "User has collapsed the work FAB button by scrolling down in the all apps"
+                + " work A-Z list.")
         LAUNCHER_WORK_FAB_BUTTON_COLLAPSE(1276),
 
-        @UiEvent(doc = "User has collapsed the work FAB button by swiping up")
+        @UiEvent(doc = "User has collapsed the work FAB button by scrolling up in the all apps"
+                + " work A-Z list.")
         LAUNCHER_WORK_FAB_BUTTON_EXTEND(1277),
+
+        @UiEvent(doc = "User scrolled down on the search result page.")
+        LAUNCHER_ALLAPPS_SEARCH_SCROLLED_DOWN(1285),
+
+        @UiEvent(doc = "User scrolled up on the search result page.")
+        LAUNCHER_ALLAPPS_SEARCH_SCROLLED_UP(1286),
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index 85d54c0..edc8c1b 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -17,7 +17,10 @@
 package com.android.launcher3.model;
 
 import static com.android.launcher3.InvariantDeviceProfile.DeviceType;
-import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+import static com.android.launcher3.LauncherPrefs.DB_FILE;
+import static com.android.launcher3.LauncherPrefs.DEVICE_TYPE;
+import static com.android.launcher3.LauncherPrefs.HOTSEAT_COUNT;
+import static com.android.launcher3.LauncherPrefs.WORKSPACE_SIZE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_2;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
@@ -25,7 +28,6 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.text.TextUtils;
 
 import com.android.launcher3.InvariantDeviceProfile;
@@ -58,11 +60,11 @@
     }
 
     public DeviceGridState(Context context) {
-        SharedPreferences prefs = LauncherPrefs.getPrefs(context);
-        mGridSizeString = prefs.getString(KEY_WORKSPACE_SIZE, "");
-        mNumHotseat = prefs.getInt(KEY_HOTSEAT_COUNT, -1);
-        mDeviceType = prefs.getInt(KEY_DEVICE_TYPE, TYPE_PHONE);
-        mDbFile = prefs.getString(KEY_DB_FILE, "");
+        LauncherPrefs lp = LauncherPrefs.get(context);
+        mGridSizeString = lp.get(WORKSPACE_SIZE);
+        mNumHotseat = lp.get(HOTSEAT_COUNT);
+        mDeviceType = lp.get(DEVICE_TYPE);
+        mDbFile = lp.get(DB_FILE);
     }
 
     /**
@@ -90,12 +92,11 @@
      * Stores the device state to shared preferences
      */
     public void writeToPrefs(Context context) {
-        LauncherPrefs.getPrefs(context).edit()
-                .putString(KEY_WORKSPACE_SIZE, mGridSizeString)
-                .putInt(KEY_HOTSEAT_COUNT, mNumHotseat)
-                .putInt(KEY_DEVICE_TYPE, mDeviceType)
-                .putString(KEY_DB_FILE, mDbFile)
-                .apply();
+        LauncherPrefs.get(context).put(
+                WORKSPACE_SIZE.to(mGridSizeString),
+                HOTSEAT_COUNT.to(mNumHotseat),
+                DEVICE_TYPE.to(mDeviceType),
+                DB_FILE.to(mDbFile));
     }
 
     /**
diff --git a/src/com/android/launcher3/pm/InstallSessionHelper.java b/src/com/android/launcher3/pm/InstallSessionHelper.java
index 150bca4..db23566 100644
--- a/src/com/android/launcher3/pm/InstallSessionHelper.java
+++ b/src/com/android/launcher3/pm/InstallSessionHelper.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.pm;
 
-import static com.android.launcher3.LauncherPrefs.getPrefs;
-
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
@@ -34,6 +32,7 @@
 import androidx.annotation.RequiresApi;
 import androidx.annotation.WorkerThread;
 
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.SessionCommitReceiver;
 import com.android.launcher3.Utilities;
@@ -65,7 +64,7 @@
     // Set<String> of session ids of promise icons that have been added to the home screen
     // as FLAG_PROMISE_NEW_INSTALLS.
     @NonNull
-    protected static final String PROMISE_ICON_IDS = "promise_icon_ids";
+    public static final String PROMISE_ICON_IDS = "promise_icon_ids";
 
     private static final boolean DEBUG = false;
 
@@ -102,7 +101,7 @@
             return mPromiseIconIds;
         }
         mPromiseIconIds = IntSet.wrap(IntArray.fromConcatString(
-                getPrefs(mAppContext).getString(PROMISE_ICON_IDS, "")));
+                LauncherPrefs.get(mAppContext).get(LauncherPrefs.PROMISE_ICON_IDS)));
 
         IntArray existingIds = new IntArray();
         for (SessionInfo info : getActiveSessions().values()) {
@@ -146,9 +145,8 @@
     }
 
     private void updatePromiseIconPrefs() {
-        getPrefs(mAppContext).edit()
-                .putString(PROMISE_ICON_IDS, getPromiseIconIds().getArray().toConcatString())
-                .apply();
+        LauncherPrefs.get(mAppContext).put(LauncherPrefs.PROMISE_ICON_IDS,
+                getPromiseIconIds().getArray().toConcatString());
     }
 
     @Nullable
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 5e97b2d..2a452be 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -17,13 +17,14 @@
 package com.android.launcher3.provider;
 
 import static com.android.launcher3.InvariantDeviceProfile.TYPE_MULTI_DISPLAY;
-import static com.android.launcher3.InvariantDeviceProfile.TYPE_PHONE;
+import static com.android.launcher3.LauncherPrefs.APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.OLD_APP_WIDGET_IDS;
+import static com.android.launcher3.LauncherPrefs.RESTORE_DEVICE;
 import static com.android.launcher3.provider.LauncherDbUtils.dropTable;
 
 import android.app.backup.BackupManager;
 import android.content.ContentValues;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.os.UserHandle;
@@ -62,13 +63,13 @@
 public class RestoreDbTask {
 
     private static final String TAG = "RestoreDbTask";
-    private static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
+    public static final String RESTORED_DEVICE_TYPE = "restored_task_pending";
 
     private static final String INFO_COLUMN_NAME = "name";
     private static final String INFO_COLUMN_DEFAULT_VALUE = "dflt_value";
 
-    private static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
-    private static final String APPWIDGET_IDS = "appwidget_ids";
+    public static final String APPWIDGET_OLD_IDS = "appwidget_old_ids";
+    public static final String APPWIDGET_IDS = "appwidget_ids";
 
     /**
      * Tries to restore the backup DB if needed
@@ -87,7 +88,7 @@
 
         // Set is pending to false irrespective of the result, so that it doesn't get
         // executed again.
-        LauncherPrefs.getPrefs(context).edit().remove(RESTORED_DEVICE_TYPE).commit();
+        LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
 
         idp.reinitializeAfterRestore(context);
     }
@@ -240,8 +241,7 @@
         }
 
         // If restored from a single display backup, remove gaps between screenIds
-        if (LauncherPrefs.getPrefs(context).getInt(RESTORED_DEVICE_TYPE, TYPE_PHONE)
-                != TYPE_MULTI_DISPLAY) {
+        if (LauncherPrefs.get(context).get(RESTORE_DEVICE) != TYPE_MULTI_DISPLAY) {
             removeScreenIdGaps(db);
         }
 
@@ -339,7 +339,7 @@
     }
 
     public static boolean isPending(Context context) {
-        return LauncherPrefs.getPrefs(context).contains(RESTORED_DEVICE_TYPE);
+        return LauncherPrefs.get(context).has(RESTORE_DEVICE);
     }
 
     /**
@@ -347,34 +347,31 @@
      */
     public static void setPending(Context context) {
         FileLog.d(TAG, "Restore data received through full backup ");
-        LauncherPrefs.getPrefs(context).edit()
-                .putInt(RESTORED_DEVICE_TYPE, new DeviceGridState(context).getDeviceType())
-                .commit();
+        LauncherPrefs.get(context)
+                .putSync(RESTORE_DEVICE.to(new DeviceGridState(context).getDeviceType()));
     }
 
     private void restoreAppWidgetIdsIfExists(Context context) {
-        SharedPreferences prefs = LauncherPrefs.getPrefs(context);
-        if (prefs.contains(APPWIDGET_OLD_IDS) && prefs.contains(APPWIDGET_IDS)) {
+        LauncherPrefs lp = LauncherPrefs.get(context);
+        if (lp.has(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS)) {
             LauncherWidgetHolder holder = LauncherWidgetHolder.newInstance(context);
             AppWidgetsRestoredReceiver.restoreAppWidgetIds(context,
-                    IntArray.fromConcatString(prefs.getString(APPWIDGET_OLD_IDS, "")).toArray(),
-                    IntArray.fromConcatString(prefs.getString(APPWIDGET_IDS, "")).toArray(),
+                    IntArray.fromConcatString(lp.get(OLD_APP_WIDGET_IDS)).toArray(),
+                    IntArray.fromConcatString(lp.get(APP_WIDGET_IDS)).toArray(),
                     holder);
             holder.destroy();
         } else {
             FileLog.d(TAG, "No app widget ids to restore.");
         }
 
-        prefs.edit().remove(APPWIDGET_OLD_IDS)
-                .remove(APPWIDGET_IDS).apply();
+        lp.remove(APP_WIDGET_IDS, OLD_APP_WIDGET_IDS);
     }
 
     public static void setRestoredAppWidgetIds(Context context, @NonNull int[] oldIds,
             @NonNull int[] newIds) {
-        LauncherPrefs.getPrefs(context).edit()
-                .putString(APPWIDGET_OLD_IDS, IntArray.wrap(oldIds).toConcatString())
-                .putString(APPWIDGET_IDS, IntArray.wrap(newIds).toConcatString())
-                .commit();
+        LauncherPrefs.get(context).putSync(
+                OLD_APP_WIDGET_IDS.to(IntArray.wrap(oldIds).toConcatString()),
+                APP_WIDGET_IDS.to(IntArray.wrap(newIds).toConcatString()));
     }
 
 }
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 7ab3013..d2f82c2 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -93,7 +93,8 @@
         WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
 
         Intent intent = getIntent();
-        if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)) {
+        if (intent.hasExtra(EXTRA_FRAGMENT) || intent.hasExtra(EXTRA_FRAGMENT_ARGS)
+                || intent.hasExtra(EXTRA_FRAGMENT_ARG_KEY)) {
             getActionBar().setDisplayHomeAsUpEnabled(true);
         }
 
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 585bea9..5526839 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -19,6 +19,8 @@
 import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
 import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
 
+import static com.android.launcher3.LauncherPrefs.THEMED_ICONS;
+
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -74,7 +76,7 @@
      * Returns true if workspace icon theming is enabled
      */
     public static boolean isThemedIconEnabled(Context context) {
-        return LauncherPrefs.getPrefs(context).getBoolean(KEY_THEMED_ICONS, false);
+        return LauncherPrefs.get(context).get(THEMED_ICONS);
     }
 
     public static String getDefaultBodyFont(Context context) {
diff --git a/tests/src/com/android/launcher3/LauncherPrefsTest.kt b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
new file mode 100644
index 0000000..151abf1
--- /dev/null
+++ b/tests/src/com/android/launcher3/LauncherPrefsTest.kt
@@ -0,0 +1,148 @@
+package com.android.launcher3
+
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private val TEST_BOOLEAN_ITEM = LauncherPrefs.nonRestorableItem("1", false)
+private val TEST_STRING_ITEM = LauncherPrefs.nonRestorableItem("2", "( ͡❛ ͜ʖ ͡❛)")
+private val TEST_INT_ITEM = LauncherPrefs.nonRestorableItem("3", -1)
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class LauncherPrefsTest {
+
+    private val launcherPrefs by lazy {
+        LauncherPrefs.get(InstrumentationRegistry.getInstrumentation().targetContext).apply {
+            remove(TEST_BOOLEAN_ITEM, TEST_STRING_ITEM, TEST_INT_ITEM)
+        }
+    }
+
+    @Test
+    fun has_keyMissingFromLauncherPrefs_returnsFalse() {
+        assertThat(launcherPrefs.has(TEST_BOOLEAN_ITEM)).isFalse()
+    }
+
+    @Test
+    fun has_keyPresentInLauncherPrefs_returnsTrue() {
+        with(launcherPrefs) {
+            putSync(TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue))
+            assertThat(has(TEST_BOOLEAN_ITEM)).isTrue()
+            remove(TEST_BOOLEAN_ITEM)
+        }
+    }
+
+    @Test
+    fun addListener_listeningForStringItemUpdates_isCorrectlyNotifiedOfUpdates() {
+        val latch = CountDownLatch(1)
+        val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+        with(launcherPrefs) {
+            putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue))
+            addListener(listener, TEST_STRING_ITEM)
+            putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"))
+
+            assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+            remove(TEST_STRING_ITEM)
+        }
+    }
+
+    @Test
+    fun removeListener_previouslyListeningForStringItemUpdates_isNoLongerNotifiedOfUpdates() {
+        val latch = CountDownLatch(1)
+        val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+        with(launcherPrefs) {
+            addListener(listener, TEST_STRING_ITEM)
+            removeListener(listener, TEST_STRING_ITEM)
+            putSync(TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "hello."))
+
+            // latch will be still be 1 (and await will return false) if the listener was not called
+            assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+            remove(TEST_STRING_ITEM)
+        }
+    }
+
+    @Test
+    fun addListenerAndRemoveListener_forMultipleItems_bothWorkProperly() {
+        var latch = CountDownLatch(3)
+        val listener = OnSharedPreferenceChangeListener { _, _ -> latch.countDown() }
+
+        with(launcherPrefs) {
+            addListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+            putSync(
+                TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue + 123),
+                TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue + "abc"),
+                TEST_BOOLEAN_ITEM.to(!TEST_BOOLEAN_ITEM.defaultValue)
+            )
+            assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue()
+
+            removeListener(listener, TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+            latch = CountDownLatch(1)
+            putSync(
+                TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
+                TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
+                TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
+            )
+            remove(TEST_INT_ITEM, TEST_STRING_ITEM, TEST_BOOLEAN_ITEM)
+
+            assertThat(latch.await(2, TimeUnit.SECONDS)).isFalse()
+        }
+    }
+
+    @Test
+    fun get_booleanItemNotInLauncherprefs_returnsDefaultValue() {
+        assertThat(launcherPrefs.get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
+    }
+
+    @Test
+    fun get_stringItemNotInLauncherPrefs_returnsDefaultValue() {
+        assertThat(launcherPrefs.get(TEST_STRING_ITEM)).isEqualTo(TEST_STRING_ITEM.defaultValue)
+    }
+
+    @Test
+    fun get_intItemNotInLauncherprefs_returnsDefaultValue() {
+        assertThat(launcherPrefs.get(TEST_INT_ITEM)).isEqualTo(TEST_INT_ITEM.defaultValue)
+    }
+
+    @Test
+    fun put_storesItemInLauncherPrefs_successfully() {
+        val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
+
+        with(launcherPrefs) {
+            putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
+            assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(notDefaultValue)
+            remove(TEST_BOOLEAN_ITEM)
+        }
+    }
+
+    @Test
+    fun put_storesListOfItemsInLauncherPrefs_successfully() {
+        with(launcherPrefs) {
+            putSync(
+                TEST_STRING_ITEM.to(TEST_STRING_ITEM.defaultValue),
+                TEST_INT_ITEM.to(TEST_INT_ITEM.defaultValue),
+                TEST_BOOLEAN_ITEM.to(TEST_BOOLEAN_ITEM.defaultValue)
+            )
+            assertThat(has(TEST_BOOLEAN_ITEM, TEST_INT_ITEM, TEST_STRING_ITEM)).isTrue()
+            remove(TEST_STRING_ITEM, TEST_INT_ITEM, TEST_BOOLEAN_ITEM)
+        }
+    }
+
+    @Test
+    fun remove_deletesItemFromLauncherPrefs_successfully() {
+        val notDefaultValue = !TEST_BOOLEAN_ITEM.defaultValue
+
+        with(launcherPrefs) {
+            putSync(TEST_BOOLEAN_ITEM.to(notDefaultValue))
+            remove(TEST_BOOLEAN_ITEM)
+            assertThat(get(TEST_BOOLEAN_ITEM)).isEqualTo(TEST_BOOLEAN_ITEM.defaultValue)
+        }
+    }
+}
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 843f011..7b38ed6 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -132,10 +132,10 @@
                             (CellLayoutLayoutParams) callView.getLayoutParams();
                     // is icon
                     if (callView instanceof DoubleShadowBubbleTextView) {
-                        board.addIcon(params.cellX, params.cellY);
+                        board.addIcon(params.getCellX(), params.getCellY());
                     } else {
                         // is widget
-                        board.addWidget(params.cellX, params.cellY, params.cellHSpan,
+                        board.addWidget(params.getCellX(), params.getCellY(), params.cellHSpan,
                                 params.cellVSpan, (char) ('A' + widgetCount));
                         widgetCount++;
                     }
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
index dcc8ec7..a85fa3a 100644
--- a/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt
@@ -24,7 +24,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherFiles
+import com.android.launcher3.LauncherPrefs
+import com.android.launcher3.LauncherPrefs.Companion.WORKSPACE_SIZE
 import com.android.launcher3.LauncherSettings.Favorites.*
 import com.android.launcher3.config.FeatureFlags
 import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
@@ -754,11 +755,7 @@
             .edit()
             .putBoolean(FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC.key, true)
             .commit()
-        context
-            .getSharedPreferences(LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
-            .edit()
-            .putString(DeviceGridState.KEY_WORKSPACE_SIZE, srcGridSize)
-            .commit()
+        LauncherPrefs.get(context).putSync(WORKSPACE_SIZE.to(srcGridSize))
         FeatureFlags.initialize(context)
     }
 
diff --git a/tests/src/com/android/launcher3/util/LauncherModelHelper.java b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
index 93bf312..caec301 100644
--- a/tests/src/com/android/launcher3/util/LauncherModelHelper.java
+++ b/tests/src/com/android/launcher3/util/LauncherModelHelper.java
@@ -55,6 +55,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherModel.ModelUpdateTask;
+import com.android.launcher3.LauncherPrefs;
 import com.android.launcher3.LauncherProvider;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.model.AllAppsList;
@@ -506,7 +507,7 @@
 
         SanboxModelContext() {
             super(ApplicationProvider.getApplicationContext(),
-                    UserCache.INSTANCE, InstallSessionHelper.INSTANCE,
+                    UserCache.INSTANCE, InstallSessionHelper.INSTANCE, LauncherPrefs.INSTANCE,
                     LauncherAppState.INSTANCE, InvariantDeviceProfile.INSTANCE,
                     DisplayController.INSTANCE, CustomWidgetManager.INSTANCE,
                     SettingsCache.INSTANCE, PluginManagerWrapper.INSTANCE,