Merge "Adding new tracing call from SysUI" into ub-launcher3-master
diff --git a/Android.bp b/Android.bp
index fc99880..cb695df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -32,7 +32,7 @@
 }
 
 java_library_static {
-    name: "launcher-log-protos-lite",
+    name: "launcher_log_protos_lite",
     srcs: [
         "protos/*.proto",
         "proto_overrides/*.proto",
@@ -45,4 +45,5 @@
             "proto_overrides",
         ],
     },
+    static_libs: ["libprotobuf-java-lite"],
 }
diff --git a/Android.mk b/Android.mk
index 66ccae0..c066a12 100644
--- a/Android.mk
+++ b/Android.mk
@@ -48,7 +48,9 @@
     androidx.preference_preference \
     iconloader_base
 
-LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    LauncherPluginLib \
+    launcher_log_protos_lite
 
 LOCAL_SRC_FILES := \
     $(call all-proto-files-under, protos) \
@@ -144,7 +146,10 @@
 LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    SystemUISharedLib \
+    launcherprotosnano \
+    launcher_log_protos_lite
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
@@ -213,7 +218,10 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_JAVA_LIBRARIES := SystemUISharedLib launcherprotosnano
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    SystemUISharedLib \
+    launcherprotosnano \
+    launcher_log_protos_lite
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 26698eb..d747bb8 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -44,6 +44,7 @@
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
 
     <!--
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 7690b9d..3b3dc01 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.model;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.os.UserHandle;
 
@@ -68,4 +69,9 @@
     public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
             LauncherAppState app) {
     }
+
+    public WidgetItem getWidgetProviderInfoByProviderName(
+            ComponentName providerName) {
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/proguard.flags b/proguard.flags
index 01302cf..e556c94 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -48,3 +48,6 @@
 -dontwarn android.view.**
 -dontwarn android.os.**
 -dontwarn android.graphics.**
+
+# Ignore warnings for hidden utility classes referenced from the shared lib
+-dontwarn com.android.internal.util.**
\ No newline at end of file
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 0560d68..ec1d55b 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -58,6 +58,35 @@
   optional TipType tip_type = 17;
   optional int32 search_query_length = 18;
   optional bool is_work_app = 19;
+  optional FromFolderLabelState from_folder_label_state = 20;
+  optional ToFolderLabelState to_folder_label_state = 21;
+
+  // Note: proto does not support duplicate enum values, even if they belong to different enum type.
+  // Hence "FROM" and "TO" prefix added.
+  enum FromFolderLabelState{
+    FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+    FROM_EMPTY = 1;
+    FROM_CUSTOM = 2;
+    FROM_SUGGESTED = 3;
+  }
+
+  enum ToFolderLabelState{
+    TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
+    TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
+    TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
+    TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 3;
+    TO_SUGGESTION2_WITH_VALID_PRIMARY = 4;
+    TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
+    TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
+    TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
+    TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
+    TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
+    TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
+    TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
+    TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
+    TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
+    UNCHANGED = 14;
+  }
 }
 
 // Used to define what type of item a Target would represent.
@@ -120,6 +149,8 @@
   BACK_GESTURE = 19;
   UNDO = 20;
   DISMISS_PREDICTION = 21;
+  HYBRID_HOTSEAT_ACCEPTED = 22;
+  HYBRID_HOTSEAT_CANCELED = 23;
 }
 
 enum TipType {
@@ -129,6 +160,7 @@
   QUICK_SCRUB_TEXT = 3;
   PREDICTION_TEXT = 4;
   DWB_TOAST = 5;
+  HYBRID_HOTSEAT = 6;
 }
 
 // Used to define the action component of the LauncherEvent.
@@ -138,7 +170,8 @@
     AUTOMATED = 1;
     COMMAND = 2;
     TIP = 3;
-    // SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
+    SOFT_KEYBOARD = 4;
+    // HARD_KEYBOARD, ASSIST
   }
 
   enum Touch {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 5d871c3..a06a2dd 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -24,6 +24,7 @@
 
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
 
     <application
         android:backupAgent="com.android.launcher3.LauncherBackupAgent"
@@ -46,10 +47,7 @@
             </intent-filter>
         </service>
 
-        <!-- STOPSHIP: Change exported to false once all the integration is complete.
-        It is set to true so that the activity can be started from command line -->
         <activity android:name="com.android.quickstep.RecentsActivity"
-            android:exported="true"
             android:excludeFromRecents="true"
             android:launchMode="singleTask"
             android:clearTaskOnLaunch="true"
@@ -92,12 +90,12 @@
                   android:directBootAware="true" />
 
         <activity
-            android:name="com.android.quickstep.interaction.BackGestureTutorialActivity"
+            android:name="com.android.quickstep.interaction.GestureSandboxActivity"
             android:autoRemoveFromRecents="true"
             android:excludeFromRecents="true"
             android:screenOrientation="portrait">
             <intent-filter>
-                <action android:name="com.android.quickstep.action.BACK_GESTURE_TUTORIAL" />
+                <action android:name="com.android.quickstep.action.GESTURE_SANDBOX" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
diff --git a/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml b/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
index fa3a0f8..4fda2a9 100644
--- a/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
+++ b/quickstep/recents_ui_overrides/res/drawable/hotseat_edu_notification_icon.xml
@@ -15,5 +15,5 @@
 -->
 <vector android:height="24dp" android:viewportHeight="24"
     android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
-    <path android:fillColor="@color/bottom_panel_background" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
+    <path android:fillColor="?android:attr/colorAccent" android:pathData="M19 9l1.25-2.75L23 5l-2.75-1.25L19 1l-1.25 2.75L15 5l2.75 1.25L19 9zm-7.5.5L9 4 6.5 9.5 1 12l5.5 2.5L9 20l2.5-5.5L17 12l-5.5-2.5zM19 15l-1.25 2.75L15 19l2.75 1.25L19 23l1.25-2.75L23 19l-2.75-1.25L19 15z"/>
 </vector>
diff --git a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
index d94c665..fe99037 100644
--- a/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
+++ b/quickstep/recents_ui_overrides/res/layout/predicted_hotseat_edu.xml
@@ -24,13 +24,13 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="32dp"
-        android:backgroundTint="@color/bottom_panel_background"
+        android:backgroundTint="?android:attr/colorAccent"
         android:background="@drawable/bottom_sheet_top_border" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@color/bottom_panel_background"
+        android:background="?android:attr/colorAccent"
         android:orientation="vertical">
 
         <TextView
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
index f82af62..834e6cf 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -296,7 +296,7 @@
                 predictedApp.container = LauncherSettings.Favorites.CONTAINER_PREDICTION;
                 predictedApps.add(predictedApp);
             } else {
-                if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_STUDIO_BUILD) {
                     Log.e(TAG, "Predicted app not found: " + mapper);
                 }
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
index bfbd00e..e7290a4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduController.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityTracker;
+import com.android.launcher3.util.Themes;
 
 import java.util.List;
 
@@ -109,7 +110,7 @@
                 NOTIFICATION_CHANNEL_ID)
                 .setContentTitle(name)
                 .setOngoing(true)
-                .setColor(mLauncher.getColor(R.color.bottom_panel_background))
+                .setColor(Themes.getColorAccent(mLauncher))
                 .setContentIntent(PendingIntent.getActivity(mLauncher, 0, intent,
                         PendingIntent.FLAG_CANCEL_CURRENT))
                 .setSmallIcon(R.drawable.hotseat_edu_notification_icon)
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 4c87945..8926246 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.hybridhotseat;
 
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
+
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -32,6 +35,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.views.AbstractSlideInView;
@@ -95,6 +99,7 @@
         handleClose(true);
         mHotseatEduController.migrate();
         mHotseatEduController.finishOnboarding();
+        logUserAction(true);
         Toast.makeText(mLauncher, R.string.hotseat_items_migrated, Toast.LENGTH_LONG).show();
     }
 
@@ -102,6 +107,7 @@
         if (mHotseatEduController == null) return;
         Toast.makeText(getContext(), R.string.hotseat_no_migration, Toast.LENGTH_LONG).show();
         mHotseatEduController.finishOnboarding();
+        logUserAction(false);
         handleClose(true);
     }
 
@@ -133,7 +139,28 @@
                 mLauncher.getDeviceProfile().hotseatBarSizePx + insets.bottom;
     }
 
+    private void logUserAction(boolean migrated) {
+        LauncherLogProto.Action action = new LauncherLogProto.Action();
+        LauncherLogProto.Target target = new LauncherLogProto.Target();
+        action.type = LauncherLogProto.Action.Type.TOUCH;
+        action.touch = LauncherLogProto.Action.Touch.TAP;
+        target.containerType = LauncherLogProto.ContainerType.TIP;
+        target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+        target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
+                : HYBRID_HOTSEAT_CANCELED;
+        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+        UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+    }
 
+    private void logOnBoardingSeen() {
+        LauncherLogProto.Action action = new LauncherLogProto.Action();
+        LauncherLogProto.Target target = new LauncherLogProto.Target();
+        action.type = LauncherLogProto.Action.Type.TIP;
+        target.containerType = LauncherLogProto.ContainerType.TIP;
+        target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
+        LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+        UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
+    }
     private void animateOpen() {
         if (mIsOpen || mOpenCloseAnimator.isRunning()) {
             return;
@@ -164,7 +191,8 @@
                 || predictions.size() < mLauncher.getDeviceProfile().inv.numHotseatIcons) {
             return;
         }
-        mLauncher.getDragLayer().addView(this);
+        attachToContainer();
+        logOnBoardingSeen();
         animateOpen();
         for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
             WorkspaceItemInfo info = predictions.get(i);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index b2e1798..4bbb48c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -86,6 +86,7 @@
         mIsDrawingDot = true;
         int count = canvas.save();
         canvas.translate(-getWidth() * RING_EFFECT_RATIO, -getHeight() * RING_EFFECT_RATIO);
+        canvas.scale(1 + 2 * RING_EFFECT_RATIO, 1 + 2 * RING_EFFECT_RATIO);
         super.drawDotIfNecessary(canvas);
         canvas.restoreToCount(count);
         mIsDrawingDot = false;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index a1c8378..7895bac 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -37,6 +37,7 @@
 
 import android.graphics.Rect;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -47,6 +48,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.SysUINavigationMode;
@@ -143,6 +145,10 @@
     public void onStateTransitionEnd(Launcher launcher) {
         launcher.getRotationHelper().setCurrentStateRequest(REQUEST_ROTATE);
         DiscoveryBounce.showForOverviewIfNeeded(launcher);
+        RecentsView recentsView = launcher.getOverviewPanel();
+        AccessibilityManagerCompat.sendCustomAccessibilityEvent(
+                recentsView.getPageAt(recentsView.getCurrentPage()),
+                AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
index b80830a..519939e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -172,7 +172,7 @@
         mMotionPauseDetector.setDisallowPause(!handlingOverviewAnim()
                 || upDisplacement < mMotionPauseMinDisplacement
                 || upDisplacement > mMotionPauseMaxDisplacement);
-        mMotionPauseDetector.addPosition(displacement, event.getEventTime());
+        mMotionPauseDetector.addPosition(event);
         return super.onDrag(displacement, event);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 8628db0..799f1ad 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -317,7 +317,7 @@
         // home screen elements will appear in the shelf on motion pause.
         mMotionPauseDetector.setDisallowPause(mIsHomeScreenVisible
                 || -displacement.y < mMotionPauseMinDisplacement);
-        mMotionPauseDetector.addPosition(displacement.y, ev.getEventTime());
+        mMotionPauseDetector.addPosition(ev);
 
         if (mIsHomeScreenVisible) {
             // Cancel the shelf anim so it doesn't clobber mNonOverviewAnim.
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 28fc3da..1b6d291 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -234,6 +234,7 @@
             public void adjustActivityControllerInterpolators() {
                 if (mAdjustInterpolatorsRunnable != null) {
                     mAdjustInterpolatorsRunnable.run();
+                    mAdjustInterpolatorsRunnable = null;
                 }
             }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index 482348f..345a147 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -917,6 +917,12 @@
             windowAnim.addAnimatorListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
+                    if (mRecentsAnimationController == null) {
+                        // If the recents animation is interrupted, we still end the running
+                        // animation (not canceled) so this is still called. In that case, we can
+                        // skip doing any future work here for the current gesture.
+                        return;
+                    }
                     // Finalize the state and notify of the change
                     mGestureState.setState(STATE_END_TARGET_ANIMATION_FINISHED);
                 }
@@ -938,6 +944,12 @@
             windowAnim.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
+                    if (mRecentsAnimationController == null) {
+                        // If the recents animation is interrupted, we still end the running
+                        // animation (not canceled) so this is still called. In that case, we can
+                        // skip doing any future work here for the current gesture.
+                        return;
+                    }
                     if (target == NEW_TASK && mRecentsView != null
                             && mRecentsView.getNextPage() == mRecentsView.getRunningTaskIndex()) {
                         // We are about to launch the current running task, so use LAST_TASK state
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 70a2f9b..28e8fb6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -729,20 +729,15 @@
             }
         } else {
             // Dump everything
+            FeatureFlags.dump(pw);
+            PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
             mDeviceState.dump(pw);
+            mOverviewComponentObserver.dump(pw);
             pw.println("TouchState:");
             boolean resumed = mOverviewComponentObserver != null
                     && mOverviewComponentObserver.getActivityInterface().isResumed();
             pw.println("  resumed=" + resumed);
             pw.println("  mConsumer=" + mConsumer.getName());
-            pw.println("FeatureFlags:");
-            pw.println("  APPLY_CONFIG_AT_RUNTIME=" + APPLY_CONFIG_AT_RUNTIME.get());
-            pw.println("  QUICKSTEP_SPRINGS=" + QUICKSTEP_SPRINGS.get());
-            pw.println("  UNSTABLE_SPRINGS=" + UNSTABLE_SPRINGS.get());
-            pw.println("  ADAPTIVE_ICON_WINDOW_ANIM=" + ADAPTIVE_ICON_WINDOW_ANIM.get());
-            pw.println("  ENABLE_QUICKSTEP_LIVE_TILE=" + ENABLE_QUICKSTEP_LIVE_TILE.get());
-            pw.println("  ENABLE_HINTS_IN_OVERVIEW=" + ENABLE_HINTS_IN_OVERVIEW.get());
-            pw.println("  FAKE_LANDSCAPE_UI=" + FAKE_LANDSCAPE_UI.get());
             ActiveGestureLog.INSTANCE.dump("", pw);
             pw.println("ProtoTrace:");
             pw.println("  file="
@@ -777,7 +772,7 @@
 
     protected boolean shouldNotifyBackGesture() {
         return mBackGestureNotificationCounter > 0 &&
-                mDeviceState.getGestureBlockedActivityPackage() != null;
+                !mDeviceState.getGestureBlockedActivityPackages().isEmpty();
     }
 
     @WorkerThread
@@ -786,8 +781,8 @@
             mBackGestureNotificationCounter--;
             Utilities.getDevicePrefs(this).edit()
                     .putInt(KEY_BACK_NOTIFICATION_COUNT, mBackGestureNotificationCounter).apply();
-            sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(
-                    mDeviceState.getGestureBlockedActivityPackage()));
+            mDeviceState.getGestureBlockedActivityPackages().forEach(blockedPackage ->
+                    sendBroadcast(new Intent(NOTIFY_ACTION_BACK).setPackage(blockedPackage)));
         }
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
index d3765c5..5ad48eb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/AccessibilityInputConsumer.java
@@ -31,8 +31,8 @@
 import com.android.launcher3.R;
 import com.android.quickstep.InputConsumer;
 import com.android.quickstep.RecentsAnimationDeviceState;
-import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
 /**
@@ -117,9 +117,7 @@
                     if (pointerIndex == -1) {
                         break;
                     }
-
-                    mMotionPauseDetector.addPosition(ev.getY(pointerIndex) - mDownY,
-                            ev.getEventTime());
+                    mMotionPauseDetector.addPosition(ev, pointerIndex);
                 }
                 break;
             }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 3ee3c2d..8e7074d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -59,6 +59,7 @@
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.util.NavBarPosition;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputMonitorCompat;
 
@@ -77,6 +78,7 @@
     public static final float QUICKSTEP_TOUCH_SLOP_RATIO = 3;
 
     private final RecentsAnimationDeviceState mDeviceState;
+    private final NavBarPosition mNavBarPosition;
     private final TaskAnimationManager mTaskAnimationManager;
     private final GestureState mGestureState;
     private RecentsAnimationCallbacks mActiveCallbacks;
@@ -126,13 +128,16 @@
             Factory handlerFactory) {
         super(base);
         mDeviceState = deviceState;
+        mNavBarPosition = mDeviceState.getNavBarPosition();
         mTaskAnimationManager = taskAnimationManager;
         mGestureState = gestureState;
         mMainThreadHandler = new Handler(Looper.getMainLooper());
         mHandlerFactory = handlerFactory;
         mActivityInterface = mGestureState.getActivityInterface();
 
-        mMotionPauseDetector = new MotionPauseDetector(base);
+        mMotionPauseDetector = new MotionPauseDetector(base, false,
+                mNavBarPosition.isLeftEdge() || mNavBarPosition.isRightEdge()
+                        ? MotionEvent.AXIS_X : MotionEvent.AXIS_Y);
         mMotionPauseMinDisplacement = base.getResources().getDimension(
                 R.dimen.motion_pause_detector_min_displacement_from_app);
         mOnCompleteCallback = onCompleteCallback;
@@ -172,7 +177,7 @@
         if (mPassedWindowMoveSlop && mInteractionHandler != null
                 && !mRecentsViewDispatcher.hasConsumer()) {
             mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher(
-                    mDeviceState.getNavBarPosition().getRotationMode()));
+                    mNavBarPosition.getRotationMode()));
         }
         int edgeFlags = ev.getEdgeFlags();
         ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
@@ -285,7 +290,7 @@
                     if (mDeviceState.isFullyGesturalNavMode()) {
                         mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
                                 || isLikelyToStartNewTask);
-                        mMotionPauseDetector.addPosition(displacement, ev.getEventTime());
+                        mMotionPauseDetector.addPosition(ev);
                         mInteractionHandler.setIsLikelyToStartNewTask(isLikelyToStartNewTask);
                     }
                 }
@@ -354,9 +359,9 @@
                         ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
                 float velocityX = mVelocityTracker.getXVelocity(mActivePointerId);
                 float velocityY = mVelocityTracker.getYVelocity(mActivePointerId);
-                float velocity = mDeviceState.getNavBarPosition().isRightEdge()
+                float velocity = mNavBarPosition.isRightEdge()
                         ? velocityX
-                        : mDeviceState.getNavBarPosition().isLeftEdge()
+                        : mNavBarPosition.isLeftEdge()
                                 ? -velocityX
                                 : velocityY;
 
@@ -410,9 +415,9 @@
     }
 
     private float getDisplacement(MotionEvent ev) {
-        if (mDeviceState.getNavBarPosition().isRightEdge()) {
+        if (mNavBarPosition.isRightEdge()) {
             return ev.getX() - mDownPos.x;
-        } else if (mDeviceState.getNavBarPosition().isLeftEdge()) {
+        } else if (mNavBarPosition.isLeftEdge()) {
             return mDownPos.x - ev.getX();
         } else {
             return ev.getY() - mDownPos.y;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
index d5ed321..b9827ff 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/ScreenPinnedInputConsumer.java
@@ -23,8 +23,8 @@
 import com.android.launcher3.R;
 import com.android.quickstep.GestureState;
 import com.android.quickstep.InputConsumer;
-import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.util.MotionPauseDetector;
 
 /**
  * An input consumer that detects swipe up and hold to exit screen pinning mode.
@@ -72,7 +72,7 @@
             case MotionEvent.ACTION_MOVE:
                 float displacement = mTouchDownY - y;
                 mMotionPauseDetector.setDisallowPause(displacement < mMotionPauseMinDisplacement);
-                mMotionPauseDetector.addPosition(y, ev.getEventTime());
+                mMotionPauseDetector.addPosition(ev);
                 break;
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 321af6c..fe78a84 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -92,6 +92,7 @@
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PropertyListBuilder;
 import com.android.launcher3.anim.SpringObjectAnimator;
+import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.RotationMode;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -972,6 +973,10 @@
         TaskView runningTask = getRunningTaskView();
         if (runningTask != null) {
             runningTask.setStableAlpha(isHidden ? 0 : mContentAlpha);
+            if (!isHidden) {
+                AccessibilityManagerCompat.sendCustomAccessibilityEvent(runningTask,
+                        AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
+            }
         }
     }
 
@@ -1251,7 +1256,7 @@
     }
 
     public PendingAnimation createAllTasksDismissAnimation(long duration) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
         }
         AnimatorSet anim = new AnimatorSet();
@@ -1595,7 +1600,7 @@
     }
 
     public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mPendingAnimation != null) {
+        if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
         }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
index 49f667e..8ed1392 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -54,6 +54,7 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ConfigurationCompat;
 
 /**
  * A task in the Recents view.
@@ -357,7 +358,8 @@
 
             final float thumbnailScale;
             int thumbnailRotation = mThumbnailData.rotation;
-            int currentRotation = getDisplay() != null ? getDisplay().getRotation() : 0;
+            int currentRotation = ConfigurationCompat.getWindowConfigurationRotation(
+                    getResources().getConfiguration());
             int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
             // Landscape vs portrait change
             boolean windowingModeSupportsRotation = !mActivity.isInMultiWindowMode()
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 327bb14..a688f9a 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -13,11 +13,13 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<resources>
-    <string name="task_overlay_factory_class" translatable="false"></string>
+<resources xmlns:tools="http://schemas.android.com/tools">
+    <string name="task_overlay_factory_class" translatable="false"/>
 
-    <!-- Activity which blocks home gesture -->
-    <string name="gesture_blocking_activity" translatable="false"></string>
+    <!-- Activities which block home gesture -->
+    <string-array name="gesture_blocking_activities" tools:ignore="InconsistentArrays">
+        <item>com.android.launcher3/com.android.quickstep.interaction.GestureSandboxActivity</item>
+    </string-array>
 
     <string name="stats_log_manager_class" translatable="false">com.android.quickstep.logging.StatsLogCompatManager</string>
 
@@ -32,5 +34,5 @@
     <integer name="assistant_gesture_min_time_threshold">200</integer>
     <integer name="assistant_gesture_corner_deg_threshold">20</integer>
 
-    <string name="wellbeing_provider_pkg" translatable="false"></string>
+    <string name="wellbeing_provider_pkg" translatable="false"/>
 </resources>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 5f81e1f..45a62ab 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -67,13 +67,13 @@
     <string  name="back_gesture_tutorial_close_button_content_description" translatable="false">Close</string>
 
     <!-- Hotseat migration notification title -->
-    <string translatable="false" name="hotseat_migrate_prompt_title">Get suggested apps on the home screen</string>
+    <string translatable="false" name="hotseat_migrate_prompt_title">Easily access your most-used apps</string>
     <!-- Hotseat migration notification content -->
-    <string translatable="false" name="hotseat_migrate_prompt_content">Tap to set up</string>
+    <string translatable="false" name="hotseat_migrate_prompt_content">Pixel suggests your favorite apps based on your routines. Tap to learn more.</string>
     <!-- Hotseat migration wizard title -->
     <string translatable="false" name="hotseat_migrate_title">Suggested apps replace the bottom row of apps</string>
     <!-- Hotseat migration wizard message -->
-    <string translatable="false" name="hotseat_migrate_message">To pin a favorite app, drag it over a suggested app. To hide a suggested app, touch &amp; hold it.</string>
+    <string translatable="false" name="hotseat_migrate_message">Your current apps will move to the last screen. To pin or block a suggested app, drag it off the bottom row.</string>
     <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
     <string translatable="false" name="hotseat_items_migrated">Bottom row of apps moved to last screen</string>
     <!-- Toast message user sees after opting into fully predicted hybrid hotseat -->
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 94ff63b..15503b8 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -167,7 +167,7 @@
     @Override
     public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
         if (requestCode != -1) {
-            mPendingActivityRequestCode = -1;
+            mPendingActivityRequestCode = requestCode;
             StartActivityParams params = new StartActivityParams(this, requestCode);
             params.intent = intent;
             params.options = options;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
index 6e7c087..2e422b7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/plugins/PluginManagerWrapper.java
@@ -14,8 +14,12 @@
 
 package com.android.launcher3.uioverrides.plugins;
 
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.plugins.Plugin;
@@ -24,6 +28,9 @@
 import com.android.systemui.shared.plugins.PluginManagerImpl;
 import com.android.systemui.shared.plugins.PluginPrefs;
 
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Set;
 
 public class PluginManagerWrapper {
@@ -75,4 +82,27 @@
     public static boolean hasPlugins(Context context) {
         return PluginPrefs.hasPlugins(context);
     }
+
+    public void dump(PrintWriter pw) {
+        final List<ComponentName> enabledPlugins = new ArrayList<>();
+        final List<ComponentName> disabledPlugins = new ArrayList<>();
+        for (String action : getPluginActions()) {
+            for (ResolveInfo resolveInfo : mContext.getPackageManager().queryIntentServices(
+                    new Intent(action), MATCH_DISABLED_COMPONENTS)) {
+                ComponentName installedPlugin = new ComponentName(
+                        resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
+                if (mPluginEnabler.isEnabled(installedPlugin)) {
+                    enabledPlugins.add(installedPlugin);
+                } else {
+                    disabledPlugins.add(installedPlugin);
+                }
+            }
+        }
+
+        pw.println("PluginManager:");
+        pw.println("  numEnabledPlugins=" + enabledPlugins.size());
+        pw.println("  numDisabledPlugins=" + disabledPlugins.size());
+        pw.println("  enabledPlugins=" + enabledPlugins);
+        pw.println("  disabledPlugins=" + disabledPlugins);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index e88a8a4..b5e05ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -54,6 +54,12 @@
     }
 
     @Override
+    public void onStateDisabled(Launcher launcher) {
+        super.onStateDisabled(launcher);
+        AbstractFloatingView.closeAllOpenViews(launcher);
+    }
+
+    @Override
     public String getDescription(Launcher launcher) {
         AllAppsContainerView appsView = launcher.getAppsView();
         return appsView.getDescription();
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/src/com/android/quickstep/MultiStateCallback.java
index 6c65e01..b3875ae 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/src/com/android/quickstep/MultiStateCallback.java
@@ -130,7 +130,7 @@
             final LinkedList<Runnable> callbacks;
             if (mCallbacks.indexOfKey(stateMask) >= 0) {
                 callbacks = mCallbacks.get(stateMask);
-                if (FeatureFlags.IS_DOGFOOD_BUILD && callbacks.contains(callback)) {
+                if (FeatureFlags.IS_STUDIO_BUILD && callbacks.contains(callback)) {
                     throw new IllegalStateException("Existing callback for state found");
                 }
             } else {
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 73b78db..85ef4c6 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -35,6 +35,7 @@
 
 import com.android.systemui.shared.system.PackageManagerWrapper;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -233,4 +234,13 @@
     public BaseActivityInterface getActivityInterface() {
         return mActivityInterface;
     }
+
+    public void dump(PrintWriter pw) {
+        pw.println("OverviewComponentObserver:");
+        pw.println("  isDefaultHome=" + mIsDefaultHome);
+        pw.println("  isHomeDisabled=" + mIsHomeDisabled);
+        pw.println("  homeAndOverviewSame=" + mIsHomeAndOverviewSame);
+        pw.println("  overviewIntent=" + mOverviewIntent);
+        pw.println("  homeIntent=" + mCurrentHomeIntent);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index acf61b4..0f98b32 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -62,6 +62,12 @@
         mListeners.remove(listener);
     }
 
+    @UiThread
+    public void removeAllListeners() {
+        Preconditions.assertUIThread();
+        mListeners.clear();
+    }
+
     public void notifyAnimationCanceled() {
         mCancelled = true;
         onAnimationCanceled(null);
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 46af8bf..00adfbe 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -201,6 +201,7 @@
         if (mInputConsumerController != null) {
             mInputConsumerController.setInputListener(null);
         }
+        mInputProxySupplier = null;
     }
 
     private boolean onInputConsumerEvent(InputEvent ev) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index d0afb21..abe1592 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -66,6 +66,8 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * Manages the state of the system during a swipe up gesture.
@@ -107,7 +109,7 @@
     private Region mExclusionRegion;
     private SystemGestureExclusionListenerCompat mExclusionListener;
 
-    private ComponentName mGestureBlockedActivity;
+    private final List<ComponentName> mGestureBlockedActivities;
 
     public RecentsAnimationDeviceState(Context context) {
         final ContentResolver resolver = context.getContentResolver();
@@ -142,9 +144,19 @@
         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
 
         // Add any blocked activities
-        String blockingActivity = context.getString(R.string.gesture_blocking_activity);
-        if (!TextUtils.isEmpty(blockingActivity)) {
-            mGestureBlockedActivity = ComponentName.unflattenFromString(blockingActivity);
+        String[] blockingActivities;
+        try {
+            blockingActivities =
+                    context.getResources().getStringArray(R.array.gesture_blocking_activities);
+        } catch (Resources.NotFoundException e) {
+            blockingActivities = new String[0];
+        }
+        mGestureBlockedActivities = new ArrayList<>(blockingActivities.length);
+        for (String blockingActivity : blockingActivities) {
+            if (!TextUtils.isEmpty(blockingActivity)) {
+                mGestureBlockedActivities.add(
+                        ComponentName.unflattenFromString(blockingActivity));
+            }
         }
     }
 
@@ -180,7 +192,7 @@
             mDefaultDisplay.addChangeListener(this);
         }
 
-        if (mMode == NO_BUTTON) {
+        if (newMode == NO_BUTTON) {
             mExclusionListener.register();
         } else {
             mExclusionListener.unregister();
@@ -272,17 +284,16 @@
      * @return whether the given running task info matches the gesture-blocked activity.
      */
     public boolean isGestureBlockedActivity(ActivityManager.RunningTaskInfo runningTaskInfo) {
-        return runningTaskInfo != null && mGestureBlockedActivity != null
-                && mGestureBlockedActivity.equals(runningTaskInfo.topActivity);
+        return runningTaskInfo != null
+                && mGestureBlockedActivities.contains(runningTaskInfo.topActivity);
     }
 
     /**
-     * @return the package of the gesture-blocked activity or {@code null} if there is none.
+     * @return the packages of gesture-blocked activities.
      */
-    public String getGestureBlockedActivityPackage() {
-        return (mGestureBlockedActivity != null)
-                ? mGestureBlockedActivity.getPackageName()
-                : null;
+    public List<String> getGestureBlockedActivityPackages() {
+        return mGestureBlockedActivities.stream().map(ComponentName::getPackageName)
+                .collect(Collectors.toList());
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index e3e8ace..6902e37 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -55,7 +55,7 @@
         // Notify if recents animation is still running
         if (mController != null) {
             String msg = "New recents animation started before old animation completed";
-            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_STUDIO_BUILD) {
                 throw new IllegalArgumentException(msg);
             } else {
                 Log.e("TaskAnimationManager", msg, new Exception());
@@ -156,9 +156,9 @@
             mTargets.release();
         }
 
-        // Remove gesture state from callbacks
-        if (mCallbacks != null && mLastGestureState != null) {
-            mCallbacks.removeListener(mLastGestureState);
+        // Clean up all listeners to ensure we don't get subsequent callbacks
+        if (mCallbacks != null) {
+            mCallbacks.removeAllListeners();
         }
 
         mController = null;
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
similarity index 76%
rename from quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
rename to quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
index 295ab48..8081ad7 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/GestureSandboxActivity.java
@@ -16,7 +16,10 @@
 package com.android.quickstep.interaction;
 
 import android.graphics.Color;
+import android.graphics.Rect;
 import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.view.Display;
 import android.view.View;
 import android.view.Window;
 
@@ -26,10 +29,11 @@
 import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialStep;
 import com.android.quickstep.interaction.BackGestureTutorialFragment.TutorialType;
 
+import java.util.List;
 import java.util.Optional;
 
-/** Shows the Back gesture interactive tutorial in full screen mode. */
-public class BackGestureTutorialActivity extends FragmentActivity {
+/** Shows the gesture interactive sandbox in full screen mode. */
+public class GestureSandboxActivity extends FragmentActivity {
 
     Optional<BackGestureTutorialFragment> mFragment = Optional.empty();
 
@@ -47,6 +51,12 @@
     }
 
     @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        disableSystemGestures();
+    }
+
+    @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
         if (hasFocus) {
@@ -70,4 +80,14 @@
                         | View.SYSTEM_UI_FLAG_FULLSCREEN);
         getWindow().setNavigationBarColor(Color.TRANSPARENT);
     }
+
+    private void disableSystemGestures() {
+        Display display = getDisplay();
+        if (display != null) {
+            DisplayMetrics metrics = new DisplayMetrics();
+            display.getMetrics(metrics);
+            getWindow().setSystemGestureExclusionRects(
+                    List.of(new Rect(0, 0, metrics.widthPixels, metrics.heightPixels)));
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
index 32d0d53..cb04e5b 100644
--- a/quickstep/src/com/android/quickstep/util/BinderTracker.java
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -31,7 +31,7 @@
     private static final String TAG = "BinderTracker";
 
     public static void start() {
-        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (!FeatureFlags.IS_STUDIO_BUILD) {
             Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
             return;
         }
@@ -40,7 +40,7 @@
     }
 
     public static void stop() {
-        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (!FeatureFlags.IS_STUDIO_BUILD) {
             Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index 9cf55b3..1d8a79f 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -84,6 +84,7 @@
         float taskWidth, taskHeight, paddingHorz;
         Resources res = context.getResources();
         Rect insets = dp.getInsets();
+        final boolean overviewActionsEnabled = ENABLE_OVERVIEW_ACTIONS.get();
 
         if (dp.isMultiWindowMode) {
             if (multiWindowStrategy == MULTI_WINDOW_STRATEGY_HALF_SCREEN) {
@@ -113,7 +114,7 @@
             final int paddingResId;
             if (dp.isVerticalBarLayout()) {
                 paddingResId = R.dimen.landscape_task_card_horz_space;
-            } else if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
+            } else if (overviewActionsEnabled && removeShelfFromOverview(context)) {
                 paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
             } else {
                 paddingResId = R.dimen.portrait_task_card_horz_space;
@@ -121,9 +122,11 @@
             paddingHorz = res.getDimension(paddingResId);
         }
 
-        float topIconMargin =   res.getDimension(R.dimen.task_thumbnail_top_margin);
+        float topIconMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
         float bottomMargin = thumbnailBottomMargin(context);
-        float paddingVert = res.getDimension(R.dimen.task_card_vert_space);
+
+        float paddingVert = overviewActionsEnabled && removeShelfFromOverview(context)
+                ? 0 : res.getDimension(R.dimen.task_card_vert_space);
 
         // Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
         // we override the insets ourselves.
@@ -141,7 +144,7 @@
         // Center in the visible space
         float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
         float y = insets.top + Math.max(topIconMargin,
-                (launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
+                (launcherVisibleHeight - extraVerticalSpace - outHeight - bottomMargin) / 2);
         outRect.set(Math.round(x), Math.round(y),
                 Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
     }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 801a560..d8b10b6 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -37,8 +37,7 @@
     private static final long FORCE_PAUSE_TIMEOUT = 300;
 
     /**
-     * After {@link #makePauseHarderToTrigger()}, must
-     * move slowly for this long to trigger a pause.
+     * After {@link #mMakePauseHarderToTrigger}, must move slowly for this long to trigger a pause.
      */
     private static final long HARDER_TRIGGER_TIMEOUT = 400;
 
@@ -49,13 +48,10 @@
     private final Alarm mForcePauseTimeout;
     private final boolean mMakePauseHarderToTrigger;
     private final Context mContext;
+    private final VelocityProvider mVelocityProvider;
 
-    private Long mPreviousTime = null;
-    private Float mPreviousPosition = null;
     private Float mPreviousVelocity = null;
 
-    private Float mFirstPosition = null;
-
     private OnMotionPauseListener mOnMotionPauseListener;
     private boolean mIsPaused;
     // Bias more for the first pause to make it feel extra responsive.
@@ -73,6 +69,13 @@
      * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
      */
     public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger) {
+        this(context, makePauseHarderToTrigger, MotionEvent.AXIS_Y);
+    }
+
+    /**
+     * @param makePauseHarderToTrigger Used for gestures that require a more explicit pause.
+     */
+    public MotionPauseDetector(Context context, boolean makePauseHarderToTrigger, int axis) {
         mContext = context;
         Resources res = context.getResources();
         mSpeedVerySlow = res.getDimension(R.dimen.motion_pause_detector_speed_very_slow);
@@ -82,6 +85,7 @@
         mForcePauseTimeout = new Alarm();
         mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
+        mVelocityProvider = new LinearVelocityProvider(axis);
     }
 
     /**
@@ -101,28 +105,28 @@
 
     /**
      * Computes velocity and acceleration to determine whether the motion is paused.
-     * @param position The x or y component of the motion being tracked.
+     * @param ev The motion being tracked.
      *
      * TODO: Use historical positions as well, e.g. {@link MotionEvent#getHistoricalY(int, int)}.
      */
-    public void addPosition(float position, long time) {
-        if (mFirstPosition == null) {
-            mFirstPosition = position;
-        }
+    public void addPosition(MotionEvent ev) {
+        addPosition(ev, 0);
+    }
+
+    /**
+     * Computes velocity and acceleration to determine whether the motion is paused.
+     * @param ev The motion being tracked.
+     * @param pointerIndex Index for the pointer being tracked in the motion event
+     */
+    public void addPosition(MotionEvent ev, int pointerIndex) {
         mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
                 ? HARDER_TRIGGER_TIMEOUT
                 : FORCE_PAUSE_TIMEOUT);
-        if (mPreviousTime != null && mPreviousPosition != null) {
-            long changeInTime = Math.max(1, time - mPreviousTime);
-            float changeInPosition = position - mPreviousPosition;
-            float velocity = changeInPosition / changeInTime;
-            if (mPreviousVelocity != null) {
-                checkMotionPaused(velocity, mPreviousVelocity, time);
-            }
-            mPreviousVelocity = velocity;
+        Float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
+        if (newVelocity != null && mPreviousVelocity != null) {
+            checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
         }
-        mPreviousTime = time;
-        mPreviousPosition = position;
+        mPreviousVelocity = newVelocity;
     }
 
     private void checkMotionPaused(float velocity, float prevVelocity, long time) {
@@ -178,10 +182,8 @@
     }
 
     public void clear() {
-        mPreviousTime = null;
-        mPreviousPosition = null;
+        mVelocityProvider.clear();
         mPreviousVelocity = null;
-        mFirstPosition = null;
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
         mSlowStartTime = 0;
@@ -195,4 +197,55 @@
     public interface OnMotionPauseListener {
         void onMotionPauseChanged(boolean isPaused);
     }
+
+    /**
+     * Interface to abstract out velocity calculations
+     */
+    protected interface VelocityProvider {
+
+        /**
+         * Adds a new motion events, and returns the velocity at this point, or null if
+         * the velocity is not available
+         */
+        Float addMotionEvent(MotionEvent ev, int pointer);
+
+        /**
+         * Clears all stored motion event records
+         */
+        void clear();
+    }
+
+    private static class LinearVelocityProvider implements VelocityProvider {
+
+        private Long mPreviousTime = null;
+        private Float mPreviousPosition = null;
+
+        private final int mAxis;
+
+        LinearVelocityProvider(int axis) {
+            mAxis = axis;
+        }
+
+        @Override
+        public Float addMotionEvent(MotionEvent ev, int pointer) {
+            long time = ev.getEventTime();
+            float position = ev.getAxisValue(mAxis, pointer);
+            Float velocity = null;
+
+            if (mPreviousTime != null && mPreviousPosition != null) {
+                long changeInTime = Math.max(1, time - mPreviousTime);
+                float changeInPosition = position - mPreviousPosition;
+                velocity = changeInPosition / changeInTime;
+            }
+            mPreviousTime = time;
+            mPreviousPosition = position;
+            return velocity;
+        }
+
+        @Override
+        public void clear() {
+            mPreviousTime = null;
+            mPreviousPosition = null;
+        }
+    }
 }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index d2f5d8f..724af66 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -25,7 +25,6 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
-import android.os.RemoteException;
 
 import androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -48,7 +47,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -84,17 +82,6 @@
     }
 
     @Test
-    @PortraitLandscape
-    @Ignore // Enable after b/131115533
-    public void testPressRecentAppsLauncherAndGetOverview() throws RemoteException {
-        mDevice.pressRecentApps();
-        waitForState("Launcher internal state didn't switch to Overview",
-                () -> LauncherState.OVERVIEW);
-
-        assertNotNull("getOverview() returned null", mLauncher.getOverview());
-    }
-
-    @Test
     @NavigationModeSwitch
     @PortraitLandscape
     public void testWorkspaceSwitchToAllApps() {
@@ -271,10 +258,8 @@
     @Test
     @NavigationModeSwitch
     @PortraitLandscape
-    // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+    // b/143285809 Remove @Stability on 02/27/20 if the test doesn't flake.
     @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
-    // b/143285809
-    @Ignore
     public void testQuickSwitchFromApp() throws Exception {
         startTestActivity(2);
         startTestActivity(3);
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 5607e78..1c18076 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -15,22 +15,22 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="?attr/allAppsScrimColor"
     android:padding="8dp"
     android:orientation="vertical"
-    android:background="?attr/allAppsScrimColor"
     android:gravity="center">
 
     <ImageView
         android:id="@+id/icon"
         android:contentDescription="@string/work_apps_paused_title"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:tint="?attr/folderTextColor"
+        android:layout_width="32dp"
+        android:layout_height="32dp"
+        android:tint="?attr/workProfileOverlayTextColor"
         android:src="@drawable/ic_corp_off" />
 
     <TextView
         style="@style/TextHeadline"
-        android:textColor="?attr/folderTextColor"
+        android:textColor="?attr/workProfileOverlayTextColor"
         android:id="@+id/work_apps_paused_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -38,12 +38,12 @@
         android:layout_marginBottom="8dp"
         android:text="@string/work_apps_paused_title"
         android:textAlignment="center"
-        android:textSize="24sp" />
+        android:textSize="20sp" />
 
     <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textColor="?attr/folderTextColor"
+        android:textColor="?attr/workProfileOverlayTextColor"
         android:text="@string/work_apps_paused_body"
         android:textAlignment="center"
         android:textSize="16sp" />
diff --git a/res/layout/work_profile_edu.xml b/res/layout/work_profile_edu.xml
index f7a529d..04094c4 100644
--- a/res/layout/work_profile_edu.xml
+++ b/res/layout/work_profile_edu.xml
@@ -21,22 +21,22 @@
 
     <View
         android:layout_width="match_parent"
-        android:backgroundTint="@color/bottom_panel_background"
         android:layout_height="32dp"
-        android:background="@drawable/bottom_sheet_top_border" />
+        android:background="@drawable/bottom_sheet_top_border"
+        android:backgroundTint="?android:attr/colorAccent" />
 
     <LinearLayout
         android:id="@+id/view_wrapper"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@color/bottom_panel_background"
+        android:background="?android:attr/colorAccent"
         android:orientation="vertical"
         android:paddingLeft="@dimen/bottom_sheet_edu_padding"
         android:paddingRight="@dimen/bottom_sheet_edu_padding">
 
         <TextView
-            style="@style/TextHeadline"
             android:id="@+id/content_text"
+            style="@style/TextHeadline"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="48dp"
@@ -57,5 +57,4 @@
             android:textAlignment="center"
             android:textColor="@android:color/white" />
     </LinearLayout>
-
 </com.android.launcher3.views.WorkEduView>
\ No newline at end of file
diff --git a/res/layout/work_tab_footer.xml b/res/layout/work_tab_footer.xml
index db95416..2cffedd 100644
--- a/res/layout/work_tab_footer.xml
+++ b/res/layout/work_tab_footer.xml
@@ -32,6 +32,8 @@
         android:layout_weight="1"
         android:drawableStart="@drawable/ic_corp"
         android:drawablePadding="3dp"
+        android:drawableTint="?attr/workProfileOverlayTextColor"
+        android:textColor="?attr/workProfileOverlayTextColor"
         android:layout_height="wrap_content"
         android:ellipsize="end"
         android:gravity="center_vertical"
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index aef878b..5a15ec6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -41,6 +41,7 @@
     <attr name="folderIconBorderColor" format="color" />
     <attr name="folderTextColor" format="color" />
     <attr name="folderHintColor" format="color" />
+    <attr name="workProfileOverlayTextColor" format="color" />
 
     <!-- BubbleTextView specific attributes. -->
     <declare-styleable name="BubbleTextView">
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 36f8468..194ef2c 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -45,5 +45,4 @@
     <color name="back_gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
 
 
-    <color name="bottom_panel_background">#f01A73E8</color>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bde3c31..9d0fb56 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -142,7 +142,7 @@
     <string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
 
     <!-- Default folder title -->
-    <string name="folder_hint_text">Tap to edit</string>
+    <string name="folder_hint_text">Edit Name</string>
 
     <!-- Accessibility -->
     <!-- The format string for when an app is temporarily disabled. -->
@@ -330,7 +330,7 @@
     <!--- User onboarding title for personal apps -->
     <string name="work_profile_edu_personal_apps">Personal apps are private &amp; can\'t be seen by IT</string>
     <!--- User onboarding title for work profile apps -->
-    <string name="work_profile_edu_work_apps">Work apps are badged and monitored by IT</string>
+    <string name="work_profile_edu_work_apps">Work apps are badged &amp; visible to IT</string>
     <!-- Action label to proceed to the next work profile edu section-->
     <string name="work_profile_edu_next">Next</string>
     <!-- Action label to finish work profile edu-->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 1174a2f..cee268b 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -50,6 +50,7 @@
         <item name="folderTextColor">#FF212121</item>
         <item name="folderHintColor">#FF616161</item>
         <item name="loadingIconColor">#CCFFFFFF</item>
+        <item name="workProfileOverlayTextColor">#FF212121</item>
 
         <item name="android:windowTranslucentStatus">false</item>
         <item name="android:windowTranslucentNavigation">false</item>
@@ -77,6 +78,7 @@
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderIconBorderColor">#FF80868B</item>
         <item name="folderTextColor">?attr/workspaceTextColor</item>
+
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
@@ -100,6 +102,7 @@
         <item name="folderHintColor">#FFCCCCCC</item>
         <item name="isMainColorDark">true</item>
         <item name="loadingIconColor">#99FFFFFF</item>
+        <item name="workProfileOverlayTextColor">@android:color/white</item>
     </style>
 
     <style name="LauncherTheme.Dark.DarkMainColor" parent="@style/LauncherTheme.Dark">
diff --git a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
index f769055..74ef55e 100644
--- a/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/folder/FolderNameProviderTest.java
@@ -15,7 +15,7 @@
  */
 package com.android.launcher3.folder;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
 
 import android.content.ComponentName;
 import android.content.Context;
@@ -61,15 +61,16 @@
         ArrayList<WorkspaceItemInfo> list = new ArrayList<>();
         list.add(mItem1);
         list.add(mItem2);
-        String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
-        new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
-        assertTrue(suggestedNameOut[0].equals("Work"));
+        FolderNameInfo[] nameInfos =
+                new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
+        new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+        assertEquals("Work", nameInfos[0].getLabel());
 
-        suggestedNameOut[0] = "candidate1";
-        suggestedNameOut[1] = "candidate2";
-        suggestedNameOut[2] = "candidate3";
-        new FolderNameProvider().getSuggestedFolderName(mContext, list, suggestedNameOut);
-        assertTrue(suggestedNameOut[3].equals("Work"));
+        nameInfos[0] = new FolderNameInfo("candidate1", 0.9);
+        nameInfos[1] = new FolderNameInfo("candidate2", 0.8);
+        nameInfos[2] = new FolderNameInfo("candidate3", 0.7);
+        new FolderNameProvider().getSuggestedFolderName(mContext, list, nameInfos);
+        assertEquals("Work", nameInfos[3].getLabel());
 
     }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
index 48b5a45..95a4146 100644
--- a/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
+++ b/robolectric_tests/src/com/android/launcher3/logging/FileLogTest.java
@@ -49,8 +49,9 @@
     @After
     public void tearDown() {
         // Clear existing logs
-        new File(mTempDir, "log-0").delete();
-        new File(mTempDir, "log-1").delete();
+        for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+            new File(mTempDir, "log-" + i).delete();
+        }
         mTempDir.delete();
 
         mTestActive = false;
@@ -89,8 +90,9 @@
 
         Calendar threeDaysAgo = Calendar.getInstance();
         threeDaysAgo.add(Calendar.HOUR, -72);
-        new File(mTempDir, "log-0").setLastModified(threeDaysAgo.getTimeInMillis());
-        new File(mTempDir, "log-1").setLastModified(threeDaysAgo.getTimeInMillis());
+        for (int i = 0; i < FileLog.LOG_DAYS; i++) {
+            new File(mTempDir, "log-" + i).setLastModified(threeDaysAgo.getTimeInMillis());
+        }
 
         FileLog.print("Testing", "abracadabra", new Exception("cat! cat!"));
         writer = new StringWriter();
diff --git a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
index 50f9494..7072adf 100644
--- a/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/BackupRestoreTest.java
@@ -134,7 +134,7 @@
                 { APP_ICON, SHORTCUT, SHORTCUT, NO__ICON},
             }}, 2, OLD_WORK_PROFILE_ID);
         // simulates the creation of backup upon restore
-        new GridBackupTable(RuntimeEnvironment.application, mDb, mDb, mIdp.numHotseatIcons,
+        new GridBackupTable(RuntimeEnvironment.application, mDb, mIdp.numHotseatIcons,
                 mIdp.numColumns, mIdp.numRows).doBackup(
                         MY_OLD_PROFILE_ID, GridBackupTable.OPTION_REQUIRES_SANITIZATION);
         // reset favorites table
diff --git a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
index 36fd96b..f46b849 100644
--- a/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
+++ b/robolectric_tests/src/com/android/launcher3/model/GridBackupTableTest.java
@@ -63,7 +63,7 @@
 
     @Test
     public void backupTableCreated() {
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 4, 4, 4);
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 4, 4, 4);
         assertFalse(backupTable.backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
@@ -75,14 +75,14 @@
 
     @Test
     public void backupTableRestored() {
-        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         // Delete entries
         mDb.delete(TABLE_NAME, null, null);
         assertEquals(0, queryNumEntries(mDb, TABLE_NAME));
 
-        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, mDb, 3, 3, 3);
+        GridBackupTable backupTable = new GridBackupTable(mContext, mDb, 3, 3, 3);
         assertTrue(backupTable.backupOrRestoreAsNeeded());
 
         // Items have been restored
@@ -96,7 +96,7 @@
 
     @Test
     public void backupTableRemovedOnAdd() {
-        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -107,7 +107,7 @@
 
     @Test
     public void backupTableRemovedOnDelete() {
-        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
@@ -118,7 +118,7 @@
 
     @Test
     public void backupTableRetainedOnUpdate() {
-        assertFalse(new GridBackupTable(mContext, mDb, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
+        assertFalse(new GridBackupTable(mContext, mDb, 4, 4, 4).backupOrRestoreAsNeeded());
         Settings.call(mContext.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
 
         assertTrue(tableExists(mDb, BACKUP_TABLE_NAME));
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index ac61c49..8718820 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -538,7 +538,7 @@
         try {
             dispatchRestoreInstanceState(states);
         } catch (IllegalArgumentException ex) {
-            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_STUDIO_BUILD) {
                 throw ex;
             }
             // Mismatched viewId / viewType preventing restore. Skip restore on production builds.
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 466b7b2..f07040d 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -96,7 +96,7 @@
             }
 
             if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
-                if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_STUDIO_BUILD) {
                     throw new IllegalStateException("Parent of the focused item is not supported.");
                 } else {
                     return false;
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 787eee1..336e423 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -48,6 +48,8 @@
 
     public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
 
+    public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
+
     public int options;
 
     public Intent suggestedFolderNames;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 397a38b..3fc8de2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -482,7 +482,6 @@
     @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
-        mAllAppsController.highlightWorkTabIfNecessary();
         mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
     }
 
@@ -2144,7 +2143,7 @@
                     Object tag = v.getTag();
                     String desc = "Collision while binding workspace item: " + item
                             + ". Collides with " + tag;
-                    if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                    if (FeatureFlags.IS_STUDIO_BUILD) {
                         throw (new RuntimeException(desc));
                     } else {
                         Log.d(TAG, desc);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 4cd038d..d77285d 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -91,7 +91,7 @@
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
-        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (FeatureFlags.IS_STUDIO_BUILD) {
             mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
         }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index bb6c330..f618fe1 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,7 @@
 package com.android.launcher3;
 
 import static com.android.launcher3.LauncherAppState.ACTION_FORCE_ROLOAD;
-import static com.android.launcher3.config.FeatureFlags.IS_DOGFOOD_BUILD;
+import static com.android.launcher3.config.FeatureFlags.IS_STUDIO_BUILD;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
@@ -55,7 +55,6 @@
 import com.android.launcher3.pm.PackageInstallInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutRequest;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ItemInfoMatcher;
 import com.android.launcher3.util.LooperExecutor;
@@ -67,7 +66,6 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 import java.util.function.Supplier;
@@ -128,16 +126,6 @@
     }
 
     /**
-     * Returns AppInfo with corresponding package name.
-     * TODO: move to enqueueModelTask
-     */
-    public Optional<AppInfo> getAppInfoByPackageName(String pkg) {
-        return mBgAllAppsList.data.stream()
-                .filter(info -> info.componentName.getPackageName().equals(pkg))
-                .findAny();
-    }
-
-    /**
      * Adds the provided items to the workspace.
      */
     public void addAndBindAddedWorkspaceItems(List<Pair<ItemInfo, Object>> itemList) {
@@ -250,7 +238,7 @@
                     enqueueModelUpdateTask(new UserLockStateChangedTask(user));
                 }
             }
-        } else if (IS_DOGFOOD_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
+        } else if (IS_STUDIO_BUILD && ACTION_FORCE_ROLOAD.equals(action)) {
             for (Callbacks cb : getCallbacks()) {
                 if (cb instanceof Launcher) {
                     ((Launcher) cb).recreate();
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5544240..697048a 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -119,7 +119,7 @@
 
     @Override
     public boolean onCreate() {
-        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (FeatureFlags.IS_STUDIO_BUILD) {
             Log.d(TAG, "Launcher process started");
         }
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index ae4eae9..01893e9 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -374,6 +374,8 @@
     protected void onPageEndTransition() {
         mWasInOverscroll = false;
         AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
+        AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
+                AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
     }
 
     protected int getUnboundedScrollX() {
@@ -1502,7 +1504,7 @@
             return false;
         }
 
-        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (FeatureFlags.IS_STUDIO_BUILD) {
             duration *= Settings.Global.getFloat(getContext().getContentResolver(),
                     Settings.Global.WINDOW_ANIMATION_SCALE, 1);
         }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index b7f8547..a8f492f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -1858,7 +1858,7 @@
                         CellLayout parentCell = getParentCellLayoutForView(cell);
                         if (parentCell != null) {
                             parentCell.removeView(cell);
-                        } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                        } else if (FeatureFlags.IS_STUDIO_BUILD) {
                             throw new NullPointerException("mDragInfo.cell has null parent");
                         }
                         addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -2153,7 +2153,7 @@
 
         ItemInfo item = d.dragInfo;
         if (item == null) {
-            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_STUDIO_BUILD) {
                 throw new NullPointerException("DragObject has null info");
             }
             return;
@@ -2775,7 +2775,7 @@
                     mDragInfo.container, mDragInfo.screenId);
             if (cellLayout != null) {
                 cellLayout.onDropChild(mDragInfo.cell);
-            } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            } else if (FeatureFlags.IS_STUDIO_BUILD) {
                 throw new RuntimeException("Invalid state: cellLayout == null in "
                         + "Workspace#onDropCompleted. Please file a bug. ");
             }
@@ -2794,7 +2794,7 @@
         CellLayout parentCell = getParentCellLayoutForView(v);
         if (parentCell != null) {
             parentCell.removeView(v);
-        } else if (FeatureFlags.IS_DOGFOOD_BUILD) {
+        } else if (FeatureFlags.IS_STUDIO_BUILD) {
             // When an app is uninstalled using the drop target, we wait until resume to remove
             // the icon. We also remove all the corresponding items from the workspace at
             // {@link Launcher#bindComponentsRemoved}. That call can come before or after
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 56bd1b6..fcccc59 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -103,6 +103,8 @@
 
     private final MultiValueAlpha mMultiValueAlpha;
 
+    Rect mInsets = new Rect();
+
     public AllAppsContainerView(Context context) {
         this(context, null);
     }
@@ -153,6 +155,11 @@
         return mMultiValueAlpha.getProperty(index);
     }
 
+    public WorkFooterContainer getWorkFooterContainer() {
+        return mWorkFooterContainer;
+    }
+
+
     @Override
     protected void setDampedScrollShift(float shift) {
         // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
@@ -320,6 +327,7 @@
 
     @Override
     public void setInsets(Rect insets) {
+        mInsets.set(insets);
         DeviceProfile grid = mLauncher.getDeviceProfile();
         int leftRightPadding = grid.desiredWorkspaceLeftRightMarginPx
                 + grid.cellLayoutPaddingLeftRightPx;
@@ -411,6 +419,7 @@
                 new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                         ViewGroup.LayoutParams.WRAP_CONTENT));
         this.addView(mWorkFooterContainer);
+        mWorkFooterContainer.setInsets(mInsets);
         mWorkFooterContainer.post(() -> mAH[AdapterHolder.WORK].applyPadding());
     }
 
@@ -548,17 +557,6 @@
         return mHeader != null && mHeader.getVisibility() == View.VISIBLE;
     }
 
-    public void onScrollUpEnd() {
-        highlightWorkTabIfNecessary();
-    }
-
-    void highlightWorkTabIfNecessary() {
-        if (mUsingTabs) {
-            ((PersonalWorkSlidingTabStrip) findViewById(R.id.tabs))
-                    .highlightWorkTabIfNecessary();
-        }
-    }
-
     /**
      * Adds an update listener to {@param animator} that adds springs to the animation.
      */
@@ -643,9 +641,9 @@
                         R.layout.work_apps_paused, null);
                 recyclerView.post(() -> {
                     int width = recyclerView.getWidth();
-                    int height = recyclerView.getHeight();
-                    pausedOverlay.measure(makeMeasureSpec(width, EXACTLY),
-                            makeMeasureSpec(height, EXACTLY));
+                    int height = recyclerView.getHeight() -  mWorkFooterContainer.getHeight();
+                    pausedOverlay.measure(makeMeasureSpec(recyclerView.getWidth(), EXACTLY),
+                            makeMeasureSpec(recyclerView.getHeight(), EXACTLY));
                     pausedOverlay.layout(0, 0, width, height);
                     applyPadding();
                 });
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 93bdac9..6aa3efc 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -248,18 +248,6 @@
     private void onProgressAnimationEnd() {
         if (Float.compare(mProgress, 1f) == 0) {
             mAppsView.reset(false /* animate */);
-        } else if (isAllAppsExpanded()) {
-            mAppsView.onScrollUpEnd();
-        }
-    }
-
-    private boolean isAllAppsExpanded() {
-        return Float.compare(mProgress, 0f) == 0;
-    }
-
-    public void highlightWorkTabIfNecessary() {
-        if (isAllAppsExpanded()) {
-            mAppsView.highlightWorkTabIfNecessary();
         }
     }
 }
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index e8035eb..0f0fc3a 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -35,7 +35,6 @@
 import com.android.launcher3.LauncherStateManager.StateListener;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.pm.UserCache;
 
 /**
  * Abstract base class of floating view responsible for showing discovery bounce animation
@@ -144,8 +143,7 @@
 
     private static void showForHomeIfNeeded(Launcher launcher, boolean withDelay) {
         if (!launcher.isInState(NORMAL)
-                || (launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
-                && !shouldShowForWorkProfile(launcher))
+                || launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)
                 || AbstractFloatingView.getTopOpenView(launcher) != null
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
@@ -170,8 +168,7 @@
                 || !launcher.hasBeenResumed()
                 || launcher.isForceInvisible()
                 || launcher.getDeviceProfile().isVerticalBarLayout()
-                || (launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
-                && !shouldShowForWorkProfile(launcher))
+                || launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)
                 || launcher.getSystemService(UserManager.class).isDemoUser()
                 || Utilities.IS_RUNNING_IN_TEST_HARNESS) {
             return;
@@ -213,12 +210,6 @@
         }
     }
 
-    private static boolean shouldShowForWorkProfile(Launcher launcher) {
-        return !launcher.getSharedPrefs().getBoolean(
-                PersonalWorkSlidingTabStrip.KEY_SHOWED_PEEK_WORK_TAB, false)
-                && UserCache.INSTANCE.get(launcher).hasWorkProfile();
-    }
-
     private static void incrementShelfBounceCount(Launcher launcher) {
         SharedPreferences sharedPrefs = launcher.getSharedPrefs();
         int count = sharedPrefs.getInt(SHELF_BOUNCE_COUNT, 0);
diff --git a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
index 9d0ecd3..25db0e7 100644
--- a/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/LauncherAllAppsContainerView.java
@@ -64,6 +64,14 @@
     }
 
     @Override
+    public void setupHeader() {
+        super.setupHeader();
+        if (mWorkTabListener != null && !mUsingTabs) {
+            mLauncher.getStateManager().removeStateListener(mWorkTabListener);
+        }
+    }
+
+    @Override
     public void onTabChanged(int pos) {
         super.onTabChanged(pos);
         if (mUsingTabs) {
diff --git a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
index 6204f31..0e39bbe 100644
--- a/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/allapps/PersonalWorkSlidingTabStrip.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.allapps;
 
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.util.AttributeSet;
@@ -39,11 +38,8 @@
     private static final int POSITION_PERSONAL = 0;
     private static final int POSITION_WORK = 1;
 
-    public static final String KEY_SHOWED_PEEK_WORK_TAB = "showed_peek_work_tab";
-
     private final Paint mSelectedIndicatorPaint;
     private final Paint mDividerPaint;
-    private final SharedPreferences mSharedPreferences;
 
     private int mSelectedIndicatorHeight;
     private int mIndicatorLeft = -1;
@@ -72,7 +68,6 @@
         mDividerPaint.setStrokeWidth(
                 getResources().getDimensionPixelSize(R.dimen.all_apps_divider_height));
 
-        mSharedPreferences = Utilities.getPrefs(context);
         mIsRtl = Utilities.isRtl(getResources());
     }
 
@@ -128,25 +123,6 @@
             mIndicatorRight, getHeight(), mSelectedIndicatorPaint);
     }
 
-    public void highlightWorkTabIfNecessary() {
-        if (mSharedPreferences.getBoolean(KEY_SHOWED_PEEK_WORK_TAB, false)) {
-            return;
-        }
-        if (mLastActivePage != POSITION_PERSONAL) {
-            return;
-        }
-        highlightWorkTab();
-        mSharedPreferences.edit().putBoolean(KEY_SHOWED_PEEK_WORK_TAB, true).apply();
-    }
-
-    private void highlightWorkTab() {
-        View v = getChildAt(POSITION_WORK);
-        v.post(() -> {
-            v.setPressed(true);
-            v.setPressed(false);
-        });
-    }
-
     @Override
     public void setScroll(int currentScroll, int totalScroll) {
         float scrollOffset = ((float) currentScroll) / totalScroll;
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 28579c1..db4bef0 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -18,11 +18,14 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 
@@ -37,11 +40,13 @@
         return isAccessibilityEnabled(context);
     }
 
-    public static void sendCustomAccessibilityEvent(View target, int type, String text) {
+    public static void sendCustomAccessibilityEvent(View target, int type, @Nullable String text) {
         if (isObservedEventType(target.getContext(), type)) {
             AccessibilityEvent event = AccessibilityEvent.obtain(type);
             target.onInitializeAccessibilityEvent(event);
-            event.getText().add(text);
+            if (!TextUtils.isEmpty(text)) {
+                event.getText().add(text);
+            }
             getManager(target.getContext()).sendAccessibilityEvent(event);
         }
     }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index cc33965..6413044 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -22,6 +22,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.DeviceFlag;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,7 +43,10 @@
         return Utilities.IS_DEBUG_DEVICE && Utilities.isDevelopersOptionsEnabled(context);
     }
 
-    public static final boolean IS_DOGFOOD_BUILD = BuildConfig.DEBUG;
+    /**
+     * True when the build has come from Android Studio and is being used for local debugging.
+     */
+    public static final boolean IS_STUDIO_BUILD = BuildConfig.DEBUG;
 
     /**
      * Enable moving the QSB on the 0th screen of the workspace. This is not a configuration feature
@@ -90,13 +94,13 @@
             "FAKE_LANDSCAPE_UI", false, "Rotate launcher UI instead of using transposed layout");
 
     public static final BooleanFlag FOLDER_NAME_SUGGEST = getDebugFlag(
-            "FOLDER_NAME_SUGGEST", false, "Suggests folder names instead of blank text.");
+            "FOLDER_NAME_SUGGEST", true, "Suggests folder names instead of blank text.");
 
     public static final BooleanFlag APP_SEARCH_IMPROVEMENTS = new DeviceFlag(
             "APP_SEARCH_IMPROVEMENTS", false,
             "Adds localized title and keyword search and ranking");
 
-    public static final BooleanFlag ENABLE_PREDICTION_DISMISS = getDebugFlag(
+    public static final BooleanFlag ENABLE_PREDICTION_DISMISS = new DeviceFlag(
             "ENABLE_PREDICTION_DISMISS", false, "Allow option to dimiss apps from predicted list");
 
     public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
@@ -106,7 +110,7 @@
             "ASSISTANT_GIVES_LAUNCHER_FOCUS", false,
             "Allow Launcher to handle nav bar gestures while Assistant is running over it");
 
-    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = getDebugFlag(
+    public static final BooleanFlag ENABLE_HYBRID_HOTSEAT = new DeviceFlag(
             "ENABLE_HYBRID_HOTSEAT", false, "Fill gaps in hotseat with predicted apps");
 
     public static final BooleanFlag ENABLE_DEEP_SHORTCUT_ICON_CACHE = getDebugFlag(
@@ -145,6 +149,15 @@
         }
     }
 
+    public static void dump(PrintWriter pw) {
+        pw.println("FeatureFlags:");
+        synchronized (sDebugFlags) {
+            for (DebugFlag flag : sDebugFlags) {
+                pw.println("  " + flag.key + "=" + flag.get());
+            }
+        }
+    }
+
     public static class BooleanFlag {
 
         public final String key;
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 1b7b015..de7bc6d 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -160,8 +160,8 @@
 
         mOptions = options;
         if (mOptions.systemDndStartPoint != null) {
-            mMotionDownX = mOptions.systemDndStartPoint.x;
-            mMotionDownY = mOptions.systemDndStartPoint.y;
+            mLastTouch[0] = mMotionDownX = mOptions.systemDndStartPoint.x;
+            mLastTouch[1] = mMotionDownY = mOptions.systemDndStartPoint.y;
         }
 
         final int registrationX = mMotionDownX - dragLayerX;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 8e80916..c72509e 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -16,9 +16,23 @@
 
 package com.android.launcher3.folder;
 
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static com.android.launcher3.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -75,20 +89,24 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Action;
+import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.LauncherLogProto.ItemType;
+import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.ClipPathView;
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
 
 /**
  * Represents a set of icons chosen by the user or generated by the system.
@@ -186,6 +204,9 @@
     @Thunk int mScrollHintDir = SCROLL_NONE;
     @Thunk int mCurrentScrollDir = SCROLL_NONE;
 
+    private String mPreviousLabel;
+    private boolean mIsPreviousLabelSuggested;
+
     /**
      * Used to inflate the Workspace from XML.
      *
@@ -300,13 +321,13 @@
     public void startEditingFolderName() {
         post(() -> {
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
-                if (TextUtils.isEmpty(mFolderName.getText())) {
-                    String[] suggestedNames =
-                            mInfo.suggestedFolderNames.getStringArrayExtra("suggest");
-                    mFolderName.setText(suggestedNames[0]);
-                    mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
-                            suggestedNames.length));
-                    mFolderName.setEnteredCompose(false);
+                if (isEmpty(mFolderName.getText())) {
+                    FolderNameInfo[] nameInfos =
+                            (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
+                                    FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
+                    if (nameInfos != null) {
+                        showLabelSuggestion(nameInfos, false);
+                    }
                 }
             }
             mFolderName.setHint("");
@@ -324,23 +345,16 @@
         }
 
         mInfo.title = newTitle;
-        mInfo.setOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
+        mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, mFolderName.isEnteredCompose(),
                 mLauncher.getModelWriter());
         mFolderIcon.onTitleChanged(newTitle);
         mLauncher.getModelWriter().updateItemInDatabase(mInfo);
 
-        if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
-            mFolderName.setText(mInfo.title);
-            // TODO: depending on whether the title was manually edited or automatically
-            // suggested, apply different hint.
-            mFolderName.setHint("");
+        if (TextUtils.isEmpty(mInfo.title)) {
+            mFolderName.setHint(R.string.folder_hint_text);
+            mFolderName.setText("");
         } else {
-            if (TextUtils.isEmpty(mInfo.title)) {
-                mFolderName.setHint(R.string.folder_hint_text);
-                mFolderName.setText("");
-            } else {
-                mFolderName.setHint(null);
-            }
+            mFolderName.setHint(null);
         }
 
         sendCustomAccessibilityEvent(
@@ -423,8 +437,10 @@
         mItemsInvalidated = true;
         mInfo.addListener(this);
 
-        if (!TextUtils.isEmpty(mInfo.title)) {
+        if (!isEmpty(mInfo.title)) {
             mFolderName.setText(mInfo.title);
+            mPreviousLabel = mInfo.title.toString();
+            mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
             mFolderName.setHint(null);
         } else {
             mFolderName.setText("");
@@ -443,27 +459,54 @@
     }
 
     /**
-     * Show suggested folder title.
+     * Show suggested folder title in FolderEditText, push InputMethodManager suggestions and save
+     * the suggestedFolderNames.
      */
-    public void showSuggestedTitle(String[] suggestName) {
+    public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
         if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
-            mInfo.suggestedFolderNames = new Intent().putExtra("suggest", suggestName);
-            if (TextUtils.isEmpty(mFolderName.getText().toString())
-                    && !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
-                if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
-                    mFolderName.setHint("");
-                    mFolderName.setText(suggestName[0]);
-                    mInfo.title = suggestName[0];
-                    animateOpen(mInfo.contents, 0, true);
-                    mFolderName.showKeyboard();
-                    mFolderName.displayCompletions(
-                            Arrays.asList(suggestName).subList(1, suggestName.length));
-                }
+            mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
+                    nameInfos);
+            if (isEmpty(mFolderName.getText().toString())
+                    && !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME)) {
+                showLabelSuggestion(nameInfos, true);
             }
         }
     }
 
     /**
+     * Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
+     * InputMethodManager suggestions.
+     */
+    private void showLabelSuggestion(FolderNameInfo[] nameInfos, boolean animate) {
+        if (nameInfos == null) {
+            return;
+        }
+        // Open the Folder and Keyboard when the first or second suggestion is valid non-empty
+        // string.
+        boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !isEmpty(
+                nameInfos[0].getLabel())
+                || nameInfos.length > 1 && nameInfos[1] != null && !isEmpty(
+                nameInfos[1].getLabel());
+
+        if (shouldOpen) {
+            CharSequence firstLabel = nameInfos[0] == null ? "" : nameInfos[0].getLabel();
+            if (!isEmpty(firstLabel)) {
+                mFolderName.setHint("");
+                mFolderName.setText(firstLabel);
+            }
+            if (animate) {
+                animateOpen(mInfo.contents, 0, true);
+            }
+            mFolderName.showKeyboard();
+            mFolderName.displayCompletions(
+                    asList(nameInfos).subList(1, nameInfos.length).stream()
+                            .filter(Objects::nonNull)
+                            .map(s -> s.getLabel().toString())
+                            .collect(Collectors.toList()));
+        }
+    }
+
+    /**
      * Creates a new UserFolder, inflated from R.layout.user_folder.
      *
      * @param launcher The main activity.
@@ -580,7 +623,7 @@
             dragLayer.addView(this);
             mDragController.addDropTarget(this);
         } else {
-            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+            if (FeatureFlags.IS_STUDIO_BUILD) {
                 Log.e(TAG, "Opening folder (" + this + ") which already has a parent:"
                         + getParent());
             }
@@ -608,9 +651,9 @@
 
                 if (!skipUserEventLog) {
                     mLauncher.getUserEventDispatcher().logActionOnItem(
-                        Touch.TAP,
-                        Direction.NONE,
-                        ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
+                            LauncherLogProto.Action.Touch.TAP,
+                            LauncherLogProto.Action.Direction.NONE,
+                            LauncherLogProto.ItemType.FOLDER_ICON, mInfo.cellX, mInfo.cellY);
                 }
 
 
@@ -1392,6 +1435,7 @@
             if (hasFocus) {
                 startEditingFolderName();
             } else {
+                logEditFolderLabel();
                 mFolderName.dispatchBackKey();
             }
         }
@@ -1405,11 +1449,12 @@
     }
 
     @Override
-    public void fillInLogContainerData(View v, ItemInfo info, Target target, Target targetParent) {
+    public void fillInLogContainerData(View v, ItemInfo info, LauncherLogProto.Target target,
+            LauncherLogProto.Target targetParent) {
         target.gridX = info.cellX;
         target.gridY = info.cellY;
         target.pageIndex = mContent.getCurrentPage();
-        targetParent.containerType = ContainerType.FOLDER;
+        targetParent.containerType = LauncherLogProto.ContainerType.FOLDER;
     }
 
     private class OnScrollHintListener implements OnAlarmListener {
@@ -1507,7 +1552,7 @@
 
     @Override
     public int getLogContainerType() {
-        return ContainerType.FOLDER;
+        return LauncherLogProto.ContainerType.FOLDER;
     }
 
     /**
@@ -1542,7 +1587,7 @@
                     }
                 } else {
                     mLauncher.getUserEventDispatcher().logActionTapOutside(
-                            LoggerUtils.newContainerTarget(ContainerType.FOLDER));
+                            LoggerUtils.newContainerTarget(LauncherLogProto.ContainerType.FOLDER));
                     close(true);
                     return true;
                 }
@@ -1572,4 +1617,113 @@
             super.draw(canvas);
         }
     }
+
+    private void logEditFolderLabel() {
+        LauncherEvent launcherEvent = LauncherEvent.newBuilder()
+                .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
+                .addSrcTarget(newEditTextTargetBuilder()
+                        .setFromFolderLabelState(getFromFolderLabelState())
+                        .setToFolderLabelState(getToFolderLabelState()))
+                .addSrcTarget(newFolderTargetBuilder())
+                .addSrcTarget(newParentContainerTarget())
+                .build();
+        mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
+        mPreviousLabel = mFolderName.getText().toString();
+        mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
+    }
+
+    private Target.FromFolderLabelState getFromFolderLabelState() {
+        return mPreviousLabel == null
+                ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
+                : mPreviousLabel.isEmpty()
+                ? FROM_EMPTY
+                : mIsPreviousLabelSuggested
+                ? FROM_SUGGESTED
+                : FROM_CUSTOM;
+    }
+
+    private Target.ToFolderLabelState getToFolderLabelState() {
+        String newLabel =
+                checkNotNull(mFolderName.getText().toString(),
+                        "Expected valid folder label, but found null");
+
+        Optional<String[]> suggestedLabels = Optional.ofNullable(
+                (FolderNameInfo[]) mInfo.suggestedFolderNames
+                        .getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
+                .map(folderNameInfoArray ->
+                        stream(folderNameInfoArray)
+                                .filter(Objects::nonNull)
+                                .map(FolderNameInfo::getLabel)
+                                .map(CharSequence::toString)
+                                .toArray(String[]::new));
+
+
+        int accepted_suggestion_index = suggestedLabels
+                .map(folderNameInfoArray ->
+                        IntStream.range(0, folderNameInfoArray.length)
+                                .filter(index -> newLabel.equalsIgnoreCase(
+                                        folderNameInfoArray[index]))
+                                .findFirst()
+                                .orElse(-1)
+                ).orElse(-1);
+
+        boolean hasValidPrimary = suggestedLabels
+                .map(labels -> labels.length > 0 && !isEmpty(labels[0]))
+                .orElse(false);
+        String primarySuffix = hasValidPrimary
+                ? "_WITH_VALID_PRIMARY"
+                : "_WITH_EMPTY_PRIMARY";
+
+        boolean isEmptySuggestions = suggestedLabels
+                .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+                .orElse(true);
+        boolean isSuggestionsEnabled = FeatureFlags.FOLDER_NAME_SUGGEST.get();
+        String suggestionsSuffix =  isSuggestionsEnabled
+                ? isEmptySuggestions
+                    ? "_WITH_EMPTY_SUGGESTIONS"
+                    : "_WITH_VALID_SUGGESTIONS"
+                : "_WITH_SUGGESTIONS_DISABLED";
+
+        return newLabel.equals(mPreviousLabel)
+                ? Target.ToFolderLabelState.UNCHANGED
+                : newLabel.isEmpty()
+                    ? Target.ToFolderLabelState.valueOf("TO_EMPTY" + suggestionsSuffix)
+                    : accepted_suggestion_index >= 0
+                        ? Target.ToFolderLabelState.valueOf("TO_SUGGESTION"
+                            + accepted_suggestion_index
+                            + primarySuffix)
+                        : Target.ToFolderLabelState.valueOf("TO_CUSTOM" + suggestionsSuffix);
+    }
+
+
+    private Target.Builder newEditTextTargetBuilder() {
+        return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
+    }
+
+    private Target.Builder newFolderTargetBuilder() {
+        return Target.newBuilder()
+                .setType(Target.Type.CONTAINER)
+                .setContainerType(ContainerType.FOLDER)
+                .setPageIndex(mInfo.screenId)
+                .setGridX(mInfo.cellX)
+                .setGridY(mInfo.cellY)
+                .setCardinality(mInfo.contents.size());
+    }
+
+    private Target.Builder newParentContainerTarget() {
+        Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+
+        switch (mInfo.container) {
+            case CONTAINER_HOTSEAT:
+                return builder.setContainerType(ContainerType.HOTSEAT);
+            case CONTAINER_DESKTOP:
+                return builder.setContainerType(ContainerType.WORKSPACE);
+            default:
+                throw new AssertionError(String
+                        .format("Expected container to be either %s or %s but found %s.",
+                                CONTAINER_HOTSEAT,
+                                CONTAINER_DESKTOP,
+                                mInfo.container));
+        }
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6a47b98..ab1ff10 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -393,15 +393,16 @@
             if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
             final int finalIndex = index;
 
-            String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
+            FolderNameInfo[] nameInfos =
+                    new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
             if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
                 Executors.UI_HELPER_EXECUTOR.post(() -> {
                     d.folderNameProvider.getSuggestedFolderName(
-                            getContext(), mInfo.contents, suggestedNameOut);
-                    showFinalView(finalIndex, item, suggestedNameOut);
+                            getContext(), mInfo.contents, nameInfos);
+                    showFinalView(finalIndex, item, nameInfos);
                 });
             } else {
-                showFinalView(finalIndex, item, suggestedNameOut);
+                showFinalView(finalIndex, item, nameInfos);
             }
         } else {
             addItem(item);
@@ -409,12 +410,12 @@
     }
 
     private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
-            String[] suggestedNameOut) {
+            FolderNameInfo[] nameInfos) {
         postDelayed(() -> {
             mPreviewItemManager.hidePreviewItem(finalIndex, false);
             mFolder.showItem(item);
             invalidate();
-            mFolder.showSuggestedTitle(suggestedNameOut);
+            mFolder.showSuggestedTitle(nameInfos);
         }, DROP_IN_ANIMATION_DURATION);
     }
 
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
new file mode 100644
index 0000000..ecbe46c
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameInfo.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.folder;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Information about a single label suggestions of the Folder.
+ */
+
+public final class FolderNameInfo implements Parcelable {
+    private final double mScore;
+    private final CharSequence mLabel;
+
+    /**
+     * Create a simple completion with label.
+     *
+     * @param label The text that should be inserted into the editor and pushed to
+     *              InputMethodManager suggestions.
+     * @param score The score for the label between 0.0 and 1.0.
+     */
+    public FolderNameInfo(CharSequence label, double score) {
+        mScore = score;
+        mLabel = label;
+    }
+
+    private FolderNameInfo(Parcel source) {
+        mScore = source.readDouble();
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+    }
+
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     *
+     * @param dest  The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeDouble(mScore);
+        TextUtils.writeToParcel(mLabel, dest, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    @NonNull
+    public static final Parcelable.Creator<FolderNameInfo> CREATOR =
+            new Parcelable.Creator<FolderNameInfo>() {
+                public FolderNameInfo createFromParcel(Parcel source) {
+                    return new FolderNameInfo(source);
+                }
+
+                public FolderNameInfo[] newArray(int size) {
+                    return new FolderNameInfo[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    @NonNull
+    public String toString() {
+        return mLabel.toString() + ":" + mScore;
+    }
+}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 957e636..184dbb9 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -21,15 +21,21 @@
 import android.util.Log;
 
 import com.android.launcher3.AppInfo;
+import com.android.launcher3.FolderInfo;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.WorkspaceItemInfo;
+import com.android.launcher3.model.AllAppsList;
+import com.android.launcher3.model.BaseModelUpdateTask;
+import com.android.launcher3.model.BgDataModel;
+import com.android.launcher3.util.IntSparseArrayMap;
 import com.android.launcher3.util.ResourceBasedOverride;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
@@ -49,6 +55,8 @@
      * name edit box can also be used to provide suggestion.
      */
     public static final int SUGGEST_MAX = 4;
+    protected IntSparseArrayMap<FolderInfo> mFolderInfos;
+    protected List<AppInfo> mAppInfos;
 
     /**
      * Retrieve instance of this object that can be overridden in runtime based on the build
@@ -57,78 +65,113 @@
     public static FolderNameProvider newInstance(Context context) {
         FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
                 context.getApplicationContext(), R.string.folder_name_provider_class);
+        fnp.load(context);
+
         return fnp;
     }
 
-    public CharSequence getSuggestedFolderName(Context context,
-            ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
+    public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
+            IntSparseArrayMap<FolderInfo> folderInfos) {
+        FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
+                context.getApplicationContext(), R.string.folder_name_provider_class);
+        fnp.load(appInfos, folderInfos);
+
+        return fnp;
+    }
+
+    private void load(Context context) {
+        LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
+                new FolderNameWorker());
+    }
+
+    private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
+        mAppInfos = appInfos;
+        mFolderInfos = folderInfos;
+    }
+
+    /**
+     * Generate and rank the suggested Folder names.
+     */
+    public void getSuggestedFolderName(Context context,
+            ArrayList<WorkspaceItemInfo> workspaceItemInfos,
+            FolderNameInfo[] nameInfos) {
 
         if (DEBUG) {
-            Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+            Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
         }
         // If all the icons are from work profile,
         // Then, suggest "Work" as the folder name
         List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
-                .filter(distinctByKey(p-> p.user))
+                .filter(distinctByKey(p -> p.user))
                 .collect(Collectors.toList());
 
         if (distinctItemInfos.size() == 1
                 && !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
             // Place it as last viable suggestion
-            setAsLastSuggestion(candidates,
+            setAsLastSuggestion(nameInfos,
                     context.getResources().getString(R.string.work_folder_name));
         }
 
         // If all the icons are from same package (e.g., main icon, shortcut, shortcut)
         // Then, suggest the package's title as the folder name
         distinctItemInfos = workspaceItemInfos.stream()
-                .filter(distinctByKey(p-> p.getTargetComponent() != null
+                .filter(distinctByKey(p -> p.getTargetComponent() != null
                         ? p.getTargetComponent().getPackageName() : ""))
                 .collect(Collectors.toList());
 
         if (distinctItemInfos.size() == 1) {
-            Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
-                    .getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
-                            .getPackageName());
+            Optional<AppInfo> info = getAppInfoByPackageName(
+                    distinctItemInfos.get(0).getTargetComponent().getPackageName());
             // Place it as first viable suggestion and shift everything else
-            info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
+            info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
         }
         if (DEBUG) {
-            Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+            Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
         }
-        return candidates[0];
     }
 
-    private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
-        if (contains(candidatesOut, candidate)) {
+    private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
+        if (mAppInfos == null || mAppInfos.isEmpty()) {
+            return Optional.empty();
+        }
+        return mAppInfos.stream()
+                .filter(info -> info.componentName.getPackageName().equals(packageName))
+                .findAny();
+    }
+
+    private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+        if (nameInfos.length == 0 || contains(nameInfos, label)) {
             return;
         }
-        for (int i = candidatesOut.length - 1; i > 0; i--) {
-            if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
-                candidatesOut[i] = candidatesOut[i - 1];
+        for (int i = nameInfos.length - 1; i > 0; i--) {
+            if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
+                nameInfos[i] = nameInfos[i - 1];
             }
         }
-        candidatesOut[0] = candidate;
+        nameInfos[0] = new FolderNameInfo(label, 1.0);
     }
 
-    private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
-        if (contains(candidatesOut, candidate)) {
+    private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+        if (nameInfos.length == 0 || contains(nameInfos, label)) {
             return;
         }
 
-        for (int i = 0; i < candidate.length(); i++) {
-            if (TextUtils.isEmpty(candidatesOut[i])) {
-                candidatesOut[i] = candidate;
+        for (int i = 0; i < nameInfos.length; i++) {
+            if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
+                nameInfos[i] = new FolderNameInfo(label, 1.0);
                 return;
             }
         }
-        candidatesOut[candidate.length() - 1] = candidate;
+        // Overwrite the last suggestion.
+        int lastIndex = nameInfos.length - 1;
+        nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
     }
 
-    private boolean contains(CharSequence[] list, CharSequence key) {
-        return Arrays.asList(list).stream()
-                .filter(s -> s != null)
-                .anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
+    private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
+        return Arrays.stream(nameInfos)
+                .filter(Objects::nonNull)
+                .anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
+                        label.toString()));
     }
 
     // This method can be moved to some Utility class location.
@@ -136,4 +179,13 @@
         Map<Object, Boolean> map = new ConcurrentHashMap<>();
         return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
     }
+
+    private class FolderNameWorker extends BaseModelUpdateTask {
+        @Override
+        public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
+            mFolderInfos = dataModel.folders.clone();
+            mAppInfos = Arrays.asList(apps.copyData());
+        }
+    }
+
 }
diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java
index f579451..94d30f6 100644
--- a/src/com/android/launcher3/graphics/DragPreviewProvider.java
+++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java
@@ -152,7 +152,7 @@
     }
 
     public final void generateDragOutline(Bitmap preview) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && mOutlineGeneratorCallback != null) {
+        if (FeatureFlags.IS_STUDIO_BUILD && mOutlineGeneratorCallback != null) {
             throw new RuntimeException("Drag outline generated twice");
         }
 
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index def76e8..a429af2 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -25,6 +25,7 @@
 
 import android.annotation.TargetApi;
 import android.app.Fragment;
+import android.appwidget.AppWidgetHostView;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
@@ -55,6 +56,7 @@
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.ItemInfo;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetInfo;
 import com.android.launcher3.LauncherModel;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherSettings.Favorites;
@@ -72,6 +74,8 @@
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.Callbacks;
 import com.android.launcher3.model.LoaderResults;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -248,6 +252,16 @@
             addInScreenFromBind(folderIcon, info);
         }
 
+        private void inflateAndAddWidgets(LauncherAppWidgetInfo info, WidgetsModel widgetsModel) {
+            WidgetItem widgetItem = widgetsModel.getWidgetProviderInfoByProviderName(
+                    info.providerName);
+            AppWidgetHostView view = new AppWidgetHostView(mContext);
+            view.setAppWidget(-1, widgetItem.widgetInfo);
+            view.updateAppWidget(null);
+            view.setTag(info);
+            addInScreenFromBind(view, info);
+        }
+
         private void dispatchVisibilityAggregated(View view, boolean isVisible) {
             // Similar to View.dispatchVisibilityAggregated implementation.
             final boolean thisVisible = view.getVisibility() == VISIBLE;
@@ -272,9 +286,9 @@
                         mContext).getModel();
                 final WorkspaceItemsInfoFetcher fetcher = new WorkspaceItemsInfoFetcher();
                 launcherModel.enqueueModelUpdateTask(fetcher);
-                ArrayList<ItemInfo> workspaceItems;
+                WorkspaceResult workspaceResult;
                 try {
-                    workspaceItems = fetcher.mTask.get(5, TimeUnit.SECONDS);
+                    workspaceResult = fetcher.mTask.get(5, TimeUnit.SECONDS);
                 } catch (InterruptedException | ExecutionException | TimeoutException e) {
                     Log.d(TAG, "Error fetching workspace items info", e);
                     return;
@@ -284,9 +298,14 @@
                 // items
                 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
                 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+                ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+                ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
 
-                filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceItems,
-                        currentWorkspaceItems, otherWorkspaceItems);
+                filterCurrentWorkspaceItems(0 /* currentScreenId */,
+                        workspaceResult.mWorkspaceItems, currentWorkspaceItems,
+                        otherWorkspaceItems);
+                filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
+                        currentAppWidgets, otherAppWidgets);
                 sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
 
                 for (ItemInfo itemInfo : currentWorkspaceItems) {
@@ -303,6 +322,17 @@
                             break;
                     }
                 }
+                for (ItemInfo itemInfo : currentAppWidgets) {
+                    switch (itemInfo.itemType) {
+                        case Favorites.ITEM_TYPE_APPWIDGET:
+                        case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+                            inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+                                    workspaceResult.mWidgetsModel);
+                            break;
+                        default:
+                            break;
+                    }
+                }
             } else {
                 // Add hotseat icons
                 for (int i = 0; i < mIdp.numHotseatIcons; i++) {
@@ -349,10 +379,10 @@
         }
     }
 
-    private static class WorkspaceItemsInfoFetcher implements Callable<ArrayList<ItemInfo>>,
+    private static class WorkspaceItemsInfoFetcher implements Callable<WorkspaceResult>,
             LauncherModel.ModelUpdateTask {
 
-        private final FutureTask<ArrayList<ItemInfo>> mTask = new FutureTask<>(this);
+        private final FutureTask<WorkspaceResult> mTask = new FutureTask<>(this);
 
         private LauncherAppState mApp;
         private LauncherModel mModel;
@@ -374,14 +404,16 @@
         }
 
         @Override
-        public ArrayList<ItemInfo> call() throws Exception {
+        public WorkspaceResult call() throws Exception {
             if (!mModel.isModelLoaded()) {
                 Log.d(TAG, "Workspace not loaded, loading now");
                 mModel.startLoaderForResults(
                         new LoaderResults(mApp, mBgDataModel, mAllAppsList, new Callbacks[0]));
-                return new ArrayList<>();
+                return null;
             }
-            return mBgDataModel.workspaceItems;
+
+            return new WorkspaceResult(mBgDataModel.workspaceItems, mBgDataModel.appWidgets,
+                    mBgDataModel.widgetsModel);
         }
     }
 
@@ -389,4 +421,17 @@
         view.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY));
         view.layout(0, 0, width, height);
     }
+
+    private static class WorkspaceResult {
+        private final ArrayList<ItemInfo> mWorkspaceItems;
+        private final ArrayList<LauncherAppWidgetInfo> mAppWidgets;
+        private final WidgetsModel mWidgetsModel;
+
+        private WorkspaceResult(ArrayList<ItemInfo> workspaceItems,
+                ArrayList<LauncherAppWidgetInfo> appWidgets, WidgetsModel widgetsModel) {
+            mWorkspaceItems = workspaceItems;
+            mAppWidgets = appWidgets;
+            mWidgetsModel = widgetsModel;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java
index 2c972a0..67f07b1 100644
--- a/src/com/android/launcher3/logging/FileLog.java
+++ b/src/com/android/launcher3/logging/FileLog.java
@@ -42,7 +42,7 @@
     private static Handler sHandler = null;
     private static File sLogsDirectory = null;
 
-    private static final int LOG_DAYS = 2;
+    public static final int LOG_DAYS = 4;
 
     public static void setDir(File logsDir) {
         if (ENABLED) {
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 8289da9..199d13f 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -69,8 +69,7 @@
 public class UserEventDispatcher implements ResourceBasedOverride {
 
     private static final String TAG = "UserEvent";
-    private static final boolean IS_VERBOSE =
-            FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isPropertyEnabled(LogConfig.USEREVENT);
+    private static final boolean IS_VERBOSE = Utilities.isPropertyEnabled(LogConfig.USEREVENT);
     private static final String UUID_STORAGE = "uuid";
 
     public static UserEventDispatcher newInstance(Context context,
@@ -372,6 +371,25 @@
         dispatchUserEvent(event, null);
     }
 
+    /**
+     * Logs proto lite version of LauncherEvent object to clearcut.
+     */
+    public void logLauncherEvent(
+                com.android.launcher3.userevent.LauncherLogProto.LauncherEvent launcherEvent) {
+
+        if (mPreviousHomeGesture) {
+            mPreviousHomeGesture = false;
+        }
+        mAppOrTaskLaunch = false;
+        launcherEvent.toBuilder()
+            .setElapsedContainerMillis(SystemClock.uptimeMillis() - mElapsedContainerMillis)
+            .setElapsedSessionMillis(SystemClock.uptimeMillis() - mElapsedSessionMillis).build();
+        if (!IS_VERBOSE) {
+            return;
+        }
+        Log.d(TAG, launcherEvent.toString());
+    }
+
     public void logDeepShortcutsOpen(View icon) {
         LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(icon);
         if (icon == null || !(icon.getTag() instanceof ItemInfo || provider == null)) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index c24b939..32fce0b 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -273,7 +273,7 @@
             switch (item.itemType) {
                 case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                     folders.remove(item.id);
-                    if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                    if (FeatureFlags.IS_STUDIO_BUILD) {
                         for (ItemInfo info : itemsIdMap) {
                             if (info.container == item.id) {
                                 // We are deleting a folder which still contains items that
diff --git a/src/com/android/launcher3/model/GridBackupTable.java b/src/com/android/launcher3/model/GridBackupTable.java
index eb99af5..07a7551 100644
--- a/src/com/android/launcher3/model/GridBackupTable.java
+++ b/src/com/android/launcher3/model/GridBackupTable.java
@@ -33,8 +33,6 @@
 import com.android.launcher3.LauncherSettings.Settings;
 import com.android.launcher3.pm.UserCache;
 
-import java.util.Objects;
-
 /**
  * Helper class to backup and restore Favorites table into a separate table
  * within the same data base.
@@ -63,8 +61,7 @@
     private static final int STATE_SANITIZED = 2;
 
     private final Context mContext;
-    private final SQLiteDatabase mFavoritesDb;
-    private final SQLiteDatabase mBackupDb;
+    private final SQLiteDatabase mDb;
 
     private final int mOldHotseatSize;
     private final int mOldGridX;
@@ -77,11 +74,10 @@
     @IntDef({STATE_NOT_FOUND, STATE_RAW, STATE_SANITIZED})
     private @interface BackupState { }
 
-    public GridBackupTable(Context context, SQLiteDatabase favoritesDb, SQLiteDatabase backupDb,
-            int hotseatSize, int gridX, int gridY) {
+    public GridBackupTable(Context context, SQLiteDatabase db, int hotseatSize, int gridX,
+            int gridY) {
         mContext = context;
-        mFavoritesDb = favoritesDb;
-        mBackupDb = backupDb;
+        mDb = db;
 
         mOldHotseatSize = hotseatSize;
         mOldGridX = gridX;
@@ -94,7 +90,7 @@
      */
     public boolean backupOrRestoreAsNeeded() {
         // Check if backup table exists
-        if (!tableExists(mBackupDb, BACKUP_TABLE_NAME)) {
+        if (!tableExists(mDb, BACKUP_TABLE_NAME)) {
             if (Settings.call(mContext.getContentResolver(), Settings.METHOD_WAS_EMPTY_DB_CREATED)
                     .getBoolean(Settings.EXTRA_VALUE, false)) {
                 // No need to copy if empty DB was created.
@@ -109,7 +105,7 @@
         }
         long userSerial = UserCache.INSTANCE.get(mContext).getSerialNumberForUser(
                 Process.myUserHandle());
-        copyTable(mBackupDb, BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME, userSerial);
+        copyTable(mDb, BACKUP_TABLE_NAME, Favorites.TABLE_NAME, userSerial);
         Log.d(TAG, "Backup table found");
         return true;
     }
@@ -122,37 +118,28 @@
     /**
      * Copy valid grid entries from one table to another.
      */
-    private static void copyTable(SQLiteDatabase fromDb, String fromTable, SQLiteDatabase toDb,
-            String toTable, long userSerial) {
-        dropTable(toDb, toTable);
-        Favorites.addTableToDb(toDb, userSerial, false, toTable);
-        if (fromDb != toDb) {
-            toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
-            toDb.execSQL(
-                    "INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable
-                            + " where _id > " + ID_PROPERTY);
-        } else {
-            toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable + " where _id > "
-                    + ID_PROPERTY);
-        }
+    private static void copyTable(SQLiteDatabase db, String from, String to, long userSerial) {
+        dropTable(db, to);
+        Favorites.addTableToDb(db, userSerial, false, to);
+        db.execSQL("INSERT INTO " + to + " SELECT * FROM " + from + " where _id > " + ID_PROPERTY);
     }
 
     private void encodeDBProperties(int options) {
         ContentValues values = new ContentValues();
         values.put(Favorites._ID, ID_PROPERTY);
-        values.put(KEY_DB_VERSION, mFavoritesDb.getVersion());
+        values.put(KEY_DB_VERSION, mDb.getVersion());
         values.put(KEY_GRID_X_SIZE, mOldGridX);
         values.put(KEY_GRID_Y_SIZE, mOldGridY);
         values.put(KEY_HOTSEAT_SIZE, mOldHotseatSize);
         values.put(Favorites.OPTIONS, options);
-        mBackupDb.insert(BACKUP_TABLE_NAME, null, values);
+        mDb.insert(BACKUP_TABLE_NAME, null, values);
     }
 
     /**
      * Load DB properties from grid backup table.
      */
     public @BackupState int loadDBProperties() {
-        try (Cursor c = mBackupDb.query(BACKUP_TABLE_NAME, new String[] {
+        try (Cursor c = mDb.query(BACKUP_TABLE_NAME, new String[] {
                 KEY_DB_VERSION,     // 0
                 KEY_GRID_X_SIZE,    // 1
                 KEY_GRID_Y_SIZE,    // 2
@@ -163,7 +150,7 @@
                 Log.e(TAG, "Meta data not found in backup table");
                 return STATE_NOT_FOUND;
             }
-            if (!validateDBVersion(mBackupDb.getVersion(), c.getInt(0))) {
+            if (!validateDBVersion(mDb.getVersion(), c.getInt(0))) {
                 return STATE_NOT_FOUND;
             }
 
@@ -179,7 +166,7 @@
      * Restore workspace from raw backup if available.
      */
     public boolean restoreFromRawBackupIfAvailable(long oldProfileId) {
-        if (!tableExists(mBackupDb, Favorites.BACKUP_TABLE_NAME)
+        if (!tableExists(mDb, Favorites.BACKUP_TABLE_NAME)
                 || loadDBProperties() != STATE_RAW
                 || mOldHotseatSize != mRestoredHotseatSize
                 || mOldGridX != mRestoredGridX
@@ -187,8 +174,7 @@
             // skip restore if dimensions in backup table differs from current setup.
             return false;
         }
-        copyTable(mBackupDb, Favorites.BACKUP_TABLE_NAME, mFavoritesDb, Favorites.TABLE_NAME,
-                oldProfileId);
+        copyTable(mDb, Favorites.BACKUP_TABLE_NAME, Favorites.TABLE_NAME, oldProfileId);
         Log.d(TAG, "Backup restored");
         return true;
     }
@@ -197,8 +183,7 @@
      * Performs a backup on the workspace layout.
      */
     public void doBackup(long profileId, int options) {
-        copyTable(mFavoritesDb, Favorites.TABLE_NAME, mBackupDb, Favorites.BACKUP_TABLE_NAME,
-                profileId);
+        copyTable(mDb, Favorites.TABLE_NAME, Favorites.BACKUP_TABLE_NAME, profileId);
         encodeDBProperties(options);
     }
 
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
index 4f92066..c35c4b9 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTask.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -909,7 +909,7 @@
             boolean dbChanged = false;
 
             GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
-                    transaction.getDb(), srcHotseatCount, sourceSize.x, sourceSize.y);
+                    srcHotseatCount, sourceSize.x, sourceSize.y);
             if (backupTable.backupOrRestoreAsNeeded()) {
                 dbChanged = true;
                 srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4f61a2a..6223a23 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -59,6 +59,7 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderGridOrganizer;
+import com.android.launcher3.folder.FolderNameInfo;
 import com.android.launcher3.folder.FolderNameProvider;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon;
 import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
@@ -900,15 +901,18 @@
     }
 
     private void loadFolderNames() {
-        FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext());
+        FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+                mBgAllAppsList.data, mBgDataModel.folders);
 
         synchronized (mBgDataModel) {
             for (int i = 0; i < mBgDataModel.folders.size(); i++) {
-                String[] suggestedOut = new String[FolderNameProvider.SUGGEST_MAX];
+                FolderNameInfo[] suggestionInfos =
+                        new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
                 FolderInfo info = mBgDataModel.folders.valueAt(i);
                 if (info.suggestedFolderNames == null) {
-                    provider.getSuggestedFolderName(mApp.getContext(), info.contents, suggestedOut);
-                    info.suggestedFolderNames = new Intent().putExtra("suggest", suggestedOut);
+                    provider.getSuggestedFolderName(mApp.getContext(), info.contents,
+                            suggestionInfos);
+                    info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
                 }
             }
         }
diff --git a/src/com/android/launcher3/model/ModelUtils.java b/src/com/android/launcher3/model/ModelUtils.java
index 628dd95..1473124 100644
--- a/src/com/android/launcher3/model/ModelUtils.java
+++ b/src/com/android/launcher3/model/ModelUtils.java
@@ -97,7 +97,7 @@
                         return Integer.compare(lhs.screenId, rhs.screenId);
                     }
                     default:
-                        if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                        if (FeatureFlags.IS_STUDIO_BUILD) {
                             throw new RuntimeException(
                                     "Unexpected container type when sorting workspace items.");
                         }
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
index ccd1554..27fa580 100644
--- a/src/com/android/launcher3/model/ModelWriter.java
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -115,8 +115,9 @@
         ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
         if (modelItem != null && item != modelItem) {
             // check all the data is consistent
-            if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_DOGFOOD_BUILD &&
-                    modelItem instanceof WorkspaceItemInfo && item instanceof WorkspaceItemInfo) {
+            if (!Utilities.IS_DEBUG_DEVICE && !FeatureFlags.IS_STUDIO_BUILD
+                    && modelItem instanceof WorkspaceItemInfo
+                    && item instanceof WorkspaceItemInfo) {
                 if (modelItem.title.toString().equals(item.title.toString()) &&
                         modelItem.getIntent().filterEquals(item.getIntent()) &&
                         modelItem.id == item.id &&
@@ -320,7 +321,7 @@
      */
     public void prepareToUndoDelete() {
         if (!mPreparingToUndo) {
-            if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_DOGFOOD_BUILD) {
+            if (!mDeleteRunnables.isEmpty() && FeatureFlags.IS_STUDIO_BUILD) {
                 throw new IllegalStateException("There are still uncommitted delete operations!");
             }
             mDeleteRunnables.clear();
diff --git a/src/com/android/launcher3/model/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
index 3ef48cd..2fc064c 100644
--- a/src/com/android/launcher3/model/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -19,6 +19,8 @@
 import com.android.launcher3.ItemInfoWithIcon;
 import com.android.launcher3.LauncherSettings;
 
+import java.util.Objects;
+
 /**
  * Represents a {@link Package} in the widget tray section.
  */
@@ -48,4 +50,17 @@
     public PackageItemInfo clone() {
         return new PackageItemInfo(this);
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PackageItemInfo that = (PackageItemInfo) o;
+        return Objects.equals(packageName, that.packageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(packageName);
+    }
 }
diff --git a/src/com/android/launcher3/provider/RestoreDbTask.java b/src/com/android/launcher3/provider/RestoreDbTask.java
index 842be40..0fe3673 100644
--- a/src/com/android/launcher3/provider/RestoreDbTask.java
+++ b/src/com/android/launcher3/provider/RestoreDbTask.java
@@ -108,7 +108,7 @@
     private void backupWorkspace(Context context, SQLiteDatabase db) throws Exception {
         InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
         // TODO(pinyaoting): Support backing up workspace with multiple grid options.
-        new GridBackupTable(context, db, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
+        new GridBackupTable(context, db, idp.numHotseatIcons, idp.numColumns, idp.numRows)
                 .doBackup(getDefaultProfileId(db), GridBackupTable.OPTION_REQUIRES_SANITIZATION);
     }
 
@@ -117,8 +117,8 @@
             throws Exception {
         // TODO(pinyaoting): Support restoring workspace with multiple grid options.
         final InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
-        GridBackupTable backupTable = new GridBackupTable(context, db, db,
-                idp.numHotseatIcons, idp.numColumns, idp.numRows);
+        GridBackupTable backupTable = new GridBackupTable(context, db, idp.numHotseatIcons,
+                idp.numColumns, idp.numRows);
         if (backupTable.restoreFromRawBackupIfAvailable(getDefaultProfileId(db))) {
             sanitizeDB(helper, db, backupManager);
             LauncherAppState.getInstance(context).getModel().forceReload();
diff --git a/src/com/android/launcher3/util/IOUtils.java b/src/com/android/launcher3/util/IOUtils.java
index 4a4a5ca..fcb96d7 100644
--- a/src/com/android/launcher3/util/IOUtils.java
+++ b/src/com/android/launcher3/util/IOUtils.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.util.Log;
 
-import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 
 import java.io.ByteArrayOutputStream;
@@ -67,7 +66,7 @@
      * Utility method to debug binary data
      */
     public static String createTempFile(Context context, byte[] data) {
-        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+        if (!FeatureFlags.IS_STUDIO_BUILD) {
             throw new IllegalStateException("Method only allowed in development mode");
         }
 
@@ -87,7 +86,7 @@
             try {
                 c.close();
             } catch (IOException e) {
-                if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                if (FeatureFlags.IS_STUDIO_BUILD) {
                     Log.d(TAG, "Error closing", e);
                 }
             }
diff --git a/src/com/android/launcher3/util/Preconditions.java b/src/com/android/launcher3/util/Preconditions.java
index ed66422..63d56e4 100644
--- a/src/com/android/launcher3/util/Preconditions.java
+++ b/src/com/android/launcher3/util/Preconditions.java
@@ -28,25 +28,25 @@
 public class Preconditions {
 
     public static void assertNotNull(Object o) {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && o == null) {
+        if (FeatureFlags.IS_STUDIO_BUILD && o == null) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertWorkerThread() {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
+        if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(MODEL_EXECUTOR.getLooper())) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertUIThread() {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && !isSameLooper(Looper.getMainLooper())) {
+        if (FeatureFlags.IS_STUDIO_BUILD && !isSameLooper(Looper.getMainLooper())) {
             throw new IllegalStateException();
         }
     }
 
     public static void assertNonUiThread() {
-        if (FeatureFlags.IS_DOGFOOD_BUILD && isSameLooper(Looper.getMainLooper())) {
+        if (FeatureFlags.IS_STUDIO_BUILD && isSameLooper(Looper.getMainLooper())) {
             throw new IllegalStateException();
         }
     }
diff --git a/src/com/android/launcher3/views/AbstractSlideInView.java b/src/com/android/launcher3/views/AbstractSlideInView.java
index 9334c46..bdba39c 100644
--- a/src/com/android/launcher3/views/AbstractSlideInView.java
+++ b/src/com/android/launcher3/views/AbstractSlideInView.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.views;
 
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 
 import android.animation.Animator;
@@ -62,6 +64,7 @@
     protected final ObjectAnimator mOpenCloseAnimator;
 
     protected View mContent;
+    private final View mColorScrim;
     protected Interpolator mScrollInterpolator;
 
     // range [0, 1], 0=> completely open, 1=> completely closed
@@ -85,11 +88,30 @@
                 announceAccessibilityChanges();
             }
         });
+        int scrimColor = getScrimColor(mLauncher);
+        mColorScrim = scrimColor != -1 ? createColorScrim(mLauncher, scrimColor) : null;
+    }
+
+    protected void attachToContainer() {
+        if (mColorScrim != null) {
+            getPopupContainer().addView(mColorScrim);
+        }
+        getPopupContainer().addView(this);
+    }
+
+    /**
+     * Returns a scrim color for a sliding view. if returned value is -1, no scrim is added.
+     */
+    protected int getScrimColor(Context context) {
+        return -1;
     }
 
     protected void setTranslationShift(float translationShift) {
         mTranslationShift = translationShift;
         mContent.setTranslationY(mTranslationShift * mContent.getHeight());
+        if (mColorScrim != null) {
+            mColorScrim.setAlpha(1 - mTranslationShift);
+        }
     }
 
     @Override
@@ -127,7 +149,8 @@
     /* SingleAxisSwipeDetector.Listener */
 
     @Override
-    public void onDragStart(boolean start) { }
+    public void onDragStart(boolean start) {
+    }
 
     @Override
     public boolean onDrag(float displacement) {
@@ -185,9 +208,25 @@
     protected void onCloseComplete() {
         mIsOpen = false;
         getPopupContainer().removeView(this);
+        if (mColorScrim != null) {
+            getPopupContainer().removeView(mColorScrim);
+        }
     }
 
     protected BaseDragLayer getPopupContainer() {
         return mLauncher.getDragLayer();
     }
+
+
+    protected static View createColorScrim(Context context, int bgColor) {
+        View view = new View(context);
+        view.forceHasOverlappingRendering(false);
+        view.setBackgroundColor(bgColor);
+
+        BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
+        lp.ignoreInsets = true;
+        view.setLayoutParams(lp);
+
+        return view;
+    }
 }
diff --git a/src/com/android/launcher3/views/WorkEduView.java b/src/com/android/launcher3/views/WorkEduView.java
index b6c81ae..81f8327 100644
--- a/src/com/android/launcher3/views/WorkEduView.java
+++ b/src/com/android/launcher3/views/WorkEduView.java
@@ -52,11 +52,15 @@
     private static final int WORK_EDU_PERSONAL_APPS = 1;
     private static final int WORK_EDU_WORK_APPS = 2;
 
+    protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
+
+
     private Rect mInsets = new Rect();
     private View mViewWrapper;
     private Button mProceedButton;
     private TextView mContentText;
     private AllAppsPagedView mAllAppsPagedView;
+
     private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
 
 
@@ -141,10 +145,15 @@
     }
 
     private void show() {
-        mLauncher.getDragLayer().addView(this);
+        attachToContainer();
         animateOpen();
     }
 
+    @Override
+    protected int getScrimColor(Context context) {
+        return FINAL_SCRIM_BG_COLOR;
+    }
+
     private void goToFirstPage() {
         if (mAllAppsPagedView != null) {
             mAllAppsPagedView.snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
diff --git a/src/com/android/launcher3/views/WorkFooterContainer.java b/src/com/android/launcher3/views/WorkFooterContainer.java
index f8add9a..9ac8230 100644
--- a/src/com/android/launcher3/views/WorkFooterContainer.java
+++ b/src/com/android/launcher3/views/WorkFooterContainer.java
@@ -119,6 +119,13 @@
         mWorkModeSwitch.refresh();
     }
 
+    /**
+     * Returns work mode switch
+     */
+    public WorkModeSwitch getWorkModeSwitch() {
+        return mWorkModeSwitch;
+    }
+
     private boolean shouldShowWorkFooter() {
         Launcher launcher = Launcher.getLauncher(getContext());
         return Utilities.ATLEAST_P && (hasShortcutsPermission(launcher)
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 6cae43d..df1a469 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.widget;
 
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
 import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 
@@ -42,7 +40,6 @@
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.AbstractSlideInView;
-import com.android.launcher3.views.BaseDragLayer;
 
 /**
  * Base class for various widgets popup
@@ -55,11 +52,14 @@
     /* Touch handling related member variables. */
     private Toast mWidgetInstructionToast;
 
-    protected final View mColorScrim;
-
     public BaseWidgetSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
-        mColorScrim = createColorScrim(context);
+    }
+
+    protected int getScrimColor(Context context) {
+        WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
+        int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
+        return setColorAlphaBound(colors.getSecondaryColor(), alpha);
     }
 
     @Override
@@ -98,16 +98,6 @@
         return true;
     }
 
-    protected void attachToContainer() {
-        getPopupContainer().addView(mColorScrim);
-        getPopupContainer().addView(this);
-    }
-
-    protected void setTranslationShift(float translationShift) {
-        super.setTranslationShift(translationShift);
-        mColorScrim.setAlpha(1 - mTranslationShift);
-    }
-
     private boolean beginDraggingWidget(WidgetCell v) {
         // Get the widget preview as the drag representation
         WidgetImageView image = v.getWidgetView();
@@ -138,7 +128,6 @@
 
     protected void onCloseComplete() {
         super.onCloseComplete();
-        getPopupContainer().removeView(mColorScrim);
         clearNavBarColor();
     }
 
@@ -177,19 +166,4 @@
     protected SystemUiController getSystemUiController() {
         return mLauncher.getSystemUiController();
     }
-
-    private static View createColorScrim(Context context) {
-        View view = new View(context);
-        view.forceHasOverlappingRendering(false);
-
-        WallpaperColorInfo colors = WallpaperColorInfo.INSTANCE.get(context);
-        int alpha = context.getResources().getInteger(R.integer.extracted_color_gradient_alpha);
-        view.setBackgroundColor(setColorAlphaBound(colors.getSecondaryColor(), alpha));
-
-        BaseDragLayer.LayoutParams lp = new BaseDragLayer.LayoutParams(MATCH_PARENT, MATCH_PARENT);
-        lp.ignoreInsets = true;
-        view.setLayoutParams(lp);
-
-        return view;
-    }
 }
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 5ebf8d3..ae32692 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -6,6 +6,7 @@
 import static com.android.launcher3.pm.ShortcutConfigActivityInfo.queryList;
 
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Process;
@@ -115,7 +116,7 @@
             }
             setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
         } catch (Exception e) {
-            if (!FeatureFlags.IS_DOGFOOD_BUILD && Utilities.isBinderSizeError(e)) {
+            if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
                 // the returned value may be incomplete and will not be refreshed until the next
                 // time Launcher starts.
                 // TODO: after figuring out a repro step, introduce a dirty bit to check when
@@ -243,4 +244,16 @@
             }
         }
     }
+
+    public WidgetItem getWidgetProviderInfoByProviderName(
+            ComponentName providerName) {
+        ArrayList<WidgetItem> widgetsList = mWidgetsList.get(
+                new PackageItemInfo(providerName.getPackageName()));
+        for (WidgetItem item : widgetsList) {
+            if (item.componentName.equals(providerName)) {
+                return item;
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/tests/Android.mk b/tests/Android.mk
index d1a6c06..a9fff8e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -51,7 +51,7 @@
     androidx.test.rules \
     androidx.test.uiautomator_uiautomator \
     mockito-target-minus-junit4 \
-    launcher-log-protos-lite
+    launcher_log_protos_lite
 
 ifneq (,$(wildcard frameworks/base))
     LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index d9edc35..810c91a 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -16,17 +16,32 @@
 package com.android.launcher3.ui;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.UNBUNDLED_POSTSUBMIT;
 
+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 androidx.test.filters.LargeTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.AllAppsPagedView;
+import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.launcher3.views.WorkFooterContainer;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.List;
+import java.util.Objects;
+
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 public class WorkTabTest extends AbstractLauncherUiTest {
@@ -52,16 +67,49 @@
     }
 
     @Test
+    // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+    @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
     public void workTabExists() {
         mDevice.pressHome();
-        waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+        waitForLauncherCondition("Launcher didn't start", Objects::nonNull);
         executeOnLauncher(launcher -> launcher.getStateManager().goToState(ALL_APPS));
-
-        /*
-        assertTrue("Personal tab is missing", waitForLauncherCondition(
-                launcher -> launcher.getAppsView().isPersonalTabVisible()));
-        assertTrue("Work tab is missing", waitForLauncherCondition(
-                launcher -> launcher.getAppsView().isWorkTabVisible()));
-        */
+        waitForLauncherCondition("Personal tab is missing",
+                launcher -> launcher.getAppsView().isPersonalTabVisible(), 60000);
+        waitForLauncherCondition("Work tab is missing",
+                launcher -> launcher.getAppsView().isWorkTabVisible(), 60000);
     }
+
+    @Test
+    // b/143285809 Remove @Stability on 02/21/20 if the test doesn't flake.
+    @TestStabilityRule.Stability(flavors = LOCAL | UNBUNDLED_POSTSUBMIT)
+    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().getWorkFooterContainer());
+
+        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 -> {
+            WorkFooterContainer wf = launcher.getAppsView().getWorkFooterContainer();
+            ((AllAppsPagedView) launcher.getAppsView().getContentView()).snapToPageImmediately(
+                    AllAppsContainerView.AdapterHolder.WORK);
+            wf.getWorkModeSwitch().toggle();
+        });
+        waitForLauncherCondition("Work toggle did not work",
+                l -> l.getSystemService(UserManager.class).isQuietModeEnabled(workProfile));
+    }
+
 }
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 4b867a7..cdda0f0 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -8,7 +8,6 @@
 
 import org.junit.rules.TestWatcher;
 import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -16,7 +15,6 @@
 
 public class FailureWatcher extends TestWatcher {
     private static final String TAG = "FailureWatcher";
-    private static boolean sHadFailedTestDeinitialization;
     final private UiDevice mDevice;
 
     public FailureWatcher(UiDevice device) {
@@ -62,35 +60,4 @@
 
         device.takeScreenshot(new File(pathname));
     }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        return new Statement() {
-
-            @Override
-            public void evaluate() throws Throwable {
-                if (sHadFailedTestDeinitialization) {
-                    Log.d(TAG, "Skipping due to a recent test deinitialization failure: " +
-                            description.getDisplayName());
-                    return;
-                }
-
-                try {
-                    FailureWatcher.super.apply(base, description).evaluate();
-                } catch (Throwable e) {
-                    final String stackTrace = Log.getStackTraceString(e);
-                    if (!stackTrace.contains(
-                            "androidx.test.internal.runner.junit4.statement.RunBefores.evaluate")) {
-                        // Test failed to deinitialize. Since the global state is probably
-                        // corrupted, won't execute other tests.
-                        Log.d(TAG,
-                                "Detected an exception from test finalizer, will skip further "
-                                        + "tests: " + stackTrace);
-                        sHadFailedTestDeinitialization = true;
-                    }
-                    throw e;
-                }
-            }
-        };
-    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index abd0f24..c5fbd7c 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1251,13 +1251,14 @@
     private Map<String, List<String>> getEvents() {
         final Map<String, List<String>> events = new HashMap<>();
         try {
-            // Logcat may skip events after the specified time. Querying for events starting 1 sec
+            // Logcat may skip events after the specified time. Querying for events starting 1 min
             // earlier.
-            final Date startTime = new Date(mStartRecordingTime.getTime() - 10000);
-            final String logcatEvents = mDevice.executeShellCommand(
-                    "logcat -d -v year --pid=" + getPid() + " -t "
-                            + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
-                            + " -s " + TestProtocol.TAPL_EVENTS_TAG);
+            final Date startTime = new Date(mStartRecordingTime.getTime() - 60000);
+            final String logcatCommand = "logcat -d -v year --pid=" + getPid() + " -t "
+                    + DATE_TIME_FORMAT.format(startTime).replaceAll(" ", "")
+                    + " -s " + TestProtocol.TAPL_EVENTS_TAG;
+            log("Events query command: " + logcatCommand);
+            final String logcatEvents = mDevice.executeShellCommand(logcatCommand);
             final Matcher matcher = EVENT_LOG_ENTRY.matcher(logcatEvents);
             while (matcher.find()) {
                 // Skip events before recording start time.