diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index 19f72132c8..f1b71e8868 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -99,13 +99,14 @@ message FolderIcon { 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 @@ message FolderContainer { } } -// 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 7f8f0a0576..e4d0adf0c0 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 @@ public class HotseatEduController { 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 29a737c227..f7fe535a48 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.LauncherState.NORMAL; 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo @Thunk int mScrollHintDir = SCROLL_NONE; @Thunk int mCurrentScrollDir = SCROLL_NONE; + private StatsLogManager mStatsLogManager; /** * Used to inflate the Workspace from XML. @@ -208,10 +211,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (hasFocus) { startEditingFolderName(); } else { + mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto()); + logFolderLabelState(); mFolderName.dispatchBackKey(); } } @@ -1631,4 +1636,15 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo 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 bb358abf89..153d6bceb1 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -20,7 +20,7 @@ import static android.text.TextUtils.isEmpty; 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 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel mPreviewItemManager.hidePreviewItem(finalIndex, false); mFolder.showItem(item); setLabelSuggestion(nameInfos, instanceId); + mFolder.logFolderLabelState(); invalidate(); }, DROP_IN_ANIMATION_DURATION); } @@ -447,10 +448,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel 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 ee9322bca6..b240f0bef2 100644 --- a/src/com/android/launcher3/logging/StatsLogManager.java +++ b/src/com/android/launcher3/logging/StatsLogManager.java @@ -57,9 +57,9 @@ public class StatsLogManager implements ResourceBasedOverride { + "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 fd024f46f8..096743a8c8 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 android.text.TextUtils.isEmpty; 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.Utilities; 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 class FolderInfo extends ItemInfo { 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 @@ public class FolderInfo extends ItemInfo { @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 @@ public class FolderInfo extends ItemInfo { } /** - * 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 @@ public class FolderInfo extends ItemInfo { return LauncherAtom.ToState.TO_STATE_UNSPECIFIED; } - if (title.equals(previousTitle)) { + if (title.equals(mPreviousTitle)) { return LauncherAtom.ToState.UNCHANGED; } @@ -290,13 +336,13 @@ public class FolderInfo extends ItemInfo { } 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 getSuggestedLabels() { @@ -312,4 +358,112 @@ public class FolderInfo extends ItemInfo { .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 7611ee74b4..f2b7e54a12 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -249,6 +249,13 @@ public class ItemInfo { public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) { } + /** + * 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. */ @@ -345,4 +352,8 @@ public class ItemInfo { itemInfo.copyFrom(this); return itemInfo; } + + public void setTitle(CharSequence title) { + this.title = title; + } }