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;
|
|
|
|
|
|
2020-03-12 12:53:43 -07:00
|
|
|
import android.content.ComponentName;
|
2019-10-10 23:11:12 -07:00
|
|
|
import android.content.Context;
|
2019-12-25 13:51:28 -08:00
|
|
|
import android.os.Process;
|
2020-03-12 12:53:43 -07:00
|
|
|
import android.os.UserHandle;
|
2019-12-25 13:51:28 -08:00
|
|
|
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.LauncherAppState;
|
|
|
|
|
import com.android.launcher3.R;
|
2020-02-12 23:58:30 -08:00
|
|
|
import com.android.launcher3.model.AllAppsList;
|
|
|
|
|
import com.android.launcher3.model.BaseModelUpdateTask;
|
|
|
|
|
import com.android.launcher3.model.BgDataModel;
|
2020-04-06 15:11:17 -07:00
|
|
|
import com.android.launcher3.model.data.AppInfo;
|
|
|
|
|
import com.android.launcher3.model.data.FolderInfo;
|
|
|
|
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
2020-02-12 23:58:30 -08:00
|
|
|
import com.android.launcher3.util.IntSparseArrayMap;
|
2020-06-18 22:57:50 -07:00
|
|
|
import com.android.launcher3.util.Preconditions;
|
2020-01-26 23:25:14 -08:00
|
|
|
import com.android.launcher3.util.ResourceBasedOverride;
|
2019-10-10 23:11:12 -07:00
|
|
|
|
|
|
|
|
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;
|
2020-02-07 20:06:34 -08:00
|
|
|
import java.util.Objects;
|
2019-12-25 13:51:28 -08:00
|
|
|
import java.util.Optional;
|
2020-03-12 12:53:43 -07:00
|
|
|
import java.util.Set;
|
2019-12-25 13:51:28 -08:00
|
|
|
import java.util.stream.Collectors;
|
2019-10-10 23:11:12 -07:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Locates provider for the folder name.
|
|
|
|
|
*/
|
2020-01-26 23:25:14 -08:00
|
|
|
public class FolderNameProvider implements ResourceBasedOverride {
|
2019-10-10 23:11:12 -07:00
|
|
|
|
2020-01-21 11:59:19 -08:00
|
|
|
private static final String TAG = "FolderNameProvider";
|
2020-01-26 23:25:14 -08:00
|
|
|
private static final boolean DEBUG = true;
|
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-02-12 23:58:30 -08:00
|
|
|
protected IntSparseArrayMap<FolderInfo> mFolderInfos;
|
|
|
|
|
protected List<AppInfo> mAppInfos;
|
2019-11-06 21:34:36 -08:00
|
|
|
|
2020-01-10 23:37:16 -08:00
|
|
|
/**
|
2020-01-26 23:25:14 -08:00
|
|
|
* Retrieve instance of this object that can be overridden in runtime based on the build
|
|
|
|
|
* variant of the application.
|
2020-01-10 23:37:16 -08:00
|
|
|
*/
|
2020-01-26 23:25:14 -08:00
|
|
|
public static FolderNameProvider newInstance(Context context) {
|
|
|
|
|
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
|
|
|
|
|
context.getApplicationContext(), R.string.folder_name_provider_class);
|
2020-06-18 22:57:50 -07:00
|
|
|
Preconditions.assertWorkerThread();
|
2020-02-12 23:58:30 -08:00
|
|
|
fnp.load(context);
|
|
|
|
|
|
2020-01-26 23:25:14 -08:00
|
|
|
return fnp;
|
|
|
|
|
}
|
2020-01-10 23:37:16 -08:00
|
|
|
|
2020-02-19 09:14:49 -08:00
|
|
|
public static FolderNameProvider newInstance(Context context, List<AppInfo> appInfos,
|
|
|
|
|
IntSparseArrayMap<FolderInfo> folderInfos) {
|
2020-06-18 22:57:50 -07:00
|
|
|
Preconditions.assertWorkerThread();
|
2020-02-19 09:14:49 -08:00
|
|
|
FolderNameProvider fnp = Overrides.getObject(FolderNameProvider.class,
|
|
|
|
|
context.getApplicationContext(), R.string.folder_name_provider_class);
|
|
|
|
|
fnp.load(appInfos, folderInfos);
|
|
|
|
|
|
|
|
|
|
return fnp;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 23:58:30 -08:00
|
|
|
private void load(Context context) {
|
|
|
|
|
LauncherAppState.getInstance(context).getModel().enqueueModelUpdateTask(
|
|
|
|
|
new FolderNameWorker());
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-19 09:14:49 -08:00
|
|
|
private void load(List<AppInfo> appInfos, IntSparseArrayMap<FolderInfo> folderInfos) {
|
|
|
|
|
mAppInfos = appInfos;
|
|
|
|
|
mFolderInfos = folderInfos;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-07 20:06:34 -08:00
|
|
|
/**
|
|
|
|
|
* Generate and rank the suggested Folder names.
|
|
|
|
|
*/
|
|
|
|
|
public void getSuggestedFolderName(Context context,
|
|
|
|
|
ArrayList<WorkspaceItemInfo> workspaceItemInfos,
|
2020-06-19 18:31:18 -07:00
|
|
|
FolderNameInfos nameInfos) {
|
2020-06-18 22:57:50 -07:00
|
|
|
Preconditions.assertWorkerThread();
|
2020-01-10 23:37:16 -08:00
|
|
|
if (DEBUG) {
|
2020-06-19 18:31:18 -07:00
|
|
|
Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
|
2020-01-10 23:37:16 -08:00
|
|
|
}
|
2019-12-25 13:51:28 -08:00
|
|
|
// If all the icons are from work profile,
|
|
|
|
|
// Then, suggest "Work" as the folder name
|
2020-03-12 12:53:43 -07:00
|
|
|
Set<UserHandle> users = workspaceItemInfos.stream().map(w -> w.user)
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
if (users.size() == 1 && !users.contains(Process.myUserHandle())) {
|
2020-02-07 20:06:34 -08:00
|
|
|
setAsLastSuggestion(nameInfos,
|
2019-12-25 13:51:28 -08:00
|
|
|
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
|
2020-03-12 12:53:43 -07:00
|
|
|
Set<String> packageNames = workspaceItemInfos.stream()
|
|
|
|
|
.map(WorkspaceItemInfo::getTargetComponent)
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
.map(ComponentName::getPackageName)
|
|
|
|
|
.collect(Collectors.toSet());
|
|
|
|
|
|
|
|
|
|
if (packageNames.size() == 1) {
|
|
|
|
|
Optional<AppInfo> info = getAppInfoByPackageName(packageNames.iterator().next());
|
2019-12-25 13:51:28 -08:00
|
|
|
// Place it as first viable suggestion and shift everything else
|
2020-02-07 20:06:34 -08:00
|
|
|
info.ifPresent(i -> setAsFirstSuggestion(nameInfos, i.title.toString()));
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
2020-01-10 23:37:16 -08:00
|
|
|
if (DEBUG) {
|
2020-06-19 18:31:18 -07:00
|
|
|
Log.d(TAG, "getSuggestedFolderName:" + nameInfos.toString());
|
2020-01-10 23:37:16 -08:00
|
|
|
}
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 23:58:30 -08:00
|
|
|
private Optional<AppInfo> getAppInfoByPackageName(String packageName) {
|
|
|
|
|
if (mAppInfos == null || mAppInfos.isEmpty()) {
|
|
|
|
|
return Optional.empty();
|
|
|
|
|
}
|
|
|
|
|
return mAppInfos.stream()
|
2020-03-12 12:53:43 -07:00
|
|
|
.filter(info -> info.componentName != null)
|
2020-02-12 23:58:30 -08:00
|
|
|
.filter(info -> info.componentName.getPackageName().equals(packageName))
|
|
|
|
|
.findAny();
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-19 18:31:18 -07:00
|
|
|
private void setAsFirstSuggestion(FolderNameInfos nameInfos, CharSequence label) {
|
|
|
|
|
if (nameInfos == null || nameInfos.contains(label)) {
|
2020-01-10 23:37:16 -08:00
|
|
|
return;
|
|
|
|
|
}
|
2020-06-19 18:31:18 -07:00
|
|
|
nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
|
|
|
|
|
nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
|
|
|
|
|
CharSequence[] labels = nameInfos.getLabels();
|
|
|
|
|
Float[] scores = nameInfos.getScores();
|
|
|
|
|
for (int i = labels.length - 1; i > 0; i--) {
|
|
|
|
|
if (labels[i - 1] != null && !TextUtils.isEmpty(labels[i - 1])) {
|
|
|
|
|
nameInfos.setLabel(i, labels[i - 1], scores[i - 1]);
|
2019-10-10 23:11:12 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-06-19 18:31:18 -07:00
|
|
|
nameInfos.setLabel(0, label, 1.0f);
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
|
2020-06-19 18:31:18 -07:00
|
|
|
private void setAsLastSuggestion(FolderNameInfos nameInfos, CharSequence label) {
|
|
|
|
|
if (nameInfos == null || nameInfos.contains(label)) {
|
2020-01-10 23:37:16 -08:00
|
|
|
return;
|
|
|
|
|
}
|
2020-06-19 18:31:18 -07:00
|
|
|
nameInfos.setStatus(FolderNameInfos.HAS_PRIMARY);
|
|
|
|
|
nameInfos.setStatus(FolderNameInfos.HAS_SUGGESTIONS);
|
|
|
|
|
CharSequence[] labels = nameInfos.getLabels();
|
|
|
|
|
for (int i = 0; i < labels.length; i++) {
|
|
|
|
|
if (labels[i] == null || TextUtils.isEmpty(labels[i])) {
|
|
|
|
|
nameInfos.setLabel(i, label, 1.0f);
|
2020-01-12 00:41:06 -08:00
|
|
|
return;
|
2019-12-25 13:51:28 -08:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-07 20:06:34 -08:00
|
|
|
// Overwrite the last suggestion.
|
2020-06-19 18:31:18 -07:00
|
|
|
nameInfos.setLabel(labels.length - 1, label, 1.0f);
|
2020-01-10 23:37:16 -08:00
|
|
|
}
|
|
|
|
|
|
2020-02-12 23:58:30 -08:00
|
|
|
private class FolderNameWorker extends BaseModelUpdateTask {
|
|
|
|
|
@Override
|
|
|
|
|
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
|
|
|
|
mFolderInfos = dataModel.folders.clone();
|
|
|
|
|
mAppInfos = Arrays.asList(apps.copyData());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-10 23:11:12 -07:00
|
|
|
}
|