Merge "Add scroll logging to RecentsView behind DEBUG flag" into sc-v2-dev
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 68dfac7..6bc49ed 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -16,8 +16,6 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_APP;
 import static com.android.launcher3.taskbar.TaskbarStashController.FLAG_IN_STASHED_LAUNCHER_STATE;
 import static com.android.launcher3.taskbar.TaskbarViewController.ALPHA_INDEX_HOME;
@@ -52,7 +50,6 @@
 import com.android.quickstep.RecentsAnimationCallbacks;
 import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
 import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 
@@ -187,8 +184,6 @@
         TaskbarStashController stashController = mControllers.taskbarStashController;
         stashController.updateStateForFlag(FLAG_IN_APP, !isResumed);
         stashController.applyState(duration);
-        SystemUiProxy.INSTANCE.get(mContext).notifyTaskbarStatus(!isResumed,
-                mControllers.taskbarStashController.isStashedInApp());
     }
 
     /**
@@ -286,11 +281,6 @@
     @Override
     protected void onStashedInAppChanged() {
         onStashedInAppChanged(mLauncher.getDeviceProfile());
-        if (mControllers.taskbarStashController.isStashedInApp()) {
-            mContext.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE);
-        } else {
-            mContext.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
-        }
     }
 
     private void onStashedInAppChanged(DeviceProfile deviceProfile) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 0316333..ad0243c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -321,6 +321,7 @@
         mControllers.taskbarViewController.setRecentsButtonDisabled(
                 mControllers.navbarButtonsViewController.isRecentsDisabled());
         mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
+        mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags);
     }
 
     public void onRotationProposal(int rotation, boolean isValid) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
index fc5abd0..5e76b96 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarModelCallbacks.java
@@ -45,6 +45,9 @@
     private final TaskbarActivityContext mContext;
     private final TaskbarView mContainer;
 
+    // Initialized in init.
+    private TaskbarControllers mControllers;
+
     private boolean mBindInProgress = false;
 
     public TaskbarModelCallbacks(
@@ -53,6 +56,10 @@
         mContainer = container;
     }
 
+    public void init(TaskbarControllers controllers) {
+        mControllers = controllers;
+    }
+
     @Override
     public void startBinding() {
         mBindInProgress = true;
@@ -161,6 +168,7 @@
         int predictionSize = mPredictedItems.size();
         int predictionNextIndex = 0;
 
+        boolean isHotseatEmpty = true;
         for (int i = 0; i < hotseatItemInfos.length; i++) {
             hotseatItemInfos[i] = mHotseatItems.get(i);
             if (hotseatItemInfos[i] == null && predictionNextIndex < predictionSize) {
@@ -168,7 +176,14 @@
                 hotseatItemInfos[i].screenId = i;
                 predictionNextIndex++;
             }
+            if (hotseatItemInfos[i] != null) {
+                isHotseatEmpty = false;
+            }
         }
         mContainer.updateHotseatItems(hotseatItemInfos);
+
+        mControllers.taskbarStashController.updateStateForFlag(
+                TaskbarStashController.FLAG_STASHED_IN_APP_EMPTY, isHotseatEmpty);
+        mControllers.taskbarStashController.applyState();
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 02170ab..6d6f0f2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,6 +17,10 @@
 
 import static android.view.HapticFeedbackConstants.LONG_PRESS;
 
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
@@ -40,8 +44,14 @@
 public class TaskbarStashController {
 
     public static final int FLAG_IN_APP = 1 << 0;
-    public static final int FLAG_STASHED_IN_APP = 1 << 1;
-    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 2;
+    public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
+    public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
+    public static final int FLAG_STASHED_IN_APP_EMPTY = 1 << 3; // no hotseat icons
+    public static final int FLAG_IN_STASHED_LAUNCHER_STATE = 1 << 4;
+
+    // If we're in an app and any of these flags are enabled, taskbar should be stashed.
+    public static final int FLAGS_STASHED_IN_APP = FLAG_STASHED_IN_APP_MANUAL
+            | FLAG_STASHED_IN_APP_PINNED | FLAG_STASHED_IN_APP_EMPTY;
 
     /**
      * How long to stash/unstash when manually invoked via long press.
@@ -96,8 +106,6 @@
     private AnimatedFloat mTaskbarStashedHandleAlpha;
     private AnimatedFloat mTaskbarStashedHandleHintScale;
 
-    /** Whether the user has manually invoked taskbar stashing, which we persist. */
-    private boolean mIsStashedInApp;
     /** Whether we are currently visually stashed (might change based on launcher state). */
     private boolean mIsStashed = false;
     private int mState;
@@ -107,12 +115,9 @@
     // Evaluate whether the handle should be stashed
     private final StatePropertyHolder mStatePropertyHolder = new StatePropertyHolder(
             flags -> {
-                if (!supportsVisualStashing()) {
-                    return false;
-                }
-                boolean inApp = (flags & FLAG_IN_APP) != 0;
-                boolean stashedInApp = (flags & FLAG_STASHED_IN_APP) != 0;
-                boolean stashedLauncherState = (flags & FLAG_IN_STASHED_LAUNCHER_STATE) != 0;
+                boolean inApp = hasAnyFlag(flags, FLAG_IN_APP);
+                boolean stashedInApp = hasAnyFlag(flags, FLAGS_STASHED_IN_APP);
+                boolean stashedLauncherState = hasAnyFlag(flags, FLAG_IN_STASHED_LAUNCHER_STATE);
                 return (inApp && stashedInApp) || (!inApp && stashedLauncherState);
             });
 
@@ -141,12 +146,13 @@
         mTaskbarStashedHandleAlpha = stashedHandleController.getStashedHandleAlpha();
         mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
 
-        mIsStashedInApp = supportsManualStashing()
+        boolean isManuallyStashedInApp = supportsManualStashing()
                 && mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
-        updateStateForFlag(FLAG_STASHED_IN_APP, mIsStashedInApp);
+        updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
+        applyState();
 
         SystemUiProxy.INSTANCE.get(mActivity)
-                .notifyTaskbarStatus(/* visible */ true, /* stashed */ mIsStashedInApp);
+                .notifyTaskbarStatus(/* visible */ false, /* stashed */ isStashedInApp());
     }
 
     /**
@@ -178,10 +184,18 @@
     }
 
     /**
-     * Returns whether the user has manually stashed the taskbar in apps.
+     * Returns whether the taskbar should be stashed in apps (e.g. user long pressed to stash).
      */
     public boolean isStashedInApp() {
-        return mIsStashedInApp;
+        return hasAnyFlag(FLAGS_STASHED_IN_APP);
+    }
+
+    private boolean hasAnyFlag(int flagMask) {
+        return hasAnyFlag(mState, flagMask);
+    }
+
+    private boolean hasAnyFlag(int flags, int flagMask) {
+        return (flags & flagMask) != 0;
     }
 
     public int getContentHeight() {
@@ -202,7 +216,7 @@
             // taskbar, we use an OnLongClickListener on TaskbarView instead.
             return false;
         }
-        if (updateAndAnimateIsStashedInApp(false)) {
+        if (updateAndAnimateIsManuallyStashedInApp(false)) {
             mControllers.taskbarActivityContext.getDragLayer().performHapticFeedback(LONG_PRESS);
             return true;
         }
@@ -213,25 +227,32 @@
      * Updates whether we should stash the taskbar when in apps, and animates to the changed state.
      * @return Whether we started an animation to either be newly stashed or unstashed.
      */
-    public boolean updateAndAnimateIsStashedInApp(boolean isStashedInApp) {
+    public boolean updateAndAnimateIsManuallyStashedInApp(boolean isManuallyStashedInApp) {
         if (!supportsManualStashing()) {
             return false;
         }
-        if (mIsStashedInApp != isStashedInApp) {
-            mIsStashedInApp = isStashedInApp;
-            mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, mIsStashedInApp).apply();
-            updateStateForFlag(FLAG_STASHED_IN_APP, mIsStashedInApp);
+        if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL) != isManuallyStashedInApp) {
+            mPrefs.edit().putBoolean(SHARED_PREFS_STASHED_KEY, isManuallyStashedInApp).apply();
+            updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
             applyState();
-
-            SystemUiProxy.INSTANCE.get(mActivity)
-                    .notifyTaskbarStatus(/* visible */ true, /* stashed */ mIsStashedInApp);
-            mControllers.uiController.onStashedInAppChanged();
             return true;
         }
         return false;
     }
 
     private Animator createAnimToIsStashed(boolean isStashed, long duration) {
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        mAnimator = new AnimatorSet();
+
+        if (!supportsVisualStashing()) {
+            // Just hide/show the icons instead of stashing into a handle.
+            mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
+                    .setDuration(duration));
+            return mAnimator;
+        }
+
         AnimatorSet fullLengthAnimatorSet = new AnimatorSet();
         // Not exactly half and may overlap. See [first|second]HalfDurationScale below.
         AnimatorSet firstHalfAnimatorSet = new AnimatorSet();
@@ -287,10 +308,6 @@
         secondHalfAnimatorSet.setDuration((long) (duration * secondHalfDurationScale));
         secondHalfAnimatorSet.setStartDelay((long) (duration * (1 - secondHalfDurationScale)));
 
-        if (mAnimator != null) {
-            mAnimator.cancel();
-        }
-        mAnimator = new AnimatorSet();
         mAnimator.playTogether(fullLengthAnimatorSet, firstHalfAnimatorSet,
                 secondHalfAnimatorSet);
         mAnimator.addListener(new AnimatorListenerAdapter() {
@@ -360,6 +377,13 @@
         return mStatePropertyHolder.setState(mState, duration, false);
     }
 
+    /** Called when some system ui state has changed. (See SYSUI_STATE_... in QuickstepContract) */
+    public void updateStateForSysuiFlags(int systemUiStateFlags) {
+        updateStateForFlag(FLAG_STASHED_IN_APP_PINNED,
+                hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING));
+        applyState();
+    }
+
     /**
      * Updates the proper flag to indicate whether the task bar should be stashed.
      *
@@ -377,16 +401,44 @@
         }
     }
 
+    /**
+     * Called after updateStateForFlag() and applyState() have been called.
+     * @param changedFlags The flags that have changed.
+     */
+    private void onStateChangeApplied(int changedFlags) {
+        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP)) {
+            mControllers.uiController.onStashedInAppChanged();
+        }
+        if (hasAnyFlag(changedFlags, FLAGS_STASHED_IN_APP | FLAG_IN_APP)) {
+            SystemUiProxy.INSTANCE.get(mActivity)
+                    .notifyTaskbarStatus(/* visible */ hasAnyFlag(FLAG_IN_APP),
+                            /* stashed */ isStashedInApp());
+        }
+        if (hasAnyFlag(changedFlags, FLAG_STASHED_IN_APP_MANUAL)) {
+            if (hasAnyFlag(FLAG_STASHED_IN_APP_MANUAL)) {
+                mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_HIDE);
+            } else {
+                mActivity.getStatsLogManager().logger().log(LAUNCHER_TASKBAR_LONGPRESS_SHOW);
+            }
+        }
+    }
+
     private class StatePropertyHolder {
         private final IntPredicate mStashCondition;
 
         private boolean mIsStashed;
+        private int mPrevFlags;
 
         StatePropertyHolder(IntPredicate stashCondition) {
             mStashCondition = stashCondition;
         }
 
         public Animator setState(int flags, long duration, boolean start) {
+            if (mPrevFlags != flags) {
+                int changedFlags = mPrevFlags ^ flags;
+                onStateChangeApplied(changedFlags);
+                mPrevFlags = flags;
+            }
             boolean isStashed = mStashCondition.test(flags);
             if (mIsStashed != isStashed) {
                 mIsStashed = isStashed;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 4cd6814..ed09fa5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -85,6 +85,8 @@
         mTaskbarView.getLayoutParams().height = mActivity.getDeviceProfile().taskbarSize;
 
         mTaskbarIconScaleForStash.updateValue(1f);
+
+        mModelCallbacks.init(controllers);
         LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
     }
 
@@ -254,7 +256,8 @@
         }
 
         public View.OnLongClickListener getBackgroundOnLongClickListener() {
-            return view -> mControllers.taskbarStashController.updateAndAnimateIsStashedInApp(true);
+            return view -> mControllers.taskbarStashController
+                    .updateAndAnimateIsManuallyStashedInApp(true);
         }
 
         /**
diff --git a/res/color-night-v31/folder_background_dark.xml b/res/color-night-v31/folder_background_dark.xml
index a5bd636..d607395 100644
--- a/res/color-night-v31/folder_background_dark.xml
+++ b/res/color-night-v31/folder_background_dark.xml
@@ -16,5 +16,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android" >
     <item
         android:color="@android:color/system_neutral2_50"
-        android:lStar="30" />
+        android:lStar="35" />
 </selector>
diff --git a/res/color-night-v31/folder_preview_dark.xml b/res/color-night-v31/folder_preview_dark.xml
new file mode 100644
index 0000000..a5bd636
--- /dev/null
+++ b/res/color-night-v31/folder_preview_dark.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_neutral2_50"
+        android:lStar="30" />
+</selector>
diff --git a/res/color-v31/folder_preview_light.xml b/res/color-v31/folder_preview_light.xml
new file mode 100644
index 0000000..fe30c87
--- /dev/null
+++ b/res/color-v31/folder_preview_light.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:color="@android:color/system_accent2_50"
+        android:lStar="80" />
+</selector>
diff --git a/res/drawable-v28/round_rect_folder.xml b/res/drawable-v28/round_rect_folder.xml
index 0403be0..77a4aa4 100644
--- a/res/drawable-v28/round_rect_folder.xml
+++ b/res/drawable-v28/round_rect_folder.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/folderFillColor" />
+    <solid android:color="?attr/folderBackgroundColor" />
     <corners android:radius="?android:attr/dialogCornerRadius" />
 </shape>
diff --git a/res/drawable/round_rect_folder.xml b/res/drawable/round_rect_folder.xml
index 8b3d06c..6c5864e 100644
--- a/res/drawable/round_rect_folder.xml
+++ b/res/drawable/round_rect_folder.xml
@@ -16,6 +16,6 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <solid android:color="?attr/folderFillColor" />
+    <solid android:color="?attr/folderBackgroundColor" />
     <corners android:radius="@dimen/bg_round_rect_radius" />
 </shape>
diff --git a/res/layout/keyboard_drag_and_drop.xml b/res/layout/keyboard_drag_and_drop.xml
index e9463c4..bc3a9c1 100644
--- a/res/layout/keyboard_drag_and_drop.xml
+++ b/res/layout/keyboard_drag_and_drop.xml
@@ -26,7 +26,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
-        android:background="?attr/folderFillColor"
+        android:background="?attr/folderBackgroundColor"
         android:padding="8dp"
         android:textColor="?attr/folderTextColor"
         />
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 319c87d..92aa678 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -43,7 +43,8 @@
 
     <attr name="folderDotColor" format="color" />
     <attr name="folderPaginationColor" format="color" />
-    <attr name="folderFillColor" format="color" />
+    <attr name="folderPreviewColor" format="color" />
+    <attr name="folderBackgroundColor" format="color" />
     <attr name="folderIconRadius" format="float" />
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
@@ -72,7 +73,7 @@
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="FolderIconPreview">
-        <attr name="folderFillColor" />
+        <attr name="folderPreviewColor" />
         <attr name="folderIconBorderColor" />
         <attr name="folderDotColor" />
     </declare-styleable>
@@ -184,11 +185,9 @@
         <attr name="twoPanelPortraitMinCellHeightDps" format="float" />
         <!-- twoPanelPortraitMinCellHeightDps defaults to minCellWidthDps, if not specified -->
         <attr name="twoPanelPortraitMinCellWidthDps" format="float" />
-        <!-- twoPanelLandscapeMinCellHeightDps defaults to twoPanelPortraitMinCellHeightDps,
-        if not specified -->
+        <!-- twoPanelLandscapeMinCellHeightDps defaults to minCellHeightDps, if not specified -->
         <attr name="twoPanelLandscapeMinCellHeightDps" format="float" />
-        <!-- twoPanelLandscapeMinCellWidthDps defaults to twoPanelPortraitMinCellWidthDps,
-        if not specified -->
+        <!-- twoPanelLandscapeMinCellWidthDps defaults to minCellWidthDps, if not specified -->
         <attr name="twoPanelLandscapeMinCellWidthDps" format="float" />
 
         <!-- These border spaces are only used if GridDisplayOption#isScalable is true -->
@@ -217,15 +216,20 @@
         if not specified -->
         <attr name="twoPanelLandscapeBorderSpaceVerticalDps" format="float" />
 
-
+        <!-- allAppsCellSpacingDps defaults to borderSpaceDps, if not specified -->
         <attr name="allAppsCellSpacingDps" format="float" />
+        <!-- The following values are only enabled if grid is supported. -->
+        <!-- allAppsIconSize defaults to iconSize, if not specified -->
+        <attr name="allAppsIconSize" format="float" />
+        <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
+        <attr name="allAppsIconTextSize" format="float" />
 
         <attr name="iconImageSize" format="float" />
         <!-- landscapeIconSize defaults to iconImageSize, if not specified -->
         <attr name="landscapeIconSize" format="float" />
         <!-- twoPanelPortraitIconSize defaults to iconImageSize, if not specified -->
         <attr name="twoPanelPortraitIconSize" format="float" />
-        <!-- twoPanelLandscapeIconSize defaults to landscapeIconSize, if not specified -->
+        <!-- twoPanelLandscapeIconSize defaults to iconImageSize, if not specified -->
         <attr name="twoPanelLandscapeIconSize" format="float" />
 
         <attr name="iconTextSize" format="float" />
@@ -233,7 +237,7 @@
         <attr name="landscapeIconTextSize" format="float" />
         <!-- twoPanelPortraitIconTextSize defaults to iconTextSize, if not specified -->
         <attr name="twoPanelPortraitIconTextSize" format="float" />
-        <!-- twoPanelLandscapeIconTextSize defaults to landscapeIconTextSize, if not specified -->
+        <!-- twoPanelLandscapeIconTextSize defaults to iconTextSize, if not specified -->
         <attr name="twoPanelLandscapeIconTextSize" format="float" />
 
         <!-- If set, this display option is used to determine the default grid -->
@@ -242,12 +246,6 @@
             <flag name="split_display" value="0x2" />
         </attr>
 
-        <!-- The following values are only enabled if grid is supported. -->
-        <!-- allAppsIconSize defaults to iconSize, if not specified -->
-        <attr name="allAppsIconSize" format="float" />
-        <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
-        <attr name="allAppsIconTextSize" format="float" />
-
         <!-- Margin on left and right of the workspace when GridDisplayOption#isScalable is true -->
         <attr name="horizontalMargin" format="float"/>
         <!-- twoPanelLandscapeHorizontalMargin defaults to horizontalMargin if not specified -->
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 5020127..0b1b451 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -63,6 +63,9 @@
     <color name="folder_background_light">#F9F9F9</color>
     <color name="folder_background_dark">#464746</color>
 
+    <color name="folder_preview_light">#F9F9F9</color>
+    <color name="folder_preview_dark">#464746</color>
+
     <color name="folder_dot_color">?attr/colorPrimary</color>
     <color name="folder_pagination_color_light">#ff006c5f</color>
     <color name="folder_pagination_color_dark">#ffbfebe3</color>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8ad4fcd..818a032 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -51,7 +51,8 @@
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
         <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
-        <item name="folderFillColor">@color/folder_background_light</item>
+        <item name="folderPreviewColor">@color/folder_preview_light</item>
+        <item name="folderBackgroundColor">@color/folder_background_light</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">@color/workspace_text_color_dark</item>
         <item name="isFolderDarkText">true</item>
@@ -110,7 +111,8 @@
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">@color/folder_dot_color</item>
         <item name="folderPaginationColor">@color/folder_pagination_color_dark</item>
-        <item name="folderFillColor">@color/folder_background_dark</item>
+        <item name="folderPreviewColor">@color/folder_preview_dark</item>
+        <item name="folderBackgroundColor">@color/folder_background_dark</item>
         <item name="folderIconBorderColor">?android:attr/colorPrimary</item>
         <item name="folderTextColor">@color/workspace_text_color_light</item>
         <item name="isFolderDarkText">false</item>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 4e06ff9..0b60b32 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -80,6 +80,7 @@
     public final float aspectRatio;
 
     public final boolean isScalableGrid;
+    private final int mTypeIndex;
 
     /**
      * The maximum amount of left/right workspace padding as a percentage of the screen width.
@@ -169,7 +170,7 @@
     public int qsbBottomMarginPx;
 
     // All apps
-    public int allAppsCellSpacingPx;
+    public Point allAppsCellSpacePx;
     public int allAppsOpenVerticalTranslate;
     public int allAppsCellHeightPx;
     public int allAppsCellWidthPx;
@@ -262,6 +263,20 @@
         mMetrics = context.getResources().getDisplayMetrics();
         final Resources res = context.getResources();
 
+        if (isTwoPanels) {
+            if (isLandscape) {
+                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_LANDSCAPE;
+            } else {
+                mTypeIndex = InvariantDeviceProfile.INDEX_TWO_PANEL_PORTRAIT;
+            }
+        } else {
+            if (isLandscape) {
+                mTypeIndex = InvariantDeviceProfile.INDEX_LANDSCAPE;
+            } else {
+                mTypeIndex = InvariantDeviceProfile.INDEX_DEFAULT;
+            }
+        }
+
         hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
         isTaskbarPresent = isTablet && ApiWrapper.TASKBAR_DRAWN_IN_PROCESS
                 && FeatureFlags.ENABLE_TASKBAR.get();
@@ -283,7 +298,9 @@
         folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
 
         cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
-        allAppsCellSpacingPx = pxFromDp(inv.allAppsCellSpacing, mMetrics, 1f);
+        allAppsCellSpacePx = new Point(
+                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].x, mMetrics, 1f),
+                pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].y, mMetrics, 1f));
         cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
         folderCellLayoutBorderSpaceOriginalPx = pxFromDp(inv.folderBorderSpace, mMetrics, 1f);
         folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
@@ -339,7 +356,8 @@
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
         hotseatExtraVerticalSize =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
-        updateHotseatIconSize(pxFromDp(inv.iconSize, mMetrics, 1f));
+        updateHotseatIconSize(
+                pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, 1f));
 
         qsbBottomMarginOriginalPx = isScalableGrid
                 ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -451,22 +469,9 @@
             return 0;
         }
 
-        int horizontalMarginPx;
-
-        if (isScalableGrid) {
-            if (isTwoPanels) {
-                if (isLandscape) {
-                    horizontalMarginPx = pxFromDp(idp.twoPanelLandscapeHorizontalMargin, mMetrics);
-                } else {
-                    horizontalMarginPx = pxFromDp(idp.twoPanelPortraitHorizontalMargin, mMetrics);
-                }
-            } else {
-                horizontalMarginPx = pxFromDp(idp.horizontalMargin, mMetrics);
-            }
-        } else {
-            horizontalMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
-        }
-        return horizontalMarginPx;
+        return isScalableGrid
+                ? pxFromDp(idp.horizontalMargin[mTypeIndex], mMetrics)
+                : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
     }
 
     private void updateHotseatIconSize(int hotseatIconSizePx) {
@@ -487,21 +492,8 @@
             return new Point(0, 0);
         }
 
-        int horizontalSpacePx;
-        int verticalSpacePx;
-
-        if (isTwoPanels) {
-            if (isLandscape) {
-                horizontalSpacePx = pxFromDp(idp.twoPanelLandscapeBorderSpace.x, mMetrics);
-                verticalSpacePx = pxFromDp(idp.twoPanelLandscapeBorderSpace.y, mMetrics);
-            } else {
-                horizontalSpacePx = pxFromDp(idp.twoPanelPortraitBorderSpace.x, mMetrics);
-                verticalSpacePx = pxFromDp(idp.twoPanelPortraitBorderSpace.y, mMetrics);
-            }
-        } else {
-            horizontalSpacePx = pxFromDp(idp.borderSpace.x, mMetrics);
-            verticalSpacePx = pxFromDp(idp.borderSpace.y, mMetrics);
-        }
+        int horizontalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].x, mMetrics);
+        int verticalSpacePx = pxFromDp(idp.borderSpaces[mTypeIndex].y, mMetrics);
 
         return new Point(horizontalSpacePx, verticalSpacePx);
     }
@@ -598,7 +590,7 @@
     private void updateAllAppsWidth() {
         if (isTwoPanels) {
             int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
-                    + (allAppsCellSpacingPx * (numShownAllAppsColumns + 1));
+                    + (allAppsCellSpacePx.x * (numShownAllAppsColumns + 1));
             allAppsLeftRightPadding = Math.max(1, (availableWidthPx - usedWidth) / 2);
         } else {
             allAppsLeftRightPadding =
@@ -661,26 +653,8 @@
 
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
-        float invIconSizeDp;
-        float invIconTextSizeSp;
-
-        if (isTwoPanels) {
-            if (isLandscape) {
-                invIconSizeDp = inv.twoPanelLandscapeIconSize;
-                invIconTextSizeSp = inv.twoPanelLandscapeIconTextSize;
-            } else {
-                invIconSizeDp = inv.twoPanelPortraitIconSize;
-                invIconTextSizeSp = inv.twoPanelPortraitIconTextSize;
-            }
-        } else {
-            if (isLandscape) {
-                invIconSizeDp = inv.landscapeIconSize;
-                invIconTextSizeSp = inv.landscapeIconTextSize;
-            } else {
-                invIconSizeDp = inv.iconSize;
-                invIconTextSizeSp = inv.iconTextSize;
-            }
-        }
+        float invIconSizeDp = inv.iconSize[mTypeIndex];
+        float invIconTextSizeSp = inv.iconTextSize[mTypeIndex];
 
         iconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, iconScale));
         iconTextSizePx = (int) (pxFromSp(invIconTextSizeSp, mMetrics) * iconScale);
@@ -689,9 +663,8 @@
         cellLayoutBorderSpacePx = getCellLayoutBorderSpaceScaled(inv, scale);
 
         if (isScalableGrid) {
-            PointF minCellHeightAndWidth = getMinCellHeightAndWidth();
-            cellWidthPx = pxFromDp(minCellHeightAndWidth.x, mMetrics, scale);
-            cellHeightPx = pxFromDp(minCellHeightAndWidth.y, mMetrics, scale);
+            cellWidthPx = pxFromDp(inv.minCellSize[mTypeIndex].x, mMetrics, scale);
+            cellHeightPx = pxFromDp(inv.minCellSize[mTypeIndex].y, mMetrics, scale);
             int cellContentHeight = iconSizePx + iconDrawablePaddingPx
                     + Utilities.calculateTextHeight(iconTextSizePx);
             cellYPaddingPx = Math.max(0, cellHeightPx - cellContentHeight) / 2;
@@ -715,8 +688,10 @@
 
         // All apps
         if (numShownAllAppsColumns != inv.numColumns) {
-            allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
-            allAppsIconTextSizePx = pxFromSp(inv.allAppsIconTextSize, mMetrics);
+            allAppsIconSizePx =
+                    pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+            allAppsIconTextSizePx =
+                    pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
             autoResizeAllAppsCells();
         } else {
@@ -752,28 +727,6 @@
         folderIconOffsetYPx = (iconSizePx - folderIconSizePx) / 2;
     }
 
-    /**
-     * Returns the minimum cell height and width as a pair.
-     */
-    private PointF getMinCellHeightAndWidth() {
-        PointF result = new PointF();
-
-        if (isTwoPanels) {
-            if (isLandscape) {
-                result.x = inv.twoPanelLandscapeMinCellWidthDps;
-                result.y = inv.twoPanelLandscapeMinCellHeightDps;
-            } else {
-                result.x = inv.twoPanelPortraitMinCellWidthDps;
-                result.y = inv.twoPanelPortraitMinCellHeightDps;
-            }
-        } else {
-            result.x = inv.minCellWidth;
-            result.y = inv.minCellHeight;
-        }
-
-        return result;
-    }
-
     private void updateAvailableFolderCellDimensions(Resources res) {
         updateFolderCellSize(1f, res);
 
@@ -804,9 +757,12 @@
     }
 
     private void updateFolderCellSize(float scale, Resources res) {
-        float invIconSizeDp = isVerticalBarLayout() ? inv.landscapeIconSize : inv.iconSize;
+        float invIconSizeDp = isVerticalBarLayout()
+                ? inv.iconSize[InvariantDeviceProfile.INDEX_LANDSCAPE]
+                : inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT];
         folderChildIconSizePx = Math.max(1, pxFromDp(invIconSizeDp, mMetrics, scale));
-        folderChildTextSizePx = pxFromSp(inv.iconTextSize, mMetrics, scale);
+        folderChildTextSizePx =
+                pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, scale);
         folderLabelTextSizePx = (int) (folderChildTextSizePx * folderLabelTextScale);
 
         int textHeight = Utilities.calculateTextHeight(folderChildTextSizePx);
@@ -901,7 +857,7 @@
                     + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
 
             padding.set(desiredWorkspaceHorizontalMarginPx,
-                    (isScalableGrid ? workspaceTopPadding : edgeMarginPx),
+                    workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx),
                     desiredWorkspaceHorizontalMarginPx,
                     paddingBottom);
         }
@@ -1094,19 +1050,10 @@
 
         writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
 
-        writer.println(prefix + "\tinv.minCellWidth:" + inv.minCellWidth + "dp");
-        writer.println(prefix + "\tinv.minCellHeight:" + inv.minCellHeight + "dp");
-        writer.println(prefix + "\tinv.twoPanelPortraitMinCellHeightDps:"
-                + inv.twoPanelPortraitMinCellHeightDps + "dp");
-        writer.println(prefix + "\tinv.twoPanelPortraitMinCellWidthDps:"
-                + inv.twoPanelPortraitMinCellWidthDps + "dp");
-        writer.println(prefix + "\tinv.twoPanelLandscapeMinCellHeightDps:"
-                + inv.twoPanelLandscapeMinCellHeightDps + "dp");
-        writer.println(prefix + "\tinv.twoPanelLandscapeMinCellWidthDps:"
-                + inv.twoPanelLandscapeMinCellWidthDps + "dp");
+        writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
+        writer.println(prefix + "\tinv.numRows: " + inv.numRows);
 
-        writer.println(prefix + "\tinv.numColumns:" + inv.numColumns);
-        writer.println(prefix + "\tinv.numRows:" + inv.numRows);
+        writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
 
         writer.println(prefix + pxToDpStr("cellWidthPx", cellWidthPx));
         writer.println(prefix + pxToDpStr("cellHeightPx", cellHeightPx));
@@ -1114,7 +1061,11 @@
         writer.println(prefix + pxToDpStr("getCellSize().x", getCellSize().x));
         writer.println(prefix + pxToDpStr("getCellSize().y", getCellSize().y));
 
-        writer.println(prefix + "\tinv.iconSize:" + inv.iconSize + "dp");
+        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Horizontal",
+                cellLayoutBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Vertical",
+                cellLayoutBorderSpacePx.y));
+
         writer.println(prefix + pxToDpStr("iconSizePx", iconSizePx));
         writer.println(prefix + pxToDpStr("iconTextSizePx", iconTextSizePx));
         writer.println(prefix + pxToDpStr("iconDrawablePaddingPx", iconDrawablePaddingPx));
@@ -1132,14 +1083,6 @@
         writer.println(prefix + pxToDpStr("folderCellLayoutBorderSpacePx Vertical",
                 folderCellLayoutBorderSpacePx.y));
 
-        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Horizontal",
-                cellLayoutBorderSpacePx.x));
-        writer.println(prefix + pxToDpStr("cellLayoutBorderSpacePx Vertical",
-                cellLayoutBorderSpacePx.y));
-
-        writer.println(prefix + pxToDpStr("desiredWorkspaceHorizontalMarginPx",
-                desiredWorkspaceHorizontalMarginPx));
-
         writer.println(prefix + pxToDpStr("allAppsIconSizePx", allAppsIconSizePx));
         writer.println(prefix + pxToDpStr("allAppsIconTextSizePx", allAppsIconTextSizePx));
         writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
@@ -1161,6 +1104,8 @@
         writer.println(prefix + "\tisTaskbarPresentInApps:" + isTaskbarPresentInApps);
         writer.println(prefix + pxToDpStr("taskbarSize", taskbarSize));
 
+        writer.println(prefix + pxToDpStr("desiredWorkspaceHorizontalMarginPx",
+                desiredWorkspaceHorizontalMarginPx));
         writer.println(prefix + pxToDpStr("workspacePadding.left", workspacePadding.left));
         writer.println(prefix + pxToDpStr("workspacePadding.top", workspacePadding.top));
         writer.println(prefix + pxToDpStr("workspacePadding.right", workspacePadding.right));
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index cfc14b6..ca07249 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -86,6 +86,15 @@
     // used to offset float not being able to express extremely small weights in extreme cases.
     private static final float WEIGHT_EFFICIENT = 100000f;
 
+    // Used for arrays to specify different sizes (e.g. border spaces, width/height) in different
+    // constraints
+    static final int COUNT_SIZES = 5;
+    static final int INDEX_DEFAULT = 0;
+    static final int INDEX_LANDSCAPE = 1;
+    static final int INDEX_TWO_PANEL_PORTRAIT = 2;
+    static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
+    static final int INDEX_ALL_APPS = 4;
+
     /**
      * Number of icons per row and column in the workspace.
      */
@@ -97,36 +106,18 @@
      */
     public int numFolderRows;
     public int numFolderColumns;
-    public float iconSize;
-    public float landscapeIconSize;
-    public float twoPanelPortraitIconSize;
-    public float twoPanelLandscapeIconSize;
-    public float landscapeIconTextSize;
-    public float twoPanelPortraitIconTextSize;
-    public float twoPanelLandscapeIconTextSize;
+    public float[] iconSize;
+    public float[] iconTextSize;
     public int iconBitmapSize;
     public int fillResIconDpi;
-    public float iconTextSize;
-    public float allAppsIconSize;
-    public float allAppsIconTextSize;
-    public float allAppsCellSpacing;
     public boolean isSplitDisplay;
 
-    public float minCellHeight;
-    public float minCellWidth;
-    public float twoPanelPortraitMinCellHeightDps;
-    public float twoPanelPortraitMinCellWidthDps;
-    public float twoPanelLandscapeMinCellHeightDps;
-    public float twoPanelLandscapeMinCellWidthDps;
+    public PointF[] minCellSize;
 
-    public PointF borderSpace;
-    public PointF twoPanelPortraitBorderSpace;
-    public PointF twoPanelLandscapeBorderSpace;
+    public PointF[] borderSpaces;
     public float folderBorderSpace;
 
-    public float horizontalMargin;
-    public float twoPanelLandscapeHorizontalMargin;
-    public float twoPanelPortraitHorizontalMargin;
+    public float[] horizontalMargin;
 
     private SparseArray<TypedValue> mExtraAttrs;
 
@@ -221,17 +212,17 @@
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
                 .add(myDisplayOption);
-        result.iconSizes[DisplayOption.INDEX_DEFAULT] =
-                defaultDisplayOption.iconSizes[DisplayOption.INDEX_DEFAULT];
-        for (int i = 1; i < DisplayOption.COUNT_SIZES; i++) {
+        result.iconSizes[INDEX_DEFAULT] =
+                defaultDisplayOption.iconSizes[INDEX_DEFAULT];
+        for (int i = 1; i < COUNT_SIZES; i++) {
             result.iconSizes[i] = Math.min(
                     defaultDisplayOption.iconSizes[i], myDisplayOption.iconSizes[i]);
         }
 
-        result.minCellHeight = defaultDisplayOption.minCellHeight;
-        result.minCellWidth = defaultDisplayOption.minCellWidth;
-        result.borderSpaces[DisplayOption.INDEX_ALL_APPS] =
-                defaultDisplayOption.borderSpaces[DisplayOption.INDEX_ALL_APPS];
+        System.arraycopy(defaultDisplayOption.minCellSize, 0, result.minCellSize, 0,
+                COUNT_SIZES);
+        System.arraycopy(defaultDisplayOption.borderSpaces, 0, result.borderSpaces, 0,
+                COUNT_SIZES);
 
         initGrid(context, myInfo, result, false);
     }
@@ -274,39 +265,18 @@
 
         mExtraAttrs = closestProfile.extraAttrs;
 
-        iconSize = displayOption.iconSizes[DisplayOption.INDEX_DEFAULT];
-        landscapeIconSize = displayOption.iconSizes[DisplayOption.INDEX_LANDSCAPE];
-        twoPanelPortraitIconSize = displayOption.iconSizes[DisplayOption.INDEX_TWO_PANEL_PORTRAIT];
-        twoPanelLandscapeIconSize =
-                displayOption.iconSizes[DisplayOption.INDEX_TWO_PANEL_LANDSCAPE];
-        iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
+        iconSize = displayOption.iconSizes;
+        iconBitmapSize = ResourceUtils.pxFromDp(iconSize[INDEX_DEFAULT], metrics);
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
-        iconTextSize = displayOption.textSizes[DisplayOption.INDEX_DEFAULT];
-        landscapeIconTextSize = displayOption.textSizes[DisplayOption.INDEX_LANDSCAPE];
-        twoPanelPortraitIconTextSize =
-                displayOption.textSizes[DisplayOption.INDEX_TWO_PANEL_PORTRAIT];
-        twoPanelLandscapeIconTextSize =
-                displayOption.textSizes[DisplayOption.INDEX_TWO_PANEL_LANDSCAPE];
+        iconTextSize = displayOption.textSizes;
 
-        minCellHeight = displayOption.minCellHeight;
-        minCellWidth = displayOption.minCellWidth;
-        twoPanelPortraitMinCellHeightDps = displayOption.twoPanelPortraitMinCellHeightDps;
-        twoPanelPortraitMinCellWidthDps = displayOption.twoPanelPortraitMinCellWidthDps;
-        twoPanelLandscapeMinCellHeightDps = displayOption.twoPanelLandscapeMinCellHeightDps;
-        twoPanelLandscapeMinCellWidthDps = displayOption.twoPanelLandscapeMinCellWidthDps;
+        minCellSize = displayOption.minCellSize;
 
-        borderSpace = displayOption.borderSpaces[DisplayOption.INDEX_DEFAULT];
-        twoPanelPortraitBorderSpace =
-                displayOption.borderSpaces[DisplayOption.INDEX_TWO_PANEL_PORTRAIT];
-        twoPanelLandscapeBorderSpace =
-                displayOption.borderSpaces[DisplayOption.INDEX_TWO_PANEL_LANDSCAPE];
-        allAppsCellSpacing = displayOption.borderSpaces[DisplayOption.INDEX_ALL_APPS].x;
+        borderSpaces = displayOption.borderSpaces;
         folderBorderSpace = displayOption.folderBorderSpace;
 
         horizontalMargin = displayOption.horizontalMargin;
-        twoPanelLandscapeHorizontalMargin = displayOption.twoPanelLandscapeHorizontalMargin;
-        twoPanelPortraitHorizontalMargin = displayOption.twoPanelPortraitHorizontalMargin;
 
         numShownHotseatIcons = closestProfile.numHotseatIcons;
         numDatabaseHotseatIcons = isSplitDisplay
@@ -316,12 +286,9 @@
         numDatabaseAllAppsColumns = isSplitDisplay
                 ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
-        if (Utilities.isGridOptionsEnabled(context)) {
-            allAppsIconSize = displayOption.iconSizes[DisplayOption.INDEX_ALL_APPS];
-            allAppsIconTextSize = displayOption.textSizes[DisplayOption.INDEX_ALL_APPS];
-        } else {
-            allAppsIconSize = iconSize;
-            allAppsIconTextSize = iconTextSize;
+        if (!Utilities.isGridOptionsEnabled(context)) {
+            iconSize[INDEX_ALL_APPS] = iconSize[INDEX_DEFAULT];
+            iconTextSize[INDEX_ALL_APPS] = iconTextSize[INDEX_DEFAULT];
         }
 
         if (devicePaddingId != 0) {
@@ -566,11 +533,11 @@
 
         // Since the bitmaps are persisted, ensure that the default bitmap size is same as
         // predefined size to avoid cache invalidation
-        out.iconSizes[DisplayOption.INDEX_DEFAULT] =
-                closestPoint.iconSizes[DisplayOption.INDEX_DEFAULT];
-        for (int i = DisplayOption.INDEX_DEFAULT + 1; i < DisplayOption.COUNT_SIZES; i++) {
+        out.iconSizes[INDEX_DEFAULT] =
+                closestPoint.iconSizes[INDEX_DEFAULT];
+        for (int i = INDEX_DEFAULT + 1; i < COUNT_SIZES; i++) {
             out.iconSizes[i] = Math.min(out.iconSizes[i],
-                    out.iconSizes[DisplayOption.INDEX_DEFAULT]);
+                    out.iconSizes[INDEX_DEFAULT]);
         }
 
         return out;
@@ -724,32 +691,17 @@
     @VisibleForTesting
     static final class DisplayOption {
 
-        static final int COUNT_SIZES = 5;
-        static final int INDEX_DEFAULT = 0;
-        static final int INDEX_LANDSCAPE = 1;
-        static final int INDEX_TWO_PANEL_PORTRAIT = 2;
-        static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
-        static final int INDEX_ALL_APPS = 4;
-
         public final GridOption grid;
 
         private final float minWidthDps;
         private final float minHeightDps;
         private final boolean canBeDefault;
 
-        private float minCellHeight;
-        private float minCellWidth;
-        private float twoPanelPortraitMinCellHeightDps;
-        private float twoPanelPortraitMinCellWidthDps;
-        private float twoPanelLandscapeMinCellHeightDps;
-        private float twoPanelLandscapeMinCellWidthDps;
+        private final PointF[] minCellSize = new PointF[COUNT_SIZES];
 
         private float folderBorderSpace;
         private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
-
-        private float horizontalMargin;
-        private float twoPanelLandscapeHorizontalMargin;
-        private float twoPanelPortraitHorizontalMargin;
+        private final float[] horizontalMargin = new float[COUNT_SIZES];
 
         private final float[] iconSizes = new float[COUNT_SIZES];
         private final float[] textSizes = new float[COUNT_SIZES];
@@ -766,27 +718,32 @@
             canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
                     == defaultFlagValue;
 
-            minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
-            minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
-            twoPanelPortraitMinCellHeightDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
-                    minCellHeight);
-            twoPanelPortraitMinCellWidthDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps, minCellWidth);
-            twoPanelLandscapeMinCellHeightDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
-                    twoPanelPortraitMinCellHeightDps);
-            twoPanelLandscapeMinCellWidthDps = a.getFloat(
-                    R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
-                    twoPanelPortraitMinCellWidthDps);
+            float x;
+            float y;
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+            minCellSize[INDEX_DEFAULT] = new PointF(x, y);
+            minCellSize[INDEX_LANDSCAPE] = new PointF(x, y);
+            minCellSize[INDEX_ALL_APPS] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps,
+                    minCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
+                    minCellSize[INDEX_DEFAULT].y);
+            minCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+
+            x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
+                    minCellSize[INDEX_DEFAULT].x);
+            y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
+                    minCellSize[INDEX_DEFAULT].y);
+            minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
 
             float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceDps, 0);
             float twoPanelPortraitBorderSpaceDps = a.getFloat(
                     R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceDps, borderSpace);
             float twoPanelLandscapeBorderSpaceDps = a.getFloat(
                     R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceDps, borderSpace);
-            float x;
-            float y;
 
             x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontalDps, borderSpace);
             y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVerticalDps, borderSpace);
@@ -827,7 +784,7 @@
                             iconSizes[INDEX_DEFAULT]);
             iconSizes[INDEX_TWO_PANEL_LANDSCAPE] =
                     a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
-                            iconSizes[INDEX_LANDSCAPE]);
+                            iconSizes[INDEX_DEFAULT]);
 
             textSizes[INDEX_DEFAULT] =
                     a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
@@ -842,15 +799,18 @@
                             textSizes[INDEX_DEFAULT]);
             textSizes[INDEX_TWO_PANEL_LANDSCAPE] =
                     a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
-                            textSizes[INDEX_LANDSCAPE]);
+                            textSizes[INDEX_DEFAULT]);
 
-            horizontalMargin = a.getFloat(R.styleable.ProfileDisplayOption_horizontalMargin, 0);
-            twoPanelLandscapeHorizontalMargin = a.getFloat(
+            horizontalMargin[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_horizontalMargin, 0);
+            horizontalMargin[INDEX_LANDSCAPE] = horizontalMargin[INDEX_DEFAULT];
+            horizontalMargin[INDEX_ALL_APPS] = horizontalMargin[INDEX_DEFAULT];
+            horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
                     R.styleable.ProfileDisplayOption_twoPanelLandscapeHorizontalMargin,
-                    horizontalMargin);
-            twoPanelPortraitHorizontalMargin = a.getFloat(
+                    horizontalMargin[INDEX_DEFAULT]);
+            horizontalMargin[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
                     R.styleable.ProfileDisplayOption_twoPanelPortraitHorizontalMargin,
-                    horizontalMargin);
+                    horizontalMargin[INDEX_DEFAULT]);
 
             a.recycle();
         }
@@ -864,12 +824,11 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
-            minCellHeight = 0;
-            minCellWidth = 0;
             for (int i = 0; i < COUNT_SIZES; i++) {
                 iconSizes[i] = 0;
                 textSizes[i] = 0;
                 borderSpaces[i] = new PointF();
+                minCellSize[i] = new PointF();
             }
         }
 
@@ -877,21 +836,15 @@
             for (int i = 0; i < COUNT_SIZES; i++) {
                 iconSizes[i] *= w;
                 textSizes[i] *= w;
-                borderSpaces[i].x *=  w;
-                borderSpaces[i].y *=  w;
+                borderSpaces[i].x *= w;
+                borderSpaces[i].y *= w;
+                minCellSize[i].x *= w;
+                minCellSize[i].y *= w;
+                horizontalMargin[i] *= w;
             }
-            minCellHeight *= w;
-            minCellWidth *= w;
-            twoPanelPortraitMinCellHeightDps *= w;
-            twoPanelPortraitMinCellWidthDps *= w;
-            twoPanelLandscapeMinCellHeightDps *= w;
-            twoPanelLandscapeMinCellWidthDps *= w;
 
             folderBorderSpace *= w;
 
-            horizontalMargin *= w;
-            twoPanelLandscapeHorizontalMargin *= w;
-            twoPanelPortraitHorizontalMargin *= w;
             return this;
         }
 
@@ -899,21 +852,15 @@
             for (int i = 0; i < COUNT_SIZES; i++) {
                 iconSizes[i] += p.iconSizes[i];
                 textSizes[i] += p.textSizes[i];
-                borderSpaces[i].x +=  p.borderSpaces[i].x;
-                borderSpaces[i].y +=  p.borderSpaces[i].y;
+                borderSpaces[i].x += p.borderSpaces[i].x;
+                borderSpaces[i].y += p.borderSpaces[i].y;
+                minCellSize[i].x += p.minCellSize[i].x;
+                minCellSize[i].y += p.minCellSize[i].y;
+                horizontalMargin[i] += p.horizontalMargin[i];
             }
-            minCellHeight += p.minCellHeight;
-            minCellWidth += p.minCellWidth;
-            twoPanelPortraitMinCellHeightDps += p.twoPanelPortraitMinCellHeightDps;
-            twoPanelPortraitMinCellWidthDps += p.twoPanelPortraitMinCellWidthDps;
-            twoPanelLandscapeMinCellHeightDps += p.twoPanelLandscapeMinCellHeightDps;
-            twoPanelLandscapeMinCellWidthDps += p.twoPanelLandscapeMinCellWidthDps;
 
             folderBorderSpace += p.folderBorderSpace;
 
-            horizontalMargin += p.horizontalMargin;
-            twoPanelLandscapeHorizontalMargin += p.twoPanelLandscapeHorizontalMargin;
-            twoPanelPortraitHorizontalMargin += p.twoPanelPortraitHorizontalMargin;
             return this;
         }
     }
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
index 0bdb37c..2e27f32 100644
--- a/src/com/android/launcher3/Partner.java
+++ b/src/com/android/launcher3/Partner.java
@@ -142,7 +142,7 @@
         }
 
         if (iconSize > 0) {
-            inv.iconSize = iconSize;
+            inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT] = iconSize;
         }
     }
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index b92cf09..36faeee 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -45,6 +45,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
@@ -80,6 +81,7 @@
 import com.android.launcher3.icons.ShortcutCachingLogic;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -705,6 +707,10 @@
             }
             outObj[0] = icon;
             return icon;
+        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
+                && info instanceof SearchActionItemInfo) {
+            return new AdaptiveIconDrawable(
+                    new FastBitmapDrawable(((SearchActionItemInfo) info).bitmap), null);
         } else {
             return null;
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 9bb4b25..f154dd4 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -63,7 +63,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.keyboard.FocusedItemDecorator;
 import com.android.launcher3.model.data.AppInfo;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -482,10 +481,6 @@
         int layout = showTabs ? R.layout.all_apps_tabs : R.layout.all_apps_rv_layout;
         View newView = getLayoutInflater().inflate(layout, this, false);
         addView(newView, index);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "should show tabs:" + showTabs,
-                    new Exception());
-        }
         if (showTabs) {
             mViewPager = (AllAppsPagedView) newView;
             mViewPager.initParentViews(this);
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index c53360a..54a5c51 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -172,4 +172,8 @@
     private AllAppsContainerView.AdapterHolder getAH() {
         return mAllApps.mAH[AllAppsContainerView.AdapterHolder.WORK];
     }
+
+    public int getCurrentState() {
+        return mCurrentState;
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 57d6cc3..fa65945 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -205,6 +205,7 @@
     @TargetApi(Build.VERSION_CODES.O)
     public void setItemInfo(final ItemInfo info) {
         if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
+                && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
                 && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
                 && info.itemType != LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
             return;
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 399d142..61ffd9d 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.graphics.IconShape.getShape;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -175,15 +174,9 @@
         final float yDistance = initialY - lp.y;
 
         // Set up the Folder background.
-        final int finalColor;
-        int folderFillColor = Themes.getAttrColor(mContext, R.attr.folderFillColor);
-        if (mIsOpening) {
-            finalColor = folderFillColor;
-        } else {
-            finalColor = mFolderBackground.getColor().getDefaultColor();
-        }
-        final int initialColor = setColorAlphaBound(
-                folderFillColor, mPreviewBackground.getBackgroundAlpha());
+        final int initialColor = Themes.getAttrColor(mContext, R.attr.folderPreviewColor);
+        final int finalColor = Themes.getAttrColor(mContext, R.attr.folderBackgroundColor);
+
         mFolderBackground.mutate();
         mFolderBackground.setColor(mIsOpening ? initialColor : finalColor);
 
diff --git a/src/com/android/launcher3/folder/PreviewBackground.java b/src/com/android/launcher3/folder/PreviewBackground.java
index 18d0b10..8f9fa8a 100644
--- a/src/com/android/launcher3/folder/PreviewBackground.java
+++ b/src/com/android/launcher3/folder/PreviewBackground.java
@@ -66,7 +66,6 @@
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
 
     float mScale = 1f;
-    private float mColorMultiplier = 1f;
     private int mBgColor;
     private int mStrokeColor;
     private int mDotColor;
@@ -87,7 +86,6 @@
 
     // Drawing / animation configurations
     private static final float ACCEPT_SCALE_FACTOR = 1.20f;
-    private static final float ACCEPT_COLOR_MULTIPLIER = 1.5f;
 
     // Expressed on a scale from 0 to 255.
     private static final int BG_OPACITY = 255;
@@ -154,7 +152,7 @@
         TypedArray ta = context.getTheme().obtainStyledAttributes(R.styleable.FolderIconPreview);
         mDotColor = ta.getColor(R.styleable.FolderIconPreview_folderDotColor, 0);
         mStrokeColor = ta.getColor(R.styleable.FolderIconPreview_folderIconBorderColor, 0);
-        mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderFillColor, 0);
+        mBgColor = ta.getColor(R.styleable.FolderIconPreview_folderPreviewColor, 0);
         ta.recycle();
 
         DeviceProfile grid = activity.getDeviceProfile();
@@ -227,8 +225,7 @@
     }
 
     public int getBgColor() {
-        int alpha = (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
-        return setColorAlphaBound(mBgColor, alpha);
+        return mBgColor;
     }
 
     public int getDotColor() {
@@ -384,14 +381,10 @@
         return mDrawingDelegate != null;
     }
 
-    private void animateScale(float finalScale, float finalMultiplier,
-                              final Runnable onStart, final Runnable onEnd) {
+    private void animateScale(float finalScale, final Runnable onStart, final Runnable onEnd) {
         final float scale0 = mScale;
         final float scale1 = finalScale;
 
-        final float bgMultiplier0 = mColorMultiplier;
-        final float bgMultiplier1 = finalMultiplier;
-
         if (mScaleAnimator != null) {
             mScaleAnimator.cancel();
         }
@@ -403,7 +396,6 @@
             public void onAnimationUpdate(ValueAnimator animation) {
                 float prog = animation.getAnimatedFraction();
                 mScale = prog * scale1 + (1 - prog) * scale0;
-                mColorMultiplier = prog * bgMultiplier1 + (1 - prog) * bgMultiplier0;
                 invalidate();
             }
         });
@@ -429,8 +421,7 @@
     }
 
     public void animateToAccept(CellLayout cl, int cellX, int cellY) {
-        animateScale(ACCEPT_SCALE_FACTOR, ACCEPT_COLOR_MULTIPLIER,
-                () -> delegateDrawing(cl, cellX, cellY), null);
+        animateScale(ACCEPT_SCALE_FACTOR, () -> delegateDrawing(cl, cellX, cellY), null);
     }
 
     public void animateToRest() {
@@ -440,11 +431,7 @@
         CellLayout cl = mDrawingDelegate;
         int cellX = mDelegateCellX;
         int cellY = mDelegateCellY;
-        animateScale(1f, 1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
-    }
-
-    public int getBackgroundAlpha() {
-        return (int) Math.min(MAX_BG_OPACITY, BG_OPACITY * mColorMultiplier);
+        animateScale(1f, () -> delegateDrawing(cl, cellX, cellY), this::clearDrawingDelegate);
     }
 
     public float getStrokeWidth() {
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 5ade22b..5aab41a 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -21,10 +21,8 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.util.LongSparseArray;
 
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -60,9 +58,6 @@
     private void onUsersChanged(Intent intent) {
         enableAndResetCache();
         mUserChangeListeners.forEach(Runnable::run);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "profile changed", new Exception());
-        }
     }
 
     /**
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index f67bac6..893a215 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -117,7 +117,6 @@
     public static final String REQUEST_MOCK_SENSOR_ROTATION = "mock-sensor-rotation";
 
     public static final String PERMANENT_DIAG_TAG = "TaplTarget";
-    public static final String WORK_PROFILE_REMOVED = "b/159671700";
     public static final String TASK_VIEW_ID_CRASH = "b/195430732";
     public static final String NO_DROP_TARGET = "b/195031154";
     public static final String NULL_INT_SET = "b/200572078";
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index b6b6cdd..0ffbeeb 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -262,8 +262,6 @@
         if (userManager != null) {
             for (UserHandle userHandle : userManager.getUserProfiles()) {
                 if (!userHandle.isSystem()) {
-                    Log.d(TestProtocol.WORK_PROFILE_REMOVED,
-                            "removing user " + userHandle.getIdentifier());
                     mDevice.executeShellCommand("pm remove-user " + userHandle.getIdentifier());
                 }
             }
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
new file mode 100644
index 0000000..fcb0b7f
--- /dev/null
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.ui;
+
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.allapps.WorkAdapterProvider;
+import com.android.launcher3.allapps.WorkEduCard;
+import com.android.launcher3.allapps.WorkProfileManager;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Objects;
+
+public class WorkProfileTest extends AbstractLauncherUiTest {
+
+    private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
+
+    private int mProfileUserId;
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        String output =
+                mDevice.executeShellCommand(
+                        "pm create-user --profileOf 0 --managed TestProfile");
+        assertTrue("Failed to create work profile", output.startsWith("Success"));
+
+        String[] tokens = output.split("\\s+");
+        mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
+        mDevice.executeShellCommand("am start-user " + mProfileUserId);
+    }
+
+    @After
+    public void removeWorkProfile() throws Exception {
+        mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
+    }
+
+    @After
+    public void resumeAppStoreUpdate() {
+        executeOnLauncher(launcher -> {
+            if (launcher == null || launcher.getAppsView() == null) {
+                return;
+            }
+            launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
+        });
+    }
+
+    private void waitForWorkTabSetup() {
+        waitForLauncherCondition("Work tab not setup", launcher -> {
+            if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
+                launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
+                return true;
+            }
+            return false;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+    }
+
+    @Test
+    public void workTabExists() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+        waitForLauncherCondition("Personal tab is missing",
+                launcher -> launcher.getAppsView().isPersonalTabVisible(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+        waitForLauncherCondition("Work tab is missing",
+                launcher -> launcher.getAppsView().isWorkTabVisible(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+    }
+
+    @Test
+    public void toggleWorks() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+
+        waitForWorkTabSetup();
+
+        executeOnLauncher(launcher -> {
+            AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
+            pagedView.setCurrentPage(WORK_PAGE);
+        });
+
+        WorkProfileManager manager = getFromLauncher(l -> l.getAppsView().getWorkManager());
+
+
+        waitForLauncherCondition("work profile initial state check failed", launcher ->
+                        manager.getWorkModeSwitch() != null
+                                && manager.getCurrentState() == WorkProfileManager.STATE_ENABLED
+                                && manager.getWorkModeSwitch().isEnabled(),
+                LauncherInstrumentation.WAIT_TIME_MS);
+
+        //start work profile toggle OFF test
+        executeOnLauncher(l -> l.getAppsView().getWorkManager().getWorkModeSwitch().performClick());
+
+        waitForLauncherCondition("Work profile toggle OFF failed", launcher -> {
+            manager.reset(); // pulls current state from system
+            return manager.getCurrentState() == WorkProfileManager.STATE_DISABLED;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+
+        // start work profile toggle ON test
+        executeOnLauncher(l -> {
+            AllAppsContainerView allApps = l.getAppsView();
+            assertEquals("Work tab is not focused", allApps.getCurrentPage(), WORK_PAGE);
+            View workPausedCard = allApps.getActiveRecyclerView().findViewHolderForAdapterPosition(
+                    0).itemView;
+            workPausedCard.findViewById(R.id.enable_work_apps).performClick();
+        });
+        waitForLauncherCondition("Work profile toggle ON failed", launcher -> {
+            manager.reset(); // pulls current state from system
+            return manager.getCurrentState() == WorkProfileManager.STATE_ENABLED;
+        }, LauncherInstrumentation.WAIT_TIME_MS);
+
+    }
+
+    @Test
+    public void testEdu() {
+        mDevice.pressHome();
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
+        waitForState("Launcher internal state didn't switch to Normal", () -> NORMAL);
+        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
+        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
+        waitForWorkTabSetup();
+        executeOnLauncher(l -> {
+            l.getSharedPrefs().edit().putInt(WorkAdapterProvider.KEY_WORK_EDU_STEP, 0).commit();
+            ((AllAppsPagedView) l.getAppsView().getContentView()).setCurrentPage(WORK_PAGE);
+            l.getAppsView().getWorkManager().reset();
+        });
+
+        waitForLauncherCondition("Work profile education not shown",
+                l -> l.getAppsView().getActiveRecyclerView()
+                        .findViewHolderForAdapterPosition(0).itemView instanceof WorkEduCard,
+                LauncherInstrumentation.WAIT_TIME_MS);
+    }
+}
diff --git a/tests/src_disabled/WorkTabTest.java b/tests/src_disabled/WorkTabTest.java
deleted file mode 100644
index bfacc74..0000000
--- a/tests/src_disabled/WorkTabTest.java
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright 2018, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.ui;
-
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.widget.TextView;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.R;
-import com.android.launcher3.allapps.AllAppsContainerView;
-import com.android.launcher3.allapps.AllAppsPagedView;
-import com.android.launcher3.allapps.WorkModeSwitch;
-import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.views.WorkEduView;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.atomic.AtomicInteger;
-
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class WorkTabTest extends AbstractLauncherUiTest {
-
-    private int mProfileUserId;
-
-    private static final int WORK_PAGE = AllAppsContainerView.AdapterHolder.WORK;
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        String output =
-                mDevice.executeShellCommand(
-                        "pm create-user --profileOf 0 --managed TestProfile");
-        assertTrue("Failed to create work profile", output.startsWith("Success"));
-
-        String[] tokens = output.split("\\s+");
-        mProfileUserId = Integer.parseInt(tokens[tokens.length - 1]);
-        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Created new user uid" + mProfileUserId);
-        mDevice.executeShellCommand("am start-user " + mProfileUserId);
-    }
-
-    @After
-    public void removeWorkProfile() throws Exception {
-        Log.d(TestProtocol.WORK_PROFILE_REMOVED, "(teardown) removing uid" + mProfileUserId,
-                new Exception());
-        mDevice.executeShellCommand("pm remove-user " + mProfileUserId);
-    }
-
-    @After
-    public void resumeAppStoreUpdate() {
-        executeOnLauncher(launcher -> {
-            if (launcher == null || launcher.getAppsView() == null) {
-                return;
-            }
-            launcher.getAppsView().getAppsStore().disableDeferUpdates(DEFER_UPDATES_TEST);
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED, "resuming AppStore updates");
-        });
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void workTabExists() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
-        waitForLauncherCondition("Personal tab is missing",
-                launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
-        waitForLauncherCondition("Work tab is missing",
-                launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void toggleWorks() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher internal state didn't switch to All Apps", () -> ALL_APPS);
-        getOnceNotNull("Apps view did not bind",
-                launcher -> launcher.getAppsView().getWorkModeSwitch(), 60000);
-
-        UserManager userManager = getFromLauncher(l -> l.getSystemService(UserManager.class));
-        assertEquals(2, userManager.getUserProfiles().size());
-        UserHandle workProfile = getFromLauncher(l -> {
-            UserHandle myHandle = Process.myUserHandle();
-            List<UserHandle> userProfiles = userManager.getUserProfiles();
-            return userProfiles.get(0) == myHandle ? userProfiles.get(1) : userProfiles.get(0);
-        });
-
-        waitForLauncherCondition("work profile can't be turned off",
-                l -> userManager.requestQuietModeEnabled(true, workProfile));
-
-        assertTrue(userManager.isQuietModeEnabled(workProfile));
-        executeOnLauncher(launcher -> {
-            WorkModeSwitch wf = launcher.getAppsView().getWorkModeSwitch();
-            ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
-                    AllAppsContainerView.AdapterHolder.WORK);
-            wf.toggle();
-        });
-        waitForLauncherCondition("Work toggle did not work",
-                l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void testWorkEduFlow() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getSharedPrefs().edit().remove(
-                WorkEduView.KEY_WORK_EDU_STEP).remove(
-                WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
-
-        waitForLauncherCondition("Work tab not setup", launcher -> {
-            if (launcher.getAppsView().getContentView() instanceof AllAppsPagedView) {
-                launcher.getAppsView().getAppsStore().enableDeferUpdates(DEFER_UPDATES_TEST);
-                return true;
-            }
-            return false;
-        }, LauncherInstrumentation.WAIT_TIME_MS);
-
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        WorkEduView workEduView = getEduView();
-        // verify personal app edu is seen first and click "next"
-        executeOnLauncher(l -> {
-            assertEquals(((TextView) workEduView.findViewById(R.id.content_text)).getText(),
-                    l.getResources().getString(R.string.work_profile_edu_personal_apps));
-            workEduView.findViewById(R.id.proceed).callOnClick();
-        });
-
-        AtomicInteger attempt = new AtomicInteger(0);
-        // verify work edu is seen next
-        waitForLauncherCondition("Launcher did not show the next edu screen", l -> {
-            Log.d(TestProtocol.WORK_PROFILE_REMOVED,
-                    "running test attempt" + attempt.getAndIncrement());
-            if (!(l.getAppsView().getContentView() instanceof AllAppsPagedView)) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work tab not setup. Skipping test");
-                return false;
-            }
-            if (((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage()
-                    != WORK_PAGE) {
-                Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work page not highlighted");
-            }
-            return ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
-                    l.getResources().getString(R.string.work_profile_edu_work_apps));
-        });
-    }
-
-    @Ignore("b/182844465")
-    @Test
-    public void testWorkEduIntermittent() {
-        mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
-        executeOnLauncher(launcher -> launcher.getSharedPrefs().edit().remove(
-                WorkEduView.KEY_WORK_EDU_STEP).remove(
-                WorkEduView.KEY_LEGACY_WORK_EDU_SEEN).commit());
-
-
-        waitForLauncherCondition("Work tab not setup",
-                launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
-                60000);
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-
-        // verify personal app edu is seen
-        getEduView();
-
-        // dismiss personal edu
-        mDevice.pressHome();
-        waitForState("Launcher did not go home", () -> NORMAL);
-
-        // open work tab
-        executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-        waitForState("Launcher did not switch to all apps", () -> ALL_APPS);
-        waitForLauncherCondition("Work tab not setup",
-                launcher -> launcher.getAppsView().getContentView() instanceof AllAppsPagedView,
-                60000);
-
-        executeOnLauncher(launcher -> {
-            AllAppsPagedView pagedView = (AllAppsPagedView) launcher.getAppsView().getContentView();
-            pagedView.setCurrentPage(WORK_PAGE);
-        });
-
-        WorkEduView workEduView = getEduView();
-
-        // verify work tab edu is shown
-        waitForLauncherCondition("Launcher did not show the next edu screen",
-                l -> ((TextView) workEduView.findViewById(R.id.content_text)).getText().equals(
-                        l.getResources().getString(R.string.work_profile_edu_work_apps)));
-    }
-
-
-    private WorkEduView getEduView() {
-        waitForLauncherCondition("Edu did not show", l -> {
-            DragLayer dragLayer = l.getDragLayer();
-            return dragLayer.getChildCount() > 0 && dragLayer.getChildAt(
-                    dragLayer.getChildCount() - 1) instanceof WorkEduView;
-        }, 6000);
-        return getFromLauncher(launcher -> (WorkEduView) launcher.getDragLayer().getChildAt(
-                launcher.getDragLayer().getChildCount() - 1));
-    }
-
-}
\ No newline at end of file