Introduce FolderNameInfo class.
* Introduce FolderNameInfo class for passing down the folder name
suggestions from FolderNameProvider.
* Use FolderNameInfo for storing the serialized suggested names for
Folders. It is parsed and used in FolderEdit.
Bug: 148417030
Bug: 148916551
Bug: 148432151
Change-Id: Idaa81e203cc42889be15d0845230b4508521041c
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 787eee1..336e423 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -48,6 +48,8 @@
public static final int FLAG_MANUAL_FOLDER_NAME = 0x00000008;
+ public static final String EXTRA_FOLDER_SUGGESTIONS = "suggest";
+
public int options;
public Intent suggestedFolderNames;
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 024c7dd..0222b57 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -89,6 +89,8 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -301,12 +303,12 @@
post(() -> {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
if (TextUtils.isEmpty(mFolderName.getText())) {
- String[] suggestedNames =
- mInfo.suggestedFolderNames.getStringArrayExtra("suggest");
- mFolderName.setText(suggestedNames[0]);
- mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
- suggestedNames.length));
- mFolderName.setEnteredCompose(false);
+ FolderNameInfo[] nameInfos =
+ (FolderNameInfo[]) mInfo.suggestedFolderNames.getParcelableArrayExtra(
+ FolderInfo.EXTRA_FOLDER_SUGGESTIONS);
+ if (nameInfos != null) {
+ showLabelSuggestion(nameInfos);
+ }
}
}
mFolderName.setHint("");
@@ -443,27 +445,53 @@
}
/**
- * Show suggested folder title.
+ * Show suggested folder title in FolderEditText, push InputMethodManager suggestions and save
+ * the suggestedFolderNames.
*/
- public void showSuggestedTitle(String[] suggestName) {
+ public void showSuggestedTitle(FolderNameInfo[] nameInfos) {
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
- mInfo.suggestedFolderNames = new Intent().putExtra("suggest", suggestName);
+ mInfo.suggestedFolderNames = new Intent().putExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS,
+ nameInfos);
if (TextUtils.isEmpty(mFolderName.getText().toString())
&& !mInfo.hasOption(FolderInfo.FLAG_MANUAL_FOLDER_NAME)) {
- if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
- mFolderName.setHint("");
- mFolderName.setText(suggestName[0]);
- mInfo.title = suggestName[0];
- animateOpen(mInfo.contents, 0, true);
- mFolderName.showKeyboard();
- mFolderName.displayCompletions(
- Arrays.asList(suggestName).subList(1, suggestName.length));
- }
+ showLabelSuggestion(nameInfos);
}
}
}
/**
+ * Show suggested folder title in FolderEditText if the first suggestion is non-empty, push
+ * InputMethodManager suggestions.
+ */
+ private void showLabelSuggestion(FolderNameInfo[] nameInfos) {
+ if (nameInfos == null) {
+ return;
+ }
+ // Open the Folder and Keyboard when the first or second suggestion is valid non-empty
+ // string.
+ boolean shouldOpen = nameInfos.length > 0 && nameInfos[0] != null && !TextUtils.isEmpty(
+ nameInfos[0].getLabel())
+ || nameInfos.length > 1 && nameInfos[1] != null && !TextUtils.isEmpty(
+ nameInfos[1].getLabel());
+ CharSequence firstLabel = nameInfos[0].getLabel();
+
+ if (shouldOpen) {
+ if (!TextUtils.isEmpty(firstLabel)) {
+ mFolderName.setHint("");
+ mFolderName.setText(firstLabel);
+ mInfo.title = firstLabel;
+ }
+ animateOpen(mInfo.contents, 0, true);
+ mFolderName.showKeyboard();
+ mFolderName.displayCompletions(
+ Arrays.asList(nameInfos).subList(1, nameInfos.length).stream()
+ .filter(Objects::nonNull)
+ .map(s -> s.getLabel().toString())
+ .collect(Collectors.toList()));
+ }
+ }
+
+ /**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
* @param launcher The main activity.
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 6a47b98..ab1ff10 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -393,15 +393,16 @@
if (!itemAdded) mPreviewItemManager.hidePreviewItem(index, true);
final int finalIndex = index;
- String[] suggestedNameOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] nameInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
Executors.UI_HELPER_EXECUTOR.post(() -> {
d.folderNameProvider.getSuggestedFolderName(
- getContext(), mInfo.contents, suggestedNameOut);
- showFinalView(finalIndex, item, suggestedNameOut);
+ getContext(), mInfo.contents, nameInfos);
+ showFinalView(finalIndex, item, nameInfos);
});
} else {
- showFinalView(finalIndex, item, suggestedNameOut);
+ showFinalView(finalIndex, item, nameInfos);
}
} else {
addItem(item);
@@ -409,12 +410,12 @@
}
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
- String[] suggestedNameOut) {
+ FolderNameInfo[] nameInfos) {
postDelayed(() -> {
mPreviewItemManager.hidePreviewItem(finalIndex, false);
mFolder.showItem(item);
invalidate();
- mFolder.showSuggestedTitle(suggestedNameOut);
+ mFolder.showSuggestedTitle(nameInfos);
}, DROP_IN_ANIMATION_DURATION);
}
diff --git a/src/com/android/launcher3/folder/FolderNameInfo.java b/src/com/android/launcher3/folder/FolderNameInfo.java
new file mode 100644
index 0000000..eb9da90
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderNameInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.launcher3.folder;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Information about a single label suggestions of the Folder.
+ */
+
+public final class FolderNameInfo implements Parcelable {
+ private final double mScore;
+ private final CharSequence mLabel;
+
+ /**
+ * Create a simple completion with label.
+ *
+ * @param label The text that should be inserted into the editor and pushed to
+ * InputMethodManager suggestions.
+ * @param score The score for the label between 0.0 and 1.0.
+ */
+ public FolderNameInfo(CharSequence label, double score) {
+ mScore = score;
+ mLabel = label;
+ }
+
+ private FolderNameInfo(Parcel source) {
+ mScore = source.readDouble();
+ mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeDouble(mScore);
+ TextUtils.writeToParcel(mLabel, dest, flags);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @NonNull
+ public static final Parcelable.Creator<FolderNameInfo> CREATOR =
+ new Parcelable.Creator<FolderNameInfo>() {
+ public FolderNameInfo createFromParcel(Parcel source) {
+ return new FolderNameInfo(source);
+ }
+
+ public FolderNameInfo[] newArray(int size) {
+ return new FolderNameInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/src/com/android/launcher3/folder/FolderNameProvider.java b/src/com/android/launcher3/folder/FolderNameProvider.java
index 957e636..d5990fa 100644
--- a/src/com/android/launcher3/folder/FolderNameProvider.java
+++ b/src/com/android/launcher3/folder/FolderNameProvider.java
@@ -30,6 +30,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
@@ -60,29 +61,33 @@
return fnp;
}
- public CharSequence getSuggestedFolderName(Context context,
- ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
+ /**
+ * Generate and rank the suggested Folder names.
+ */
+ public void getSuggestedFolderName(Context context,
+ ArrayList<WorkspaceItemInfo> workspaceItemInfos,
+ FolderNameInfo[] nameInfos) {
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
// If all the icons are from work profile,
// Then, suggest "Work" as the folder name
List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.user))
+ .filter(distinctByKey(p -> p.user))
.collect(Collectors.toList());
if (distinctItemInfos.size() == 1
&& !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
// Place it as last viable suggestion
- setAsLastSuggestion(candidates,
+ setAsLastSuggestion(nameInfos,
context.getResources().getString(R.string.work_folder_name));
}
// If all the icons are from same package (e.g., main icon, shortcut, shortcut)
// Then, suggest the package's title as the folder name
distinctItemInfos = workspaceItemInfos.stream()
- .filter(distinctByKey(p-> p.getTargetComponent() != null
+ .filter(distinctByKey(p -> p.getTargetComponent() != null
? p.getTargetComponent().getPackageName() : ""))
.collect(Collectors.toList());
@@ -91,44 +96,46 @@
.getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
.getPackageName());
// Place it as first viable suggestion and shift everything else
- info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
+ info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
}
if (DEBUG) {
- Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
+ Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(nameInfos));
}
- return candidates[0];
}
- private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private void setAsFirstSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = candidatesOut.length - 1; i > 0; i--) {
- if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
- candidatesOut[i] = candidatesOut[i - 1];
+ for (int i = nameInfos.length - 1; i > 0; i--) {
+ if (nameInfos[i - 1] != null && !TextUtils.isEmpty(nameInfos[i - 1].getLabel())) {
+ nameInfos[i] = nameInfos[i - 1];
}
}
- candidatesOut[0] = candidate;
+ nameInfos[0] = new FolderNameInfo(label, 1.0);
}
- private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
- if (contains(candidatesOut, candidate)) {
+ private void setAsLastSuggestion(FolderNameInfo[] nameInfos, CharSequence label) {
+ if (nameInfos.length == 0 || contains(nameInfos, label)) {
return;
}
- for (int i = 0; i < candidate.length(); i++) {
- if (TextUtils.isEmpty(candidatesOut[i])) {
- candidatesOut[i] = candidate;
+ for (int i = 0; i < nameInfos.length; i++) {
+ if (nameInfos[i] == null || TextUtils.isEmpty(nameInfos[i].getLabel())) {
+ nameInfos[i] = new FolderNameInfo(label, 1.0);
return;
}
}
- candidatesOut[candidate.length() - 1] = candidate;
+ // Overwrite the last suggestion.
+ int lastIndex = nameInfos.length - 1;
+ nameInfos[lastIndex] = new FolderNameInfo(label, 1.0);
}
- private boolean contains(CharSequence[] list, CharSequence key) {
- return Arrays.asList(list).stream()
- .filter(s -> s != null)
- .anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
+ private boolean contains(FolderNameInfo[] nameInfos, CharSequence label) {
+ return Arrays.stream(nameInfos)
+ .filter(Objects::nonNull)
+ .anyMatch(nameInfo -> nameInfo.getLabel().toString().equalsIgnoreCase(
+ label.toString()));
}
// This method can be moved to some Utility class location.
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 8dc7a3a..8717c23 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -59,6 +59,7 @@
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
+import com.android.launcher3.folder.FolderNameInfo;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.ComponentWithLabelAndIcon.ComponentWithIconCachingLogic;
@@ -925,11 +926,13 @@
synchronized (mBgDataModel) {
for (int i = 0; i < mBgDataModel.folders.size(); i++) {
- String[] suggestedOut = new String[FolderNameProvider.SUGGEST_MAX];
+ FolderNameInfo[] suggestionInfos =
+ new FolderNameInfo[FolderNameProvider.SUGGEST_MAX];
FolderInfo info = mBgDataModel.folders.valueAt(i);
if (info.suggestedFolderNames == null) {
- provider.getSuggestedFolderName(mApp.getContext(), info.contents, suggestedOut);
- info.suggestedFolderNames = new Intent().putExtra("suggest", suggestedOut);
+ provider.getSuggestedFolderName(mApp.getContext(), info.contents,
+ suggestionInfos);
+ info.suggestedFolderNames = new Intent().putExtra("suggest", suggestionInfos);
}
}
}