Merge "Remove ConcurrentModificationException for snapshot logging Bug: 159282737" into ub-launcher3-rvc-dev
diff --git a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
index 7b3e378..cd64a94 100644
--- a/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
+++ b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
@@ -13,25 +13,30 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.fallback.RecentsRootView
+<com.android.launcher3.LauncherRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/drag_layer"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:clipChildren="false"
     android:fitsSystemWindows="true">
 
-    <com.android.quickstep.fallback.FallbackRecentsView
-        android:id="@id/overview_panel"
+    <com.android.quickstep.fallback.RecentsDragLayer
+        android:id="@+id/drag_layer"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:clipChildren="false"
-        android:clipToPadding="false"
-        android:outlineProvider="none"
-        android:theme="@style/HomeScreenElementTheme" />
+        android:clipChildren="false">
 
-    <include
-        android:id="@+id/overview_actions_view"
-        layout="@layout/overview_actions_container" />
+        <com.android.quickstep.fallback.FallbackRecentsView
+            android:id="@id/overview_panel"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:clipChildren="false"
+            android:clipToPadding="false"
+            android:outlineProvider="none"
+            android:theme="@style/HomeScreenElementTheme" />
 
-</com.android.quickstep.fallback.RecentsRootView>
+        <include
+            android:id="@+id/overview_actions_view"
+            layout="@layout/overview_actions_container" />
+
+    </com.android.quickstep.fallback.RecentsDragLayer>
+</com.android.launcher3.LauncherRootView>
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index 7334d80..e103f66 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -30,8 +30,10 @@
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.content.ComponentName;
+import android.content.pm.ShortcutInfo;
 import android.os.Process;
 import android.util.Log;
+import android.util.SparseArray;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 import android.view.ViewGroup;
@@ -65,6 +67,7 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.shortcuts.ShortcutKey;
+import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
@@ -379,16 +382,24 @@
             mRestoreHelper.restoreBackup();
         }
         StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
-        ArrayList<ComponentKey> componentKeys = new ArrayList<>();
+        ArrayList<ComponentKey> componentKeysToBeCached = new ArrayList<>();
+        SparseArray<ShortcutRequest> requests = new SparseArray<>();
         for (AppTarget appTarget : appTargets) {
             ComponentKey key;
-            if (appTarget.getShortcutInfo() != null) {
-                key = ShortcutKey.fromInfo(appTarget.getShortcutInfo());
+            ShortcutRequest request = requests.get(appTarget.getUser().getIdentifier());
+            if (request == null) {
+                request = new ShortcutRequest(mLauncher, appTarget.getUser());
+                requests.put(appTarget.getUser().getIdentifier(), request);
+            }
+            List<ShortcutInfo> shortcutInfos = request.forPackage(appTarget.getPackageName(),
+                    appTarget.getClassName()).query(ShortcutRequest.ALL);
+            if (!shortcutInfos.isEmpty()) {
+                key = ShortcutKey.fromInfo(shortcutInfos.get(0));
             } else {
                 key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
                         appTarget.getClassName()), appTarget.getUser());
+                componentKeysToBeCached.add(key);
             }
-            componentKeys.add(key);
             predictionLog.append(key.toString());
             predictionLog.append(",rank:");
             predictionLog.append(appTarget.getRank());
@@ -401,7 +412,7 @@
         }
         updateDependencies();
         fillGapsWithPrediction();
-        cachePredictionComponentKeysIfNecessary(componentKeys);
+        cachePredictionComponentKeysIfNecessary(componentKeysToBeCached);
     }
 
     private void cachePredictionComponentKeysIfNecessary(ArrayList<ComponentKey> componentKeys) {
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 597c17b..51d53fd 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
@@ -36,6 +36,7 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.graphics.IconPalette;
@@ -90,8 +91,9 @@
     public void onDraw(Canvas canvas) {
         int count = canvas.save();
         if (!mIsPinned) {
-            boolean isBadged = getTag() instanceof WorkspaceItemInfo
-                    && !Process.myUserHandle().equals(((ItemInfo) getTag()).user);
+            ItemInfo info = (ItemInfo) getTag();
+            boolean isBadged = info != null && (!Process.myUserHandle().equals(info.user)
+                    || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT);
             drawEffect(canvas, isBadged);
             canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
             canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
index 9db6576..02bae64 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
@@ -121,10 +121,6 @@
         });
 
         mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
-            // Wait until the first scroll event before applying scroll to taskViewSimulator.
-            // Since, by default the current/running task already centered, this ensures that we
-            // do not move the running task, in case RecentsView has not yet laid out completely.
-            mRecentsViewScrollLinked = true;
             if (moveWindowWithRecentsScroll()) {
                 updateFinalShift();
             }
@@ -132,6 +128,7 @@
         runOnRecentsAnimationStart(() ->
                 mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
                         mRecentsAnimationTargets));
+        mRecentsViewScrollLinked = true;
     }
 
     protected void startNewTask(Consumer<Boolean> resultCallback) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 1701020..6f2f9fb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -55,7 +55,7 @@
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.fallback.FallbackRecentsStateController;
 import com.android.quickstep.fallback.FallbackRecentsView;
-import com.android.quickstep.fallback.RecentsRootView;
+import com.android.quickstep.fallback.RecentsDragLayer;
 import com.android.quickstep.fallback.RecentsState;
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.views.OverviewActionsView;
@@ -78,7 +78,8 @@
             new ActivityTracker<>();
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
-    private RecentsRootView mRecentsRootView;
+
+    private RecentsDragLayer mDragLayer;
     private FallbackRecentsView mFallbackRecentsView;
     private OverviewActionsView mActionsView;
 
@@ -89,13 +90,14 @@
     /**
      * Init drag layer and overview panel views.
      */
-    protected void initViews() {
-        setContentView(R.layout.fallback_recents_activity);
-        mRecentsRootView = findViewById(R.id.drag_layer);
+    protected void setupViews() {
+        inflateRootView(R.layout.fallback_recents_activity);
+        setContentView(getRootView());
+        mDragLayer = findViewById(R.id.drag_layer);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
         mActionsView = findViewById(R.id.overview_actions_view);
 
-        mRecentsRootView.recreateControllers();
+        mDragLayer.recreateControllers();
         mFallbackRecentsView.init(mActionsView);
     }
 
@@ -105,12 +107,6 @@
         super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
     }
 
-    public void onRootViewSizeChanged() {
-        if (isInMultiWindowMode()) {
-            onHandleConfigChanged();
-        }
-    }
-
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
@@ -130,7 +126,7 @@
         dispatchDeviceProfileChanged();
 
         reapplyUi();
-        mRecentsRootView.recreateControllers();
+        mDragLayer.recreateControllers();
     }
 
     /**
@@ -142,19 +138,14 @@
 
         // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
         // activity.
-        return (mRecentsRootView != null) && isInMultiWindowMode()
+        return (mDragLayer != null) && isInMultiWindowMode()
                 ? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
                 : dp.copy(this);
     }
 
     @Override
     public BaseDragLayer getDragLayer() {
-        return mRecentsRootView;
-    }
-
-    @Override
-    public View getRootView() {
-        return mRecentsRootView;
+        return mDragLayer;
     }
 
     @Override
@@ -252,7 +243,7 @@
 
         mOldConfig = new Configuration(getResources().getConfiguration());
         initDeviceProfile();
-        initViews();
+        setupViews();
 
         getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
                 Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 9242771..d20bbe9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -107,11 +107,22 @@
     }
 
     @Override
+    protected boolean shouldAddDummyTaskView(int runningTaskId) {
+        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == runningTaskId
+                && getTaskViewCount() == 0) {
+            // Do not add a dummy task if we are running over home with empty recents, so that we
+            // show the empty recents message instead of showing a dummy task and later removing it.
+            return false;
+        }
+        return super.shouldAddDummyTaskView(runningTaskId);
+    }
+
+    @Override
     protected void applyLoadPlan(ArrayList<Task> tasks) {
         // When quick-switching on 3p-launcher, we add a "dummy" tile corresponding to Launcher
         // as well. This tile is never shown as we have setCurrentTaskHidden, but allows use to
         // track the index of the next task appropriately, as if we are switching on any other app.
-        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId) {
+        if (mHomeTaskInfo != null && mHomeTaskInfo.taskId == mRunningTaskId && !tasks.isEmpty()) {
             // Check if the task list has running task
             boolean found = false;
             for (Task t : tasks) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java
new file mode 100644
index 0000000..a00015a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsDragLayer.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsActivity;
+
+/**
+ * Drag layer for fallback recents activity
+ */
+public class RecentsDragLayer extends BaseDragLayer<RecentsActivity> {
+
+    public RecentsDragLayer(Context context, AttributeSet attrs) {
+        super(context, attrs, 1 /* alphaChannelCount */);
+    }
+
+    @Override
+    public void recreateControllers() {
+        mControllers = new TouchController[] {
+                new RecentsTaskController(mActivity),
+                new FallbackNavBarTouchController(mActivity),
+        };
+    }
+
+    @Override
+    public void setInsets(Rect insets) {
+        super.setInsets(insets);
+        setBackground(insets.top == 0  || !mAllowSysuiScrims
+                ? null
+                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
deleted file mode 100644
index 7f5ec9b..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.fallback;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-
-import com.android.launcher3.R;
-import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.TouchController;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.RecentsActivity;
-
-public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
-
-    private static final int MIN_SIZE = 10;
-
-    private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
-
-    public RecentsRootView(Context context, AttributeSet attrs) {
-        super(context, attrs, 1 /* alphaChannelCount */);
-        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-                | SYSTEM_UI_FLAG_LAYOUT_STABLE);
-    }
-
-    public Point getLastKnownSize() {
-        return mLastKnownSize;
-    }
-
-    @Override
-    public void recreateControllers() {
-        mControllers = new TouchController[] {
-                new RecentsTaskController(mActivity),
-                new FallbackNavBarTouchController(mActivity),
-        };
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Check size changes before the actual measure, to avoid multiple measure calls.
-        int width = Math.max(MIN_SIZE, MeasureSpec.getSize(widthMeasureSpec));
-        int height = Math.max(MIN_SIZE, MeasureSpec.getSize(heightMeasureSpec));
-        if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
-            mLastKnownSize.set(width, height);
-            mActivity.onRootViewSizeChanged();
-        }
-
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @TargetApi(23)
-    @Override
-    protected boolean fitSystemWindows(Rect insets) {
-        // Update device profile before notifying the children.
-        mActivity.getDeviceProfile().updateInsets(insets);
-        setInsets(insets);
-        return false; // Let children get the full insets
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        // If the insets haven't changed, this is a no-op. Avoid unnecessary layout caused by
-        // modifying child layout params.
-        if (!insets.equals(mInsets)) {
-            super.setInsets(insets);
-        }
-        setBackground(insets.top == 0  || !mAllowSysuiScrims
-                ? null
-                : Themes.getAttrDrawable(getContext(), R.attr.workspaceStatusBarScrim));
-    }
-
-    public void dispatchInsets() {
-        mActivity.getDeviceProfile().updateInsets(mInsets);
-        super.setInsets(mInsets);
-    }
-}
\ No newline at end of file
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 6380bbe..cb22570 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
@@ -105,6 +105,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -146,8 +147,8 @@
  * A list of recent tasks.
  */
 @TargetApi(Build.VERSION_CODES.P)
-public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
-        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
+        Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
         InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
         SplitScreenBounds.OnChangeListener {
 
@@ -389,12 +390,12 @@
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
         setEnableFreeScroll(true);
         mSizeStrategy = sizeStrategy;
+        mActivity = BaseActivity.fromContext(context);
         mOrientationState = new RecentsOrientedState(
                 context, mSizeStrategy, this::animateRecentsRotationInPlace);
 
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
-        mActivity = BaseActivity.fromContext(context);
         mModel = RecentsModel.INSTANCE.get(context);
         mIdp = InvariantDeviceProfile.INSTANCE.get(context);
 
@@ -1115,13 +1116,20 @@
     }
 
     /**
+     * Returns true if we should add a dummy taskView for the running task id
+     */
+    protected boolean shouldAddDummyTaskView(int runningTaskId) {
+        return getTaskView(runningTaskId) == null;
+    }
+
+    /**
      * Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
      *
      * All subsequent calls to reload will keep the task as the first item until {@link #reset()}
      * is called.  Also scrolls the view to this task.
      */
     public void showCurrentTask(int runningTaskId) {
-        if (getTaskView(runningTaskId) == null) {
+        if (shouldAddDummyTaskView(runningTaskId)) {
             boolean wasEmpty = getChildCount() == 0;
             // Add an empty view for now until the task plan is loaded and applied
             final TaskView taskView = mTaskViewPool.getView();
@@ -1656,6 +1664,8 @@
                     !mOrientationState.canLauncherRotate() && isInLandscape);
             resetPaddingFromTaskSize();
             requestLayout();
+            // Reapply the current page to update page scrolls.
+            setCurrentPage(mCurrentPage);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index b3b899e..d605317 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -34,6 +34,9 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logger.LauncherAtom;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FolderIcon;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.InstanceIdSequence;
 import com.android.launcher3.logging.StatsLogManager;
@@ -178,6 +181,9 @@
         private Optional<ContainerInfo> mContainerInfo = Optional.empty();
         private int mSrcState = LAUNCHER_UICHANGED__SRC_STATE__HOME;
         private int mDstState = LAUNCHER_UICHANGED__DST_STATE__BACKGROUND;
+        private Optional<FromState> mFromState = Optional.empty();
+        private Optional<ToState> mToState = Optional.empty();
+        private Optional<String> mEditText = Optional.empty();
 
         @Override
         public StatsLogger withItemInfo(ItemInfo itemInfo) {
@@ -224,25 +230,33 @@
         }
 
         @Override
+        public StatsLogger withFromState(FromState fromState) {
+            this.mFromState = Optional.of(fromState);
+            return this;
+        }
+
+        @Override
+        public StatsLogger withToState(ToState toState) {
+            this.mToState = Optional.of(toState);
+            return this;
+        }
+
+        @Override
+        public StatsLogger withEditText(String editText) {
+            this.mEditText = Optional.of(editText);
+            return this;
+        }
+
+        @Override
         public void log(EventEnum event) {
             if (!Utilities.ATLEAST_R) {
                 return;
             }
 
-            LauncherAtom.ItemInfo.Builder itemInfoBuilder =
-                    (LauncherAtom.ItemInfo.Builder) mItemInfo.buildProto().toBuilder();
-            mRank.ifPresent(itemInfoBuilder::setRank);
-            if (mContainerInfo.isPresent()) {
-                // User already provided container info;
-                // default container info from item info will be ignored.
-                itemInfoBuilder.setContainerInfo(mContainerInfo.get());
-                write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
-                return;
-            }
-
             if (mItemInfo.container < 0) {
                 // Item is not within a folder. Write to StatsLog in same thread.
-                write(event, mInstanceId, itemInfoBuilder.build(), mSrcState, mDstState);
+                write(event, mInstanceId, applyOverwrites(mItemInfo.buildProto()), mSrcState,
+                        mDstState);
             } else {
                 // Item is inside the folder, fetch folder info in a BG thread
                 // and then write to StatsLog.
@@ -252,17 +266,33 @@
                             public void execute(LauncherAppState app, BgDataModel dataModel,
                                     AllAppsList apps) {
                                 FolderInfo folderInfo = dataModel.folders.get(mItemInfo.container);
-                                LauncherAtom.ItemInfo.Builder atomInfoBuilder =
-                                        (LauncherAtom.ItemInfo.Builder) mItemInfo
-                                                .buildProto(folderInfo).toBuilder();
-                                mRank.ifPresent(atomInfoBuilder::setRank);
-                                write(event, mInstanceId, atomInfoBuilder.build(), mSrcState,
-                                        mDstState);
+                                write(event, mInstanceId,
+                                        applyOverwrites(mItemInfo.buildProto(folderInfo)),
+                                        mSrcState, mDstState);
                             }
                         });
             }
         }
 
+        private LauncherAtom.ItemInfo applyOverwrites(LauncherAtom.ItemInfo atomInfo) {
+            LauncherAtom.ItemInfo.Builder itemInfoBuilder =
+                    (LauncherAtom.ItemInfo.Builder) atomInfo.toBuilder();
+
+            mRank.ifPresent(itemInfoBuilder::setRank);
+            mContainerInfo.ifPresent(itemInfoBuilder::setContainerInfo);
+
+            if (mFromState.isPresent() || mToState.isPresent() || mEditText.isPresent()) {
+                FolderIcon.Builder folderIconBuilder = (FolderIcon.Builder) itemInfoBuilder
+                        .getFolderIcon()
+                        .toBuilder();
+                mFromState.ifPresent(folderIconBuilder::setFromLabelState);
+                mToState.ifPresent(folderIconBuilder::setToLabelState);
+                mEditText.ifPresent(folderIconBuilder::setLabelInfo);
+                itemInfoBuilder.setFolderIcon(folderIconBuilder);
+            }
+            return itemInfoBuilder.build();
+        }
+
         private void write(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo,
                 int srcState, int dstState) {
             if (IS_VERBOSE) {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index bd8ab08..b9e0f62 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -131,6 +131,10 @@
                     TestCommandReceiver.GET_SYSTEM_HEALTH_MESSAGE, startTime.toString()).
                     getString("result"));
         }
+        // b/143488140
+        mDevice.pressHome();
+        mDevice.waitForIdle();
+        startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
     }
 
     // b/143488140
diff --git a/res/layout/work_mode_switch.xml b/res/layout/work_mode_switch.xml
index b5237db..31953c7 100644
--- a/res/layout/work_mode_switch.xml
+++ b/res/layout/work_mode_switch.xml
@@ -17,7 +17,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    style="@style/PrimaryMediumText"
+    style="@style/PrimaryHeadline"
     android:id="@+id/work_mode_toggle"
     android:drawableStart="@drawable/ic_corp"
     android:drawablePadding="16dp"
@@ -25,6 +25,7 @@
     android:textColor="?attr/workProfileOverlayTextColor"
     android:layout_alignParentBottom="true"
     android:ellipsize="end"
+    android:elevation="10dp"
     android:gravity="start"
     android:lines="1"
     android:showText="false"
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 82f5dc6..25f21f3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -246,7 +246,6 @@
     <style name="DropTargetButton" parent="DropTargetButtonBase" />
 
     <style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
-    <style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
     <style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
 
     <style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ec32e62..7fc64ea 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -260,7 +260,6 @@
 
     @Thunk
     Workspace mWorkspace;
-    private View mLauncherView;
     @Thunk
     DragLayer mDragLayer;
     private DragController mDragController;
@@ -363,6 +362,7 @@
         LauncherAppState app = LauncherAppState.getInstance(this);
         mOldConfig = new Configuration(getResources().getConfiguration());
         mModel = app.getModel();
+
         mRotationHelper = new RotationHelper(this);
         InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
         initDeviceProfile(idp);
@@ -382,8 +382,7 @@
                 appWidgetId -> getWorkspace().removeWidget(appWidgetId));
         mAppWidgetHost.startListening();
 
-        mLauncherView = LayoutInflater.from(this).inflate(R.layout.launcher, null);
-
+        inflateRootView(R.layout.launcher);
         setupViews();
         mPopupDataProvider = new PopupDataProvider(this::updateNotificationDots);
 
@@ -420,7 +419,7 @@
         // For handling default keys
         setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
 
-        setContentView(mLauncherView);
+        setContentView(getRootView());
         getRootView().dispatchInsets();
 
         // Listen for broadcasts
@@ -520,12 +519,6 @@
     }
 
     @Override
-    public void reapplyUi(boolean cancelCurrentAnimation) {
-        getRootView().dispatchInsets();
-        super.reapplyUi(cancelCurrentAnimation);
-    }
-
-    @Override
     public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
         onIdpChanged(idp);
     }
@@ -581,11 +574,6 @@
         return mStateManager;
     }
 
-    @Override
-    public <T extends View> T findViewById(int id) {
-        return mLauncherView.findViewById(id);
-    }
-
     private LauncherCallbacks mLauncherCallbacks;
 
     /**
@@ -1118,10 +1106,6 @@
         mHotseat = findViewById(R.id.hotseat);
         mHotseat.setWorkspace(mWorkspace);
 
-        mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-
         // Setup the drag layer
         mDragLayer.setup(mDragController, mWorkspace);
 
@@ -1335,11 +1319,6 @@
     }
 
     @Override
-    public LauncherRootView getRootView() {
-        return (LauncherRootView) mLauncherView;
-    }
-
-    @Override
     public DragLayer getDragLayer() {
         return mDragLayer;
     }
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index 6951ff2..51504ce 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -10,6 +10,8 @@
 import android.view.ViewDebug;
 import android.view.WindowInsets;
 
+import com.android.launcher3.statemanager.StatefulActivity;
+
 import java.util.Collections;
 import java.util.List;
 
@@ -17,7 +19,7 @@
 
     private final Rect mTempRect = new Rect();
 
-    private final Launcher mLauncher;
+    private final StatefulActivity mActivity;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
@@ -31,17 +33,17 @@
 
     public LauncherRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mLauncher = Launcher.getLauncher(context);
+        mActivity = StatefulActivity.fromContext(context);
     }
 
     private void handleSystemWindowInsets(Rect insets) {
         // Update device profile before notifying th children.
-        mLauncher.getDeviceProfile().updateInsets(insets);
+        mActivity.getDeviceProfile().updateInsets(insets);
         boolean resetState = !insets.equals(mInsets);
         setInsets(insets);
 
         if (resetState) {
-            mLauncher.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
+            mActivity.getStateManager().reapplyState(true /* cancelCurrentAnimation */);
         }
     }
 
@@ -63,7 +65,7 @@
     }
 
     public void dispatchInsets() {
-        mLauncher.getDeviceProfile().updateInsets(mInsets);
+        mActivity.getDeviceProfile().updateInsets(mInsets);
         super.setInsets(mInsets);
     }
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index fb58f21..a8dca12 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -415,7 +415,7 @@
 
         // Always enter the spring loaded mode
         mLauncher.getStateManager().goToState(SPRING_LOADED);
-        mStatsLogManager.logger().withItemInfo(dragObject.originalDragInfo)
+        mStatsLogManager.logger().withItemInfo(dragObject.dragInfo)
                 .withInstanceId(dragObject.logInstanceId)
                 .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
     }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 301f79c..e950f3c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -81,7 +81,10 @@
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -99,6 +102,7 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
+import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 /**
@@ -109,6 +113,12 @@
         View.OnFocusChangeListener, DragListener, ExtendedEditText.OnBackKeyListener {
     private static final String TAG = "Launcher.Folder";
     private static final boolean DEBUG = false;
+
+    /**
+     * Used for separating folder title when logging together.
+     */
+    private static final CharSequence FOLDER_LABEL_DELIMITER = "~";
+
     /**
      * We avoid measuring {@link #mContent} with a 0 width or height, as this
      * results in CellLayout being measured as UNSPECIFIED, which it does not support.
@@ -155,6 +165,8 @@
     protected final Launcher mLauncher;
     protected DragController mDragController;
     public FolderInfo mInfo;
+    private CharSequence mFromTitle;
+    private FromState mFromLabelState;
 
     @Thunk FolderIcon mFolderIcon;
 
@@ -335,7 +347,6 @@
             Log.d(TAG, "onBackKey newTitle=" + newTitle);
         }
         mInfo.setTitle(newTitle);
-        mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
         mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
                 mLauncher.getModelWriter());
         mFolderIcon.onTitleChanged(newTitle);
@@ -415,6 +426,8 @@
 
     void bind(FolderInfo info) {
         mInfo = info;
+        mFromTitle = info.title;
+        mFromLabelState = info.getFromLabelState();
         ArrayList<WorkspaceItemInfo> children = info.contents;
         Collections.sort(children, ITEM_POS_COMPARATOR);
         updateItemLocationsInDatabaseBatch(true);
@@ -1442,10 +1455,38 @@
     public void onFocusChange(View v, boolean hasFocus) {
         if (v == mFolderName) {
             if (hasFocus) {
+                mFromLabelState = mInfo.getFromLabelState();
+                mFromTitle = mInfo.title;
                 startEditingFolderName();
             } else {
-                mStatsLogManager.logger().withItemInfo(mInfo).log(LAUNCHER_FOLDER_LABEL_UPDATED);
-                logFolderLabelState();
+                StatsLogger statsLogger = mStatsLogManager.logger()
+                        .withItemInfo(mInfo)
+                        .withFromState(mFromLabelState);
+
+                // If the folder label is suggested, it is logged to improve prediction model.
+                // When both old and new labels are logged together delimiter is used.
+                StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
+                if (mFromLabelState.equals(FromState.FROM_SUGGESTED)) {
+                    labelInfoBuilder.add(mFromTitle);
+                }
+
+                ToState toLabelState;
+                if (mFromTitle != null && mFromTitle.equals(mInfo.title)) {
+                    toLabelState = ToState.UNCHANGED;
+                } else {
+                    toLabelState = mInfo.getToLabelState();
+                    if (toLabelState.toString().startsWith("TO_SUGGESTION")) {
+                        labelInfoBuilder.add(mInfo.title);
+                    }
+                }
+                statsLogger.withToState(toLabelState);
+
+                if (labelInfoBuilder.length() > 0) {
+                    statsLogger.withEditText(labelInfoBuilder.toString());
+                }
+
+                statsLogger.log(LAUNCHER_FOLDER_LABEL_UPDATED);
+                logFolderLabelState(mFromLabelState, toLabelState);
                 mFolderName.dispatchBackKey();
             }
         }
@@ -1650,8 +1691,8 @@
      * @deprecated This method is only used for log validation and soon will be removed.
      */
     @Deprecated
-    public void logFolderLabelState() {
+    public void logFolderLabelState(FromState fromState, ToState toState) {
         mLauncher.getUserEventDispatcher()
-                .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
+                .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent(fromState, toState));
     }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 7af4664..e24033f 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,7 +20,9 @@
 
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
 import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -63,6 +65,8 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.logging.InstanceId;
 import com.android.launcher3.logging.StatsLogManager;
 import com.android.launcher3.model.data.AppInfo;
@@ -80,6 +84,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 
 /**
  * An icon that can appear on in the workspace representing an {@link Folder}.
@@ -428,7 +433,6 @@
             mPreviewItemManager.hidePreviewItem(finalIndex, false);
             mFolder.showItem(item);
             setLabelSuggestion(nameInfos, instanceId);
-            mFolder.logFolderLabelState();
             invalidate();
         }, DROP_IN_ANIMATION_DURATION);
     }
@@ -444,15 +448,40 @@
                 || mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
             return;
         }
-        if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
+        if (nameInfos == null || Stream.of(nameInfos)
+                .allMatch(nameInfo -> nameInfo == null || isEmpty(nameInfo.getLabel()))) {
+            StatsLogManager.newInstance(getContext()).logger()
+                    .withInstanceId(instanceId)
+                    .withItemInfo(mInfo)
+                    .log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS);
             return;
         }
-        mInfo.setTitle(nameInfos[0].getLabel());
-        StatsLogManager.newInstance(getContext()).logger().withItemInfo(mInfo)
-                .withInstanceId(instanceId).log(LAUNCHER_FOLDER_LABEL_UPDATED);
+        if (nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
+            StatsLogManager.newInstance(getContext()).logger()
+                    .withInstanceId(instanceId)
+                    .withItemInfo(mInfo)
+                    .log(LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY);
+            return;
+        }
+        CharSequence newTitle = nameInfos[0].getLabel();
+        FromState fromState = mInfo.getFromLabelState();
+
+        mInfo.setTitle(newTitle);
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
         mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
+
+        // Logging for folder creation flow
+        StatsLogManager.newInstance(getContext()).logger()
+                .withInstanceId(instanceId)
+                .withItemInfo(mInfo)
+                .withFromState(fromState)
+                .withToState(ToState.TO_SUGGESTION0)
+                // When LAUNCHER_FOLDER_LABEL_UPDATED event.edit_text does not have delimiter,
+                // event is assumed to be folder creation on the server side.
+                .withEditText(newTitle.toString())
+                .log(LAUNCHER_FOLDER_AUTO_LABELED);
+        mFolder.logFolderLabelState(fromState, ToState.TO_SUGGESTION0);
     }
 
 
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 82d61da..3edfa8d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -21,6 +21,8 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -66,8 +68,16 @@
                 + "resulting in a new folder creation")
         LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
 
-        @UiEvent(doc = "User action resulted in or manually updated the folder label to "
-                + "new/same value.")
+        @UiEvent(doc = "Folder's label is automatically assigned.")
+        LAUNCHER_FOLDER_AUTO_LABELED(591),
+
+        @UiEvent(doc = "Could not auto-label a folder because primary suggestion is null or empty.")
+        LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_PRIMARY(592),
+
+        @UiEvent(doc = "Could not auto-label a folder because no suggestions exist.")
+        LAUNCHER_FOLDER_AUTO_LABELING_SKIPPED_EMPTY_SUGGESTIONS(593),
+
+        @UiEvent(doc = "User manually updated the folder label.")
         LAUNCHER_FOLDER_LABEL_UPDATED(460),
 
         @UiEvent(doc = "User long pressed on the workspace empty space.")
@@ -246,6 +256,27 @@
         }
 
         /**
+         * Sets FromState field of log message.
+         */
+        default StatsLogger withFromState(FromState fromState) {
+            return this;
+        }
+
+        /**
+         * Sets ToState field of log message.
+         */
+        default StatsLogger withToState(ToState toState) {
+            return this;
+        }
+
+        /**
+         * Sets editText field of log message.
+         */
+        default StatsLogger withEditText(String editText) {
+            return this;
+        }
+
+        /**
          * Sets the final value for container related fields of log message.
          *
          * By default container related fields are derived from {@link ItemInfo}, this method would
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index 8f577b5..08eb383 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -54,7 +54,6 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.OptionalInt;
-import java.util.StringJoiner;
 import java.util.stream.IntStream;
 
 
@@ -88,20 +87,6 @@
 
     public Intent suggestedFolderNames;
 
-    // Represents the title before current.
-    // Primarily used for logging purpose.
-    private CharSequence mPreviousTitle;
-
-    // True if the title before was manually entered, suggested otherwise.
-    // Primarily used for logging purpose.
-    public boolean fromCustom;
-
-    /**
-     * Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
-     * for logging.
-     */
-    private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
-
     /**
      * The apps and shortcuts
      */
@@ -207,21 +192,16 @@
         return getDefaultItemInfoBuilder()
                 .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
                 .setRank(rank)
-                .setAttribute(fromCustom ? MANUAL_LABEL : SUGGESTED_LABEL)
+                .setAttribute(hasOption(FLAG_MANUAL_FOLDER_NAME) ? MANUAL_LABEL : SUGGESTED_LABEL)
                 .setContainerInfo(getContainerInfo())
                 .build();
     }
 
     @Override
     public void setTitle(CharSequence title) {
-        mPreviousTitle = this.title;
         this.title = title;
     }
 
-    public CharSequence getPreviousTitle() {
-        return mPreviousTitle;
-    }
-
     @Override
     public ItemInfo makeShallowCopy() {
         FolderInfo folderInfo = new FolderInfo();
@@ -235,30 +215,7 @@
      */
     @Override
     public LauncherAtom.ItemInfo buildProto() {
-        FromState fromFolderLabelState = getFromFolderLabelState();
-        ToState toFolderLabelState = getToFolderLabelState();
-        LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
-                .setCardinality(contents.size())
-                .setFromLabelState(fromFolderLabelState)
-                .setToLabelState(toFolderLabelState);
-
-        // If the folder label is suggested, it is logged to improve prediction model.
-        // When both old and new labels are logged together delimiter is used.
-        StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
-        if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
-            labelInfoBuilder.add(mPreviousTitle);
-        }
-        if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
-            labelInfoBuilder.add(title);
-        }
-        if (labelInfoBuilder.length() > 0) {
-            folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
-        }
-
-        return getDefaultItemInfoBuilder()
-                .setFolderIcon(folderIconBuilder)
-                .setContainerInfo(getContainerInfo())
-                .build();
+        return buildProto(null);
     }
 
     /**
@@ -280,15 +237,27 @@
 
     }
 
-    private LauncherAtom.ToState getToFolderLabelState() {
+    /**
+     * Returns {@link FromState} based on current {@link #title}.
+     */
+    public LauncherAtom.FromState getFromLabelState() {
+        return title == null
+                ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
+                : title.length() == 0
+                        ? LauncherAtom.FromState.FROM_EMPTY
+                        : hasOption(FLAG_MANUAL_FOLDER_NAME)
+                                ? LauncherAtom.FromState.FROM_CUSTOM
+                                : LauncherAtom.FromState.FROM_SUGGESTED;
+    }
+
+    /**
+     * Returns {@link ToState} based on current {@link #title}.
+     */
+    public LauncherAtom.ToState getToLabelState() {
         if (title == null) {
             return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
         }
 
-        if (title.equals(mPreviousTitle)) {
-            return LauncherAtom.ToState.UNCHANGED;
-        }
-
         if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
             return title.length() > 0
                     ? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
@@ -335,17 +304,6 @@
                 // fall through
         }
         return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
-
-    }
-
-    private LauncherAtom.FromState getFromFolderLabelState() {
-        return mPreviousTitle == null
-                ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
-                : mPreviousTitle.length() == 0
-                        ? LauncherAtom.FromState.FROM_EMPTY
-                        : fromCustom
-                                ? LauncherAtom.FromState.FROM_CUSTOM
-                                : LauncherAtom.FromState.FROM_SUGGESTED;
     }
 
     private Optional<String[]> getSuggestedLabels() {
@@ -368,7 +326,8 @@
      * @deprecated This method is used only for validation purpose and soon will be removed.
      */
     @Deprecated
-    public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
+    public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent(FromState fromState,
+            ToState toState) {
         return LauncherLogProto.LauncherEvent.newBuilder()
                 .setAction(LauncherLogProto.Action
                         .newBuilder()
@@ -377,8 +336,8 @@
                         .newBuilder()
                         .setType(Target.Type.ITEM)
                         .setItemType(LauncherLogProto.ItemType.EDITTEXT)
-                        .setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
-                        .setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
+                        .setFromFolderLabelState(convertFolderLabelState(fromState))
+                        .setToFolderLabelState(convertFolderLabelState(toState)))
                 .addSrcTarget(Target.newBuilder()
                         .setType(Target.Type.CONTAINER)
                         .setContainerType(LauncherLogProto.ContainerType.FOLDER)
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index 0a1607c..dbe5f42 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -18,10 +18,13 @@
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 
 import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.annotation.CallSuper;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.LauncherRootView;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
@@ -38,6 +41,8 @@
     private final Runnable mHandleDeferredResume = this::handleDeferredResume;
     private boolean mDeferredResumePending;
 
+    private LauncherRootView mRootView;
+
     /**
      * Create handlers to control the property changes for this activity
      */
@@ -55,6 +60,23 @@
      */
     public abstract StateManager<STATE_TYPE> getStateManager();
 
+    protected void inflateRootView(int layoutId) {
+        mRootView = (LauncherRootView) LayoutInflater.from(this).inflate(layoutId, null);
+        mRootView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+    }
+
+    @Override
+    public final LauncherRootView getRootView() {
+        return mRootView;
+    }
+
+    @Override
+    public <T extends View> T findViewById(int id) {
+        return mRootView.findViewById(id);
+    }
+
     /**
      * Called when transition to the state starts
      */
@@ -87,6 +109,7 @@
      * the transition if requested.
      */
     public void reapplyUi(boolean cancelCurrentAnimation) {
+        getRootView().dispatchInsets();
         getStateManager().reapplyState(cancelCurrentAnimation);
     }