2019-10-10 23:11:12 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 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.content.Context;
|
2019-12-25 13:51:28 -08:00
|
|
|
import android.os.Process;
|
|
|
|
|
import android.text.TextUtils;
|
2020-01-10 23:37:16 -08:00
|
|
|
import android.util.Log;
|
2019-10-10 23:11:12 -07:00
|
|
|
|
2019-12-25 13:51:28 -08:00
|
|
|
import com.android.launcher3.AppInfo;
|
|
|
|
|
import com.android.launcher3.LauncherAppState;
|
|
|
|
|
import com.android.launcher3.R;
|
2019-10-10 23:11:12 -07:00
|
|
|
import com.android.launcher3.WorkspaceItemInfo;
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
2020-01-10 23:37:16 -08:00
|
|
|
import java.util.Arrays;
|
2019-12-25 13:51:28 -08:00
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
import java.util.function.Function;
|
|
|
|
|
import java.util.function.Predicate;
|
|
|
|
|
import java.util.stream.Collectors;
|
2019-10-10 23:11:12 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Locates provider for the folder name.
|
|
|
|
|
*/
|
|
|
|
|
public class FolderNameProvider {
|
|
|
|
|
|
2020-01-21 11:59:19 -08:00
|
|
|
private static final String TAG = "FolderNameProvider";
|
2020-01-24 11:23:15 -08:00
|
|
|
private static final boolean DEBUG = false;
|
2020-01-10 23:37:16 -08:00
|
|
|
|
2019-11-06 21:34:36 -08:00
|
|
|
/**
|
2019-12-25 13:51:28 -08:00
|
|
|
* 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.
|
2019-11-06 21:34:36 -08:00
|
|
|
*/
|
|
|
|
|
public static final int SUGGEST_MAX = 4;
|
|
|
|
|
|
2020-01-10 23:37:16 -08:00
|
|
|
/**
|
|
|
|
|
* When inheriting class requires precaching, override this method.
|
|
|
|
|
*/
|
|
|
|
|
public void load(Context context) {}
|
|
|
|
|
|
2019-10-10 23:11:12 -07:00
|
|
|
public CharSequence getSuggestedFolderName(Context context,
|
2019-12-25 13:51:28 -08:00
|
|
|
ArrayList<WorkspaceItemInfo> workspaceItemInfos, CharSequence[] candidates) {
|
|
|
|
|
|
2020-01-10 23:37:16 -08:00
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
|
|
|
|
|
}
|
2019-12-25 13:51:28 -08:00
|
|
|
// 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))
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
if (distinctItemInfos.size() == 1
|
|
|
|
|
&& !distinctItemInfos.get(0).user.equals(Process.myUserHandle())) {
|
|
|
|
|
// Place it as last viable suggestion
|
|
|
|
|
setAsLastSuggestion(candidates,
|
|
|
|
|
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
|
|
|
|
|
? p.getTargetComponent().getPackageName() : ""))
|
|
|
|
|
.collect(Collectors.toList());
|
2019-10-10 23:11:12 -07:00
|
|
|
|
2019-12-25 13:51:28 -08:00
|
|
|
if (distinctItemInfos.size() == 1) {
|
|
|
|
|
Optional<AppInfo> info = LauncherAppState.getInstance(context).getModel()
|
|
|
|
|
.getAppInfoByPackageName(distinctItemInfos.get(0).getTargetComponent()
|
|
|
|
|
.getPackageName());
|
|
|
|
|
// Place it as first viable suggestion and shift everything else
|
|
|
|
|
info.ifPresent(i -> setAsFirstSuggestion(candidates, i.title.toString()));
|
|
|
|
|
}
|
2020-01-10 23:37:16 -08:00
|
|
|
if (DEBUG) {
|
|
|
|
|
Log.d(TAG, "getSuggestedFolderName:" + Arrays.toString(candidates));
|
|
|
|
|
}
|
2019-12-25 13:51:28 -08:00
|
|
|
return candidates[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setAsFirstSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
|
2020-01-10 23:37:16 -08:00
|
|
|
if (contains(candidatesOut, candidate)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-12-25 13:51:28 -08:00
|
|
|
for (int i = candidatesOut.length - 1; i > 0; i--) {
|
2020-01-10 23:37:16 -08:00
|
|
|
if (!TextUtils.isEmpty(candidatesOut[i - 1])) {
|
|
|
|
|
candidatesOut[i] = candidatesOut[i - 1];
|
2019-10-10 23:11:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-10 23:37:16 -08:00
|
|
|
candidatesOut[0] = candidate;
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void setAsLastSuggestion(CharSequence[] candidatesOut, CharSequence candidate) {
|
2020-01-10 23:37:16 -08:00
|
|
|
if (contains(candidatesOut, candidate)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-01-12 00:41:06 -08:00
|
|
|
|
2019-12-25 13:51:28 -08:00
|
|
|
for (int i = 0; i < candidate.length(); i++) {
|
|
|
|
|
if (TextUtils.isEmpty(candidatesOut[i])) {
|
|
|
|
|
candidatesOut[i] = candidate;
|
2020-01-12 00:41:06 -08:00
|
|
|
return;
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-12 00:41:06 -08:00
|
|
|
candidatesOut[candidate.length() - 1] = candidate;
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
|
2020-01-10 23:37:16 -08:00
|
|
|
private boolean contains(CharSequence[] list, CharSequence key) {
|
|
|
|
|
return Arrays.asList(list).stream()
|
|
|
|
|
.filter(s -> s != null)
|
|
|
|
|
.anyMatch(s -> s.toString().equalsIgnoreCase(key.toString()));
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-25 13:51:28 -08:00
|
|
|
// This method can be moved to some Utility class location.
|
|
|
|
|
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
|
|
|
|
|
Map<Object, Boolean> map = new ConcurrentHashMap<>();
|
|
|
|
|
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
|
2019-10-10 23:11:12 -07:00
|
|
|
}
|
|
|
|
|
}
|