diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto index d1185bd5bc..98ce9af2d6 100644 --- a/protos/launcher_atom.proto +++ b/protos/launcher_atom.proto @@ -96,8 +96,21 @@ enum Attribute { ADD_TO_HOMESCREEN = 6; // play install + launcher home setting ALLAPPS_PREDICTION = 7; // from prediction bar in all apps container HOTSEAT_PREDICTION = 8; // from prediction bar in hotseat container - SUGGESTED_LABEL = 9; // folder icon's label was suggested - MANUAL_LABEL = 10; // folder icon's label was manually edited + + // Folder's label is one of the non-empty suggested values. + SUGGESTED_LABEL = 9; + + // Folder's label is non-empty, manually entered by the user + // and different from any of suggested values. + MANUAL_LABEL = 10; + + // Folder's label is not yet assigned( i.e., title == null). + // Eligible for auto-labeling. + UNLABELED = 11; + + // Folder's label is empty(i.e., title == ""). + // Not eligible for auto-labeling. + EMPTY_LABEL = 12; } // Main app icons 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 c1bf2fd960..a1218aeb86 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 @@ -114,9 +114,9 @@ public class HotseatEduController { if (!putIntoFolder.isEmpty()) { ItemInfo firstItem = putIntoFolder.get(0); FolderInfo folderInfo = new FolderInfo(); - folderInfo.setTitle(""); mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container, firstItem.screenId, firstItem.cellX, firstItem.cellY); + folderInfo.setTitle("", mLauncher.getModelWriter()); folderInfo.contents.addAll(putIntoFolder); for (int i = 0; i < folderInfo.contents.size(); i++) { ItemInfo item = folderInfo.contents.get(i); diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java index 4af3544fde..d01e189f7e 100644 --- a/src/com/android/launcher3/folder/Folder.java +++ b/src/com/android/launcher3/folder/Folder.java @@ -26,7 +26,6 @@ import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTI 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 android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -339,11 +338,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo if (DEBUG) { Log.d(TAG, "onBackKey newTitle=" + newTitle); } - mInfo.setTitle(newTitle); - mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(), - mLauncher.getModelWriter()); + mInfo.setTitle(newTitle, mLauncher.getModelWriter()); mFolderIcon.onTitleChanged(newTitle); - mLauncher.getModelWriter().updateItemInDatabase(mInfo); if (TextUtils.isEmpty(mInfo.title)) { mFolderName.setHint(R.string.folder_hint_text); diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java index 152fd370b8..75275b20e5 100644 --- a/src/com/android/launcher3/folder/FolderIcon.java +++ b/src/com/android/launcher3/folder/FolderIcon.java @@ -16,8 +16,6 @@ package com.android.launcher3.folder; -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_AUTO_LABELED; @@ -72,6 +70,7 @@ 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; +import com.android.launcher3.model.data.FolderInfo.LabelState; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.touch.ItemClickHandler; @@ -443,8 +442,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) { return; } - if (!isEmpty(mFolderName.getText().toString()) - || mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) { + if (!mInfo.getLabelState().equals(LabelState.UNLABELED)) { return; } if (nameInfos == null || !nameInfos.hasSuggestions()) { @@ -464,10 +462,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel CharSequence newTitle = nameInfos.getLabels()[0]; FromState fromState = mInfo.getFromLabelState(); - mInfo.setTitle(newTitle); + mInfo.setTitle(newTitle, mFolder.mLauncher.getModelWriter()); onTitleChanged(mInfo.title); mFolder.mFolderName.setText(mInfo.title); - mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo); // Logging for folder creation flow StatsLogManager.newInstance(getContext()).logger() diff --git a/src/com/android/launcher3/model/data/FolderInfo.java b/src/com/android/launcher3/model/data/FolderInfo.java index ecd18ce268..05ce06a3f7 100644 --- a/src/com/android/launcher3/model/data/FolderInfo.java +++ b/src/com/android/launcher3/model/data/FolderInfo.java @@ -22,6 +22,7 @@ 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.logger.LauncherAtom.Attribute.EMPTY_LABEL; import static com.android.launcher3.logger.LauncherAtom.Attribute.MANUAL_LABEL; import static com.android.launcher3.logger.LauncherAtom.Attribute.SUGGESTED_LABEL; import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM; @@ -31,11 +32,14 @@ import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolder import android.os.Process; +import androidx.annotation.Nullable; + import com.android.launcher3.LauncherSettings; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.folder.FolderNameInfos; import com.android.launcher3.logger.LauncherAtom; +import com.android.launcher3.logger.LauncherAtom.Attribute; import com.android.launcher3.logger.LauncherAtom.FromState; import com.android.launcher3.logger.LauncherAtom.ToState; import com.android.launcher3.model.ModelWriter; @@ -74,6 +78,30 @@ public class FolderInfo extends ItemInfo { public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008; + /** + * Different states of folder label. + */ + public enum LabelState { + // Folder's label is not yet assigned( i.e., title == null). Eligible for auto-labeling. + UNLABELED(Attribute.UNLABELED), + + // Folder's label is empty(i.e., title == ""). Not eligible for auto-labeling. + EMPTY(EMPTY_LABEL), + + // Folder's label is one of the non-empty suggested values. + SUGGESTED(SUGGESTED_LABEL), + + // Folder's label is non-empty, manually entered by the user + // and different from any of suggested values. + MANUAL(MANUAL_LABEL); + + private final LauncherAtom.Attribute mLogAttribute; + + LabelState(Attribute logAttribute) { + this.mLogAttribute = logAttribute; + } + } + public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest"; public int options; @@ -176,8 +204,7 @@ public class FolderInfo extends ItemInfo { @Override protected String dumpProperties() { - return super.dumpProperties() - + " manuallyTypedTitle=" + hasOption(FLAG_MANUAL_FOLDER_NAME); + return String.format("%s; labelState=%s", super.dumpProperties(), getLabelState()); } @Override @@ -185,14 +212,41 @@ public class FolderInfo extends ItemInfo { return getDefaultItemInfoBuilder() .setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size())) .setRank(rank) - .setAttribute(hasOption(FLAG_MANUAL_FOLDER_NAME) ? MANUAL_LABEL : SUGGESTED_LABEL) + .setAttribute(getLabelState().mLogAttribute) .setContainerInfo(getContainerInfo()) .build(); } @Override - public void setTitle(CharSequence title) { + public void setTitle(@Nullable CharSequence title, ModelWriter modelWriter) { + // Updating label from null to empty is considered as false touch. + // Retaining null title(ie., UNLABELED state) allows auto-labeling when new items added. + if (isEmpty(title) && this.title == null) { + return; + } + + // Updating title to same value does not change any states. + if (title != null && title == this.title) { + return; + } + this.title = title; + LabelState newLabelState = + title == null ? LabelState.UNLABELED + : title.length() == 0 ? LabelState.EMPTY : + getAcceptedSuggestionIndex().isPresent() ? LabelState.SUGGESTED + : LabelState.MANUAL; + setOption(FLAG_MANUAL_FOLDER_NAME, newLabelState.equals(LabelState.MANUAL), modelWriter); + } + + /** + * Returns current state of the current folder label. + */ + public LabelState getLabelState() { + return title == null ? LabelState.UNLABELED + : title.length() == 0 ? LabelState.EMPTY : + hasOption(FLAG_MANUAL_FOLDER_NAME) ? LabelState.MANUAL + : LabelState.SUGGESTED; } @Override @@ -233,13 +287,17 @@ public class FolderInfo extends ItemInfo { * 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; + switch (getLabelState()){ + case EMPTY: + return LauncherAtom.FromState.FROM_EMPTY; + case MANUAL: + return LauncherAtom.FromState.FROM_CUSTOM; + case SUGGESTED: + return LauncherAtom.FromState.FROM_SUGGESTED; + case UNLABELED: + default: + return LauncherAtom.FromState.FROM_STATE_UNSPECIFIED; + } } /** diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 66c3cbbe27..3082b6e341 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -52,6 +52,7 @@ import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import com.android.launcher3.logger.LauncherAtom.SettingsContainer; import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; +import com.android.launcher3.model.ModelWriter; import com.android.launcher3.util.ContentWriter; import java.util.Optional; @@ -405,7 +406,10 @@ public class ItemInfo { return itemInfo; } - public void setTitle(CharSequence title) { + /** + * Sets the title of the item and writes to DB model if needed. + */ + public void setTitle(CharSequence title, ModelWriter modelWriter) { this.title = title; } }