Add LAUNCHER_FOLDER_LABEL_CHANGED events for folder label updates.

Sample Logs for all update combinations: https://docs.google.com/document/d/1CBP2yTcXdFhPdNG5ZmWFKSgd8mDbMevY-akVlUXPLDo/edit#bookmark=id.7y1p8n2dz8ge

Bug: 155410872
Bug: 152978018
Change-Id: I296b124b16aa07878f2cf7b74ab91f13b8e6cfbf
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index 19f7213..f1b71e8 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -99,13 +99,14 @@
   optional int32 cardinality = 1;
 
   // State of the folder label before the event.
-  optional FromState from_state = 2;
+  optional FromState from_label_state = 2;
 
   // State of the folder label after the event.
-  optional ToState to_state = 3;
+  optional ToState to_label_state = 3;
 
-  // Populated only when folder label was suggested.
-  optional string label = 4;
+  // Details about actual folder label.
+  // Populated when folder label is not a PII.
+  optional string label_info = 4;
 }
 
 //////////////////////////////////////////////
@@ -131,70 +132,77 @@
   }
 }
 
-// Represents state of FolderLabel before editing.
+// Represents state of EditText field before update.
 enum FromState {
   // Default value.
+  // Used when a FromState is not applicable, for example, during folder creation.
   FROM_STATE_UNSPECIFIED = 0;
 
-  // FolderLabel was empty.
+  // EditText was empty.
+  // Eg: When a folder label is updated from empty string.
   FROM_EMPTY = 1;
 
-  // FolderLabel was non-empty and manually entered by the user.
+  // EditText was non-empty and manually entered by the user.
+  // Eg: When a folder label is updated from a user-entered value.
   FROM_CUSTOM = 2;
 
-  // FolderLabel was non-empty and one of the suggestions.
+  // EditText was non-empty and one of the suggestions.
+  // Eg: When a folder label is updated from a suggested value.
   FROM_SUGGESTED = 3;
 }
 
-// Represents state of FolderLabel after editing.
+// Represents state of EditText field after update.
 enum ToState {
   // Default value.
+  // Used when ToState is not applicable, for example, when folder label is updated to a different
+  // value when folder label suggestion feature is disabled.
   TO_STATE_UNSPECIFIED = 0;
-  // User attempted to change the folder label, but was not changed.
+
+  // User attempted to change the EditText, but was not changed.
   UNCHANGED = 1;
 
   // New label matches with primary(aka top) suggestion.
   TO_SUGGESTION0 = 2;
 
-  // New label matches with second top suggestion even though the top suggestion was non-empty.
+  // New value matches with second top suggestion even though the top suggestion was non-empty.
   TO_SUGGESTION1_WITH_VALID_PRIMARY = 3;
 
-  // New label matches with second top suggestion given that top suggestion was empty.
+  // New value matches with second top suggestion given that top suggestion was empty.
   TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 4;
 
-  // New label matches with third top suggestion even though the top suggestion was non-empty.
+  // New value matches with third top suggestion even though the top suggestion was non-empty.
   TO_SUGGESTION2_WITH_VALID_PRIMARY = 5;
 
-  // New label matches with third top suggestion given that top suggestion was empty.
+  // New value matches with third top suggestion given that top suggestion was empty.
   TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 6;
 
-  // New label matches with 4th top suggestion even though the top suggestion was non-empty.
+  // New value matches with 4th top suggestion even though the top suggestion was non-empty.
   TO_SUGGESTION3_WITH_VALID_PRIMARY = 7;
 
-  // New label matches with 4th top suggestion given that top suggestion was empty.
+  // New value matches with 4th top suggestion given that top suggestion was empty.
   TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 8;
 
-  // New label is empty even though the top suggestion was non-empty.
+  // New value is empty even though the top suggestion was non-empty.
   TO_EMPTY_WITH_VALID_PRIMARY = 9;
 
-  // New label is empty given that top suggestion was empty.
+  // New value is empty given that top suggestion was empty.
   TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 10;
 
-  // New label is empty given that no suggestions were provided.
+  // New value is empty given that no suggestions were provided.
   TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 11;
 
-  // New label is empty given that suggestions feature was disabled.
+  // New value is empty given that suggestions feature was disabled.
   TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 12;
 
-  // New label is non-empty and does not match with any of the suggestions even though the top suggestion was non-empty.
+  // New value is non-empty and does not match with any of the suggestions even though the top suggestion was non-empty.
   TO_CUSTOM_WITH_VALID_PRIMARY = 13;
 
-  // New label is non-empty and not match with any suggestions given that top suggestion was empty.
+  // New value is non-empty and not match with any suggestions given that top suggestion was empty.
   TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 14;
 
-  // New label is non-empty and also no suggestions were provided.
+  // New value is non-empty and also no suggestions were provided.
   TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 15;
 
-  // New label is non-empty and also suggestions feature was disable.
+  // New value is non-empty and also suggestions feature was disable.
   TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 16;
 }
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 7f8f0a0..e4d0adf 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
@@ -121,7 +121,7 @@
         if (!putIntoFolder.isEmpty()) {
             ItemInfo firstItem = putIntoFolder.get(0);
             FolderInfo folderInfo = new FolderInfo();
-            folderInfo.title = "";
+            folderInfo.setTitle("");
             mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
                     firstItem.screenId, firstItem.cellX, firstItem.cellY);
             folderInfo.contents.addAll(putIntoFolder);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 29a737c..f7fe535 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -24,6 +24,8 @@
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
 import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
 import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
 
 import static java.util.Arrays.asList;
@@ -196,6 +198,7 @@
     @Thunk int mScrollHintDir = SCROLL_NONE;
     @Thunk int mCurrentScrollDir = SCROLL_NONE;
 
+    private StatsLogManager mStatsLogManager;
 
     /**
      * Used to inflate the Workspace from XML.
@@ -208,10 +211,12 @@
         setAlwaysDrawnWithCacheEnabled(false);
 
         mLauncher = Launcher.getLauncher(context);
+        mStatsLogManager = StatsLogManager.newInstance(context);
         // We need this view to be focusable in touch mode so that when text editing of the folder
         // name is complete, we have something to focus on, thus hiding the cursor and giving
         // reliable behavior when clicking the text field (since it will always gain focus on click).
         setFocusableInTouchMode(true);
+
     }
 
     @Override
@@ -329,8 +334,8 @@
         if (DEBUG) {
             Log.d(TAG, "onBackKey newTitle=" + newTitle);
         }
-        mInfo.previousTitle = mInfo.title;
-        mInfo.title = 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);
@@ -1326,10 +1331,8 @@
         if (d.stateAnnouncer != null) {
             d.stateAnnouncer.completeAction(R.string.item_moved);
         }
-        StatsLogManager.newInstance(getContext())
-                .log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
-                    d.logInstanceId,
-                    d.dragInfo.buildProto(mInfo));
+        mStatsLogManager
+                .log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
     }
 
     // This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1434,6 +1437,8 @@
             if (hasFocus) {
                 startEditingFolderName();
             } else {
+                mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
+                logFolderLabelState();
                 mFolderName.dispatchBackKey();
             }
         }
@@ -1631,4 +1636,15 @@
     public FolderPagedView getContent() {
         return mContent;
     }
+
+    /**
+     * Logs current folder label info.
+     *
+     * @deprecated This method is only used for log validation and soon will be removed.
+     */
+    @Deprecated
+    public void logFolderLabelState() {
+        mLauncher.getUserEventDispatcher()
+                .logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
+    }
 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index bb358ab..153d6bc 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,7 +20,7 @@
 
 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_CHANGED;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -429,6 +429,7 @@
             mPreviewItemManager.hidePreviewItem(finalIndex, false);
             mFolder.showItem(item);
             setLabelSuggestion(nameInfos, instanceId);
+            mFolder.logFolderLabelState();
             invalidate();
         }, DROP_IN_ANIMATION_DURATION);
     }
@@ -447,10 +448,9 @@
         if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
             return;
         }
-        mInfo.previousTitle = mInfo.title;
-        mInfo.title = nameInfos[0].getLabel();
+        mInfo.setTitle(nameInfos[0].getLabel());
         StatsLogManager.newInstance(getContext())
-                .log(LAUNCHER_FOLDER_LABEL_CHANGED, instanceId, mInfo.getFolderIconAtom());
+                .log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
         onTitleChanged(mInfo.title);
         mFolder.mFolderName.setText(mInfo.title);
         mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index ee9322b..b240f0b 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -57,9 +57,9 @@
                 + "resulting in a new folder creation")
         LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
 
-        @LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
-                + "resulting in new folder creation")
-        LAUNCHER_FOLDER_LABEL_CHANGED(460),
+        @LauncherUiEvent(doc = "User action resulted in or manually updated the folder label to "
+                + "new/same value.")
+        LAUNCHER_FOLDER_LABEL_UPDATED(460),
 
         @LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
         LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java
index fd024f4..096743a 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -20,6 +20,13 @@
 
 import static androidx.core.util.Preconditions.checkNotNull;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+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.stream;
 import static java.util.Optional.ofNullable;
 
@@ -32,13 +39,20 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderNameInfo;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.FromState;
+import com.android.launcher3.logger.LauncherAtom.ToState;
 import com.android.launcher3.model.ModelWriter;
+import com.android.launcher3.userevent.LauncherLogProto;
+import com.android.launcher3.userevent.LauncherLogProto.Target;
+import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
+import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.ArrayList;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.OptionalInt;
+import java.util.StringJoiner;
 import java.util.stream.IntStream;
 
 
@@ -72,9 +86,19 @@
 
     public Intent suggestedFolderNames;
 
-    // When title changes, previous title is stored.
+    // Represents the title before current.
     // Primarily used for logging purpose.
-    public CharSequence previousTitle;
+    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
@@ -179,9 +203,20 @@
     @Override
     public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
         return getDefaultItemInfoBuilder()
-            .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
-            .setContainerInfo(getContainerInfo())
-            .build();
+                .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
+                .setRank(rank)
+                .setContainerInfo(getContainerInfo())
+                .build();
+    }
+
+    @Override
+    public void setTitle(CharSequence title) {
+        mPreviousTitle = this.title;
+        this.title = title;
+    }
+
+    public CharSequence getPreviousTitle() {
+        return mPreviousTitle;
     }
 
     @Override
@@ -193,19 +228,30 @@
     }
 
     /**
-     * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging
-     * into Westworld.
-     *
+     * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
      */
-    public LauncherAtom.ItemInfo getFolderIconAtom() {
-        LauncherAtom.ToState toFolderLabelState = getToFolderLabelState();
+    @Override
+    public LauncherAtom.ItemInfo buildProto() {
+        FromState fromFolderLabelState = getFromFolderLabelState();
+        ToState toFolderLabelState = getToFolderLabelState();
         LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
                 .setCardinality(contents.size())
-                .setFromState(getFromFolderLabelState())
-                .setToState(toFolderLabelState);
-        if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
-            folderIconBuilder.setLabel(title.toString());
+                .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())
@@ -236,7 +282,7 @@
             return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
         }
 
-        if (title.equals(previousTitle)) {
+        if (title.equals(mPreviousTitle)) {
             return LauncherAtom.ToState.UNCHANGED;
         }
 
@@ -290,13 +336,13 @@
     }
 
     private LauncherAtom.FromState getFromFolderLabelState() {
-        return previousTitle == null
+        return mPreviousTitle == null
                 ? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
-                : previousTitle.toString().isEmpty()
-                ? LauncherAtom.FromState.FROM_EMPTY
-                : hasOption(FLAG_MANUAL_FOLDER_NAME)
-                ? LauncherAtom.FromState.FROM_CUSTOM
-                : LauncherAtom.FromState.FROM_SUGGESTED;
+                : mPreviousTitle.length() == 0
+                        ? LauncherAtom.FromState.FROM_EMPTY
+                        : fromCustom
+                                ? LauncherAtom.FromState.FROM_CUSTOM
+                                : LauncherAtom.FromState.FROM_SUGGESTED;
     }
 
     private Optional<String[]> getSuggestedLabels() {
@@ -312,4 +358,112 @@
                                 .map(CharSequence::toString)
                                 .toArray(String[]::new));
     }
+
+    /**
+     * Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
+     *
+     * @deprecated This method is used only for validation purpose and soon will be removed.
+     */
+    @Deprecated
+    public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
+        return LauncherLogProto.LauncherEvent.newBuilder()
+                .setAction(LauncherLogProto.Action
+                        .newBuilder()
+                        .setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
+                .addSrcTarget(Target
+                        .newBuilder()
+                        .setType(Target.Type.ITEM)
+                        .setItemType(LauncherLogProto.ItemType.EDITTEXT)
+                        .setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
+                        .setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
+                .addSrcTarget(Target.newBuilder()
+                        .setType(Target.Type.CONTAINER)
+                        .setContainerType(LauncherLogProto.ContainerType.FOLDER)
+                        .setPageIndex(screenId)
+                        .setGridX(cellX)
+                        .setGridY(cellY)
+                        .setCardinality(contents.size()))
+                .addSrcTarget(newParentContainerTarget())
+                .build();
+    }
+
+    /**
+     * @deprecated This method is used only for validation purpose and soon will be removed.
+     */
+    @Deprecated
+    private Target.Builder newParentContainerTarget() {
+        Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
+        switch (container) {
+            case CONTAINER_HOTSEAT:
+                return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
+            case CONTAINER_DESKTOP:
+                return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
+            default:
+                throw new AssertionError(String
+                        .format("Expected container to be either %s or %s but found %s.",
+                                CONTAINER_HOTSEAT,
+                                CONTAINER_DESKTOP,
+                                container));
+        }
+    }
+
+    /**
+     * @deprecated This method is used only for validation purpose and soon will be removed.
+     */
+    @Deprecated
+    private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
+        switch (fromState) {
+            case FROM_EMPTY:
+                return FROM_EMPTY;
+            case FROM_SUGGESTED:
+                return FROM_SUGGESTED;
+            case FROM_CUSTOM:
+                return FROM_CUSTOM;
+            default:
+                return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
+        }
+    }
+
+    /**
+     * @deprecated This method is used only for validation purpose and soon will be removed.
+     */
+    @Deprecated
+    private static ToFolderLabelState convertFolderLabelState(ToState toState) {
+        switch (toState) {
+            case UNCHANGED:
+                return ToFolderLabelState.UNCHANGED;
+            case TO_SUGGESTION0:
+                return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
+            case TO_SUGGESTION1_WITH_VALID_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
+            case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+            case TO_SUGGESTION2_WITH_VALID_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
+            case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+            case TO_SUGGESTION3_WITH_VALID_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
+            case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
+                return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+            case TO_EMPTY_WITH_VALID_PRIMARY:
+                return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
+            case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+                return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+            case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
+                return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+            case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
+                return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+            case TO_CUSTOM_WITH_VALID_PRIMARY:
+                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
+            case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
+                return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+            case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
+                return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
+            case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
+                return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
+            default:
+                return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 7611ee7..f2b7e54 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -252,6 +252,13 @@
     /**
      * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
      */
+    public LauncherAtom.ItemInfo buildProto() {
+        return buildProto(null);
+    }
+
+    /**
+     * Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
+     */
     public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
         LauncherAtom.ItemInfo.Builder itemBuilder = getDefaultItemInfoBuilder();
         Optional<ComponentName> nullableComponent = Optional.ofNullable(getTargetComponent());
@@ -345,4 +352,8 @@
         itemInfo.copyFrom(this);
         return itemInfo;
     }
+
+    public void setTitle(CharSequence title) {
+        this.title = title;
+    }
 }