Adds LAUNCHER_FOLDER_LABEL_CHANGED event.
Sample Log: https://docs.google.com/document/d/1CBP2yTcXdFhPdNG5ZmWFKSgd8mDbMevY-akVlUXPLDo/edit#bookmark=id.qwjknn6acmx6
Bug: 155410872
Bug: 152978018
Change-Id: Ib7641d3d42a3f4fd002d1dbb36dc4b9ea0f885fc
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cac2d8f..19f7213 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -95,7 +95,17 @@
// Represents folder in a closed state.
message FolderIcon {
+ // Number of items inside folder.
optional int32 cardinality = 1;
+
+ // State of the folder label before the event.
+ optional FromState from_state = 2;
+
+ // State of the folder label after the event.
+ optional ToState to_state = 3;
+
+ // Populated only when folder label was suggested.
+ optional string label = 4;
}
//////////////////////////////////////////////
@@ -120,3 +130,71 @@
HotseatContainer hotseat = 5;
}
}
+
+// Represents state of FolderLabel before editing.
+enum FromState {
+ // Default value.
+ FROM_STATE_UNSPECIFIED = 0;
+
+ // FolderLabel was empty.
+ FROM_EMPTY = 1;
+
+ // FolderLabel was non-empty and manually entered by the user.
+ FROM_CUSTOM = 2;
+
+ // FolderLabel was non-empty and one of the suggestions.
+ FROM_SUGGESTED = 3;
+}
+
+// Represents state of FolderLabel after editing.
+enum ToState {
+ // Default value.
+ TO_STATE_UNSPECIFIED = 0;
+ // User attempted to change the folder label, 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.
+ TO_SUGGESTION1_WITH_VALID_PRIMARY = 3;
+
+ // New label 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.
+ TO_SUGGESTION2_WITH_VALID_PRIMARY = 5;
+
+ // New label 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.
+ TO_SUGGESTION3_WITH_VALID_PRIMARY = 7;
+
+ // New label 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.
+ TO_EMPTY_WITH_VALID_PRIMARY = 9;
+
+ // New label 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.
+ TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 11;
+
+ // New label 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.
+ TO_CUSTOM_WITH_VALID_PRIMARY = 13;
+
+ // New label 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.
+ TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 15;
+
+ // New label is non-empty and also suggestions feature was disable.
+ TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 16;
+}
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 9a36b3e..29a737c 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -18,23 +18,15 @@
import static android.text.TextUtils.isEmpty;
-import static androidx.core.util.Preconditions.checkNotNull;
-
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
-import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
-import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
import static java.util.Arrays.asList;
-import static java.util.Arrays.stream;
import static java.util.Optional.ofNullable;
import android.animation.Animator;
@@ -94,12 +86,6 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pageindicators.PageIndicatorDots;
-import com.android.launcher3.userevent.LauncherLogProto.Action;
-import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.LauncherLogProto.ItemType;
-import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
-import com.android.launcher3.userevent.LauncherLogProto.Target;
-import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.Thunk;
@@ -111,10 +97,7 @@
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
-import java.util.OptionalInt;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -213,8 +196,6 @@
@Thunk int mScrollHintDir = SCROLL_NONE;
@Thunk int mCurrentScrollDir = SCROLL_NONE;
- private String mPreviousLabel;
- private boolean mIsPreviousLabelSuggested;
/**
* Used to inflate the Workspace from XML.
@@ -348,9 +329,9 @@
if (DEBUG) {
Log.d(TAG, "onBackKey newTitle=" + newTitle);
}
-
+ mInfo.previousTitle = mInfo.title;
mInfo.title = newTitle;
- mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !getAcceptedSuggestionIndex().isPresent(),
+ mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
mLauncher.getModelWriter());
mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@@ -441,8 +422,6 @@
}
mItemsInvalidated = true;
mInfo.addListener(this);
- Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString());
- mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
if (!isEmpty(mInfo.title)) {
mFolderName.setText(mInfo.title);
@@ -1455,7 +1434,6 @@
if (hasFocus) {
startEditingFolderName();
} else {
- logCurrentFolderLabelState();
mFolderName.dispatchBackKey();
}
}
@@ -1653,148 +1631,4 @@
public FolderPagedView getContent() {
return mContent;
}
-
- protected void logCurrentFolderLabelState() {
- LauncherEvent launcherEvent = LauncherEvent.newBuilder()
- .setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
- .addSrcTarget(newEditTextTargetBuilder()
- .setFromFolderLabelState(getFromFolderLabelState())
- .setToFolderLabelState(getToFolderLabelState()))
- .addSrcTarget(newFolderTargetBuilder())
- .addSrcTarget(newParentContainerTarget())
- .build();
- mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
- mPreviousLabel = mFolderName.getText().toString();
- mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
- }
-
- private Target.FromFolderLabelState getFromFolderLabelState() {
- return mPreviousLabel == null
- ? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
- : mPreviousLabel.isEmpty()
- ? FROM_EMPTY
- : mIsPreviousLabelSuggested
- ? FROM_SUGGESTED
- : FROM_CUSTOM;
- }
-
- private Target.ToFolderLabelState getToFolderLabelState() {
- String newLabel =
- checkNotNull(mFolderName.getText().toString(),
- "Expected valid folder label, but found null");
- if (newLabel.equals(mPreviousLabel)) {
- return Target.ToFolderLabelState.UNCHANGED;
- }
-
- if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED
- : ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
- }
-
- Optional<String[]> suggestedLabels = getSuggestedLabels();
- boolean isEmptySuggestions = suggestedLabels
- .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
- .orElse(true);
- if (isEmptySuggestions) {
- return newLabel.isEmpty()
- ? ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS
- : ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
- }
-
- boolean hasValidPrimary = suggestedLabels
- .map(labels -> !isEmpty(labels[0]))
- .orElse(false);
- if (newLabel.isEmpty()) {
- return hasValidPrimary ? ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
- if (!accepted_suggestion_index.isPresent()) {
- return hasValidPrimary ? ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
- }
-
- switch (accepted_suggestion_index.getAsInt()) {
- case 0:
- return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
- case 1:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
- case 2:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
- case 3:
- return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY
- : ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
- default:
- // fall through
- }
- return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
-
- }
-
- private Optional<String[]> getSuggestedLabels() {
- return ofNullable(mInfo)
- .map(info -> info.suggestedFolderNames)
- .map(
- folderNames ->
- (FolderNameInfo[])
- folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
- .map(
- folderNameInfoArray ->
- stream(folderNameInfoArray)
- .filter(Objects::nonNull)
- .map(FolderNameInfo::getLabel)
- .filter(Objects::nonNull)
- .map(CharSequence::toString)
- .toArray(String[]::new));
- }
-
- private OptionalInt getAcceptedSuggestionIndex() {
- String newLabel = checkNotNull(mFolderName.getText().toString(),
- "Expected valid folder label, but found null");
- return getSuggestedLabels()
- .map(suggestionsArray ->
- IntStream.range(0, suggestionsArray.length)
- .filter(
- index -> !isEmpty(suggestionsArray[index])
- && newLabel.equalsIgnoreCase(suggestionsArray[index]))
- .sequential()
- .findFirst()
- ).orElse(OptionalInt.empty());
-
- }
-
-
- private Target.Builder newEditTextTargetBuilder() {
- return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
- }
-
- private Target.Builder newFolderTargetBuilder() {
- return Target.newBuilder()
- .setType(Target.Type.CONTAINER)
- .setContainerType(ContainerType.FOLDER)
- .setPageIndex(mInfo.screenId)
- .setGridX(mInfo.cellX)
- .setGridY(mInfo.cellY)
- .setCardinality(mInfo.contents.size());
- }
-
- private Target.Builder newParentContainerTarget() {
- Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
- switch (mInfo.container) {
- case CONTAINER_HOTSEAT:
- return builder.setContainerType(ContainerType.HOTSEAT);
- case CONTAINER_DESKTOP:
- return builder.setContainerType(ContainerType.WORKSPACE);
- default:
- throw new AssertionError(String
- .format("Expected container to be either %s or %s but found %s.",
- CONTAINER_HOTSEAT,
- CONTAINER_DESKTOP,
- mInfo.container));
- }
- }
}
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 93208d4..bb358ab 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -20,6 +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 android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -62,6 +63,8 @@
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.icons.DotRenderer;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.FolderInfo.FolderListener;
@@ -410,10 +413,10 @@
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
getContext(), mInfo.contents, nameInfos);
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
});
} else {
- showFinalView(finalIndex, item, nameInfos);
+ showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
}
} else {
addItem(item);
@@ -421,12 +424,11 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- FolderNameInfo[] nameInfos) {
+ FolderNameInfo[] nameInfos, InstanceId instanceId) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
- setLabelSuggestion(nameInfos);
- mFolder.logCurrentFolderLabelState();
+ setLabelSuggestion(nameInfos, instanceId);
invalidate();
}, DROP_IN_ANIMATION_DURATION);
}
@@ -434,7 +436,7 @@
/**
* Set the suggested folder name.
*/
- public void setLabelSuggestion(FolderNameInfo[] nameInfos) {
+ public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
return;
}
@@ -445,7 +447,10 @@
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
return;
}
+ mInfo.previousTitle = mInfo.title;
mInfo.title = nameInfos[0].getLabel();
+ StatsLogManager.newInstance(getContext())
+ .log(LAUNCHER_FOLDER_LABEL_CHANGED, instanceId, mInfo.getFolderIconAtom());
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 9455bd3..309263d 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -61,6 +61,10 @@
+ "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 = "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 3ac6a22..fd024f4 100644
--- a/src/com/android/launcher3/model/data/FolderInfo.java
+++ b/src/com/android/launcher3/model/data/FolderInfo.java
@@ -16,16 +16,31 @@
package com.android.launcher3.model.data;
+import static android.text.TextUtils.isEmpty;
+
+import static androidx.core.util.Preconditions.checkNotNull;
+
+import static java.util.Arrays.stream;
+import static java.util.Optional.ofNullable;
+
import android.content.Intent;
import android.os.Process;
+import android.text.TextUtils;
import com.android.launcher3.LauncherSettings;
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.model.ModelWriter;
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.stream.IntStream;
+
/**
* Represents a folder containing shortcuts or apps.
@@ -57,6 +72,10 @@
public Intent suggestedFolderNames;
+ // When title changes, previous title is stored.
+ // Primarily used for logging purpose.
+ public CharSequence previousTitle;
+
/**
* The apps and shortcuts
*/
@@ -172,4 +191,125 @@
folderInfo.contents = this.contents;
return folderInfo;
}
+
+ /**
+ * Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging
+ * into Westworld.
+ *
+ */
+ public LauncherAtom.ItemInfo getFolderIconAtom() {
+ LauncherAtom.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());
+ }
+ return getDefaultItemInfoBuilder()
+ .setFolderIcon(folderIconBuilder)
+ .setContainerInfo(getContainerInfo())
+ .build();
+ }
+
+ /**
+ * Returns index of the accepted suggestion.
+ */
+ public OptionalInt getAcceptedSuggestionIndex() {
+ String newLabel = checkNotNull(title,
+ "Expected valid folder label, but found null").toString();
+ return getSuggestedLabels()
+ .map(suggestionsArray ->
+ IntStream.range(0, suggestionsArray.length)
+ .filter(
+ index -> !isEmpty(suggestionsArray[index])
+ && newLabel.equalsIgnoreCase(
+ suggestionsArray[index]))
+ .sequential()
+ .findFirst()
+ ).orElse(OptionalInt.empty());
+
+ }
+
+ private LauncherAtom.ToState getToFolderLabelState() {
+ if (title == null) {
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+ }
+
+ if (title.equals(previousTitle)) {
+ return LauncherAtom.ToState.UNCHANGED;
+ }
+
+ if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
+ : LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
+ }
+
+ Optional<String[]> suggestedLabels = getSuggestedLabels();
+ boolean isEmptySuggestions = suggestedLabels
+ .map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
+ .orElse(true);
+ if (isEmptySuggestions) {
+ return title.length() > 0
+ ? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
+ : LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
+ }
+
+ boolean hasValidPrimary = suggestedLabels
+ .map(labels -> !isEmpty(labels[0]))
+ .orElse(false);
+ if (title.length() == 0) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
+ if (!accepted_suggestion_index.isPresent()) {
+ return hasValidPrimary ? LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
+ }
+
+ switch (accepted_suggestion_index.getAsInt()) {
+ case 0:
+ return LauncherAtom.ToState.TO_SUGGESTION0;
+ case 1:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION1_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
+ case 2:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION2_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
+ case 3:
+ return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION3_WITH_VALID_PRIMARY
+ : LauncherAtom.ToState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
+ default:
+ // fall through
+ }
+ return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
+
+ }
+
+ private LauncherAtom.FromState getFromFolderLabelState() {
+ return previousTitle == 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;
+ }
+
+ private Optional<String[]> getSuggestedLabels() {
+ return ofNullable(suggestedFolderNames)
+ .map(folderNames ->
+ (FolderNameInfo[])
+ folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
+ .map(folderNameInfoArray ->
+ stream(folderNameInfoArray)
+ .filter(Objects::nonNull)
+ .map(FolderNameInfo::getLabel)
+ .filter(Objects::nonNull)
+ .map(CharSequence::toString)
+ .toArray(String[]::new));
+ }
}