Merge "Show suggestion when user taps on folder's edit text" into ub-launcher3-master

This commit is contained in:
Hyunyoung Song 2020-01-13 23:18:21 +00:00 committed by Android (Google) Code Review
commit bab1101b87
3 changed files with 68 additions and 18 deletions

View File

@ -44,7 +44,7 @@ public class ExtendedEditText extends EditText {
* Implemented by listeners of the back key. * Implemented by listeners of the back key.
*/ */
public interface OnBackKeyListener { public interface OnBackKeyListener {
public boolean onBackKey(); boolean onBackKey();
} }
private OnBackKeyListener mBackKeyListener; private OnBackKeyListener mBackKeyListener;

View File

@ -297,16 +297,22 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
} }
public void startEditingFolderName() { public void startEditingFolderName() {
post(new Runnable() { post(() -> {
@Override if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
public void run() { if (TextUtils.isEmpty(mFolderName.getText())) {
mFolderName.setHint(""); final String[] suggestedNames = new String[FolderNameProvider.SUGGEST_MAX];
mIsEditingName = true; mLauncher.getFolderNameProvider().getSuggestedFolderName(getContext(),
mInfo.contents, suggestedNames);
mFolderName.setText(suggestedNames[0]);
mFolderName.displayCompletions(Arrays.asList(suggestedNames).subList(1,
suggestedNames.length));
}
} }
mFolderName.setHint("");
mIsEditingName = true;
}); });
} }
@Override @Override
public boolean onBackKey() { public boolean onBackKey() {
// Convert to a string here to ensure that no other state associated with the text field // Convert to a string here to ensure that no other state associated with the text field
@ -316,10 +322,18 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mFolderIcon.onTitleChanged(newTitle); mFolderIcon.onTitleChanged(newTitle);
mLauncher.getModelWriter().updateItemInDatabase(mInfo); mLauncher.getModelWriter().updateItemInDatabase(mInfo);
if (TextUtils.isEmpty(mInfo.title)) { if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mFolderName.setHint(R.string.folder_hint_text); mFolderName.setText(mInfo.title);
// TODO: depending on whether the title was manually edited or automatically
// suggested, apply different hint.
mFolderName.setHint("");
} else { } else {
mFolderName.setHint(null); if (TextUtils.isEmpty(mInfo.title)) {
mFolderName.setHint(R.string.folder_hint_text);
mFolderName.setText("");
} else {
mFolderName.setHint(null);
}
} }
sendCustomAccessibilityEvent( sendCustomAccessibilityEvent(
@ -403,7 +417,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
mFolderName.setHint(null); mFolderName.setHint(null);
} else { } else {
mFolderName.setText(""); mFolderName.setText("");
mFolderName.setHint(R.string.folder_hint_text); if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mFolderName.setHint("");
} else {
mFolderName.setHint(R.string.folder_hint_text);
}
} }
// In case any children didn't come across during loading, clean up the folder accordingly // In case any children didn't come across during loading, clean up the folder accordingly
mFolderIcon.post(() -> { mFolderIcon.post(() -> {
@ -420,7 +438,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (FeatureFlags.FOLDER_NAME_SUGGEST.get() if (FeatureFlags.FOLDER_NAME_SUGGEST.get()
&& TextUtils.isEmpty(mFolderName.getText().toString())) { && TextUtils.isEmpty(mFolderName.getText().toString())) {
if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) { if (suggestName.length > 0 && !TextUtils.isEmpty(suggestName[0])) {
mFolderName.setHint(suggestName[0]); mFolderName.setHint("");
mFolderName.setText(suggestName[0]); mFolderName.setText(suggestName[0]);
mInfo.title = suggestName[0]; mInfo.title = suggestName[0];
animateOpen(mInfo.contents, 0, true); animateOpen(mInfo.contents, 0, true);
@ -534,6 +552,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
openFolder.close(true); openFolder.close(true);
} }
if (FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
mLauncher.getFolderNameProvider().load(getContext());
}
mContent.bindItems(items); mContent.bindItems(items);
centerAboutIcon(); centerAboutIcon();
mItemsInvalidated = true; mItemsInvalidated = true;
@ -1350,6 +1371,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
return itemsOnCurrentPage; return itemsOnCurrentPage;
} }
@Override
public void onFocusChange(View v, boolean hasFocus) { public void onFocusChange(View v, boolean hasFocus) {
if (v == mFolderName) { if (v == mFolderName) {
if (hasFocus) { if (hasFocus) {

View File

@ -18,13 +18,16 @@ package com.android.launcher3.folder;
import android.content.Context; import android.content.Context;
import android.os.Process; import android.os.Process;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.AppInfo; import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo; import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -38,16 +41,26 @@ import java.util.stream.Collectors;
*/ */
public class FolderNameProvider { public class FolderNameProvider {
private static final String TAG = FeatureFlags.FOLDER_NAME_SUGGEST.getKey();
private static final boolean DEBUG = FeatureFlags.FOLDER_NAME_SUGGEST.get();
/** /**
* IME usually has up to 3 suggest slots. In total, there are 4 suggest slots as the folder * IME usually has up to 3 suggest slots. In total, there are 4 suggest slots as the folder
* name edit box can also be used to provide suggestion. * name edit box can also be used to provide suggestion.
*/ */
public static final int SUGGEST_MAX = 4; public static final int SUGGEST_MAX = 4;
/**
* When inheriting class requires precaching, override this method.
*/
public void load(Context context) {}
public CharSequence getSuggestedFolderName(Context context, public CharSequence getSuggestedFolderName(Context context,
ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) { ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
CharSequence suggest; if (DEBUG) {
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
}
// If all the icons are from work profile, // If all the icons are from work profile,
// Then, suggest "Work" as the folder name // Then, suggest "Work" as the folder name
List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream() List<WorkspaceItemInfo> distinctItemInfos = workspaceItemInfos.stream()
@ -75,19 +88,28 @@ public class FolderNameProvider {
// Place it as first viable suggestion and shift everything else // Place it as first viable suggestion and shift everything else
info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString())); info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
} }
if (DEBUG) {
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
}
return candidates[0]; return candidates[0];
} }
private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) { private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
for (int i = candidatesOut.length - 1; i > 0; i--) { if (contains(candidatesOut, candidate)) {
if (TextUtils.isEmpty(candidatesOut[i])) { return;
candidatesOut[i - 1] = candidatesOut[i];
}
candidatesOut[0] = candidate;
} }
for (int i = candidatesOut.length - 1; i > 0; i--) {
if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
candidatesOut[i] = candidatesOut[i - 1];
}
}
candidatesOut[0] = candidate;
} }
private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) { private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
if (contains(candidatesOut, candidate)) {
return;
}
for (int i = 0; i < candidate.length(); i++) { for (int i = 0; i < candidate.length(); i++) {
if (TextUtils.isEmpty(candidatesOut[i])) { if (TextUtils.isEmpty(candidatesOut[i])) {
candidatesOut[i] = candidate; candidatesOut[i] = candidate;
@ -95,6 +117,12 @@ public class FolderNameProvider {
} }
} }
private boolean contains(CharSequence[] list, CharSequence key) {
return Arrays.asList(list).stream()
.filter(s -> s != null)
.anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
}
// This method can be moved to some Utility class location. // This method can be moved to some Utility class location.
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) { private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> map = new ConcurrentHashMap<>(); Map<Object, Boolean> map = new ConcurrentHashMap<>();