Merge "Improve Hybird hotseat cache support" into ub-launcher3-rvc-dev

This commit is contained in:
TreeHugger Robot
2020-05-06 22:19:53 +00:00
committed by Android (Google) Code Review
10 changed files with 211 additions and 35 deletions
@@ -43,6 +43,7 @@ import com.android.launcher3.DropTarget;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -56,6 +57,7 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@@ -69,6 +71,7 @@ import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -93,8 +96,6 @@ public class HotseatPredictionController implements DragController.DragListener,
//TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
private static final int APPTARGET_ACTION_UNPIN = 4;
private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys";
private static final String APP_LOCATION_HOTSEAT = "hotseat";
private static final String APP_LOCATION_WORKSPACE = "workspace";
@@ -115,11 +116,13 @@ public class HotseatPredictionController implements DragController.DragListener,
private DynamicItemCache mDynamicItemCache;
private final PredictionModel mPredictionModel;
private AppPredictor mAppPredictor;
private AllAppsStore mAllAppsStore;
private AnimatorSet mIconRemoveAnimators;
private boolean mUIUpdatePaused = false;
private boolean mRequiresCacheUpdate = false;
private boolean mRequiresCacheUpdate = true;
private boolean mIsCacheEmpty;
private HotseatEduController mHotseatEduController;
@@ -138,15 +141,16 @@ public class HotseatPredictionController implements DragController.DragListener,
mLauncher = launcher;
mHotseat = launcher.getHotseat();
mAllAppsStore = mLauncher.getAppsView().getAppsStore();
mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
mAllAppsStore.addUpdateListener(this);
mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
launcher.getDeviceProfile().inv.addOnChangeListener(this);
mHotseat.addOnAttachStateChangeListener(this);
mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
if (mHotseat.isAttachedToWindow()) {
onViewAttachedToWindow(mHotseat);
}
showCachedItems();
}
/**
@@ -185,6 +189,11 @@ public class HotseatPredictionController implements DragController.DragListener,
return;
}
List<WorkspaceItemInfo> predictedApps = mapToWorkspaceItemInfo(mComponentKeyMappers);
if (mComponentKeyMappers.isEmpty() != predictedApps.isEmpty()) {
// Safely ignore update as AppsList is not ready yet. This will called again once
// apps are ready (HotseatPredictionController#onAppsUpdated)
return;
}
int predictionIndex = 0;
ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
// make sure predicted icon removal and filling predictions don't step on each other
@@ -305,14 +314,23 @@ public class HotseatPredictionController implements DragController.DragListener,
mAppPredictor.requestPredictionUpdate();
}
private void showCachedItems() {
ArrayList<ComponentKey> componentKeys = getCachedComponentKeys();
/**
* Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
*/
public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
int count = Math.min(ranks.size(), apps.size());
List<WorkspaceItemInfo> items = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
preparePredictionInfo(item, ranks.get(i));
items.add(item);
}
mComponentKeyMappers.clear();
for (ComponentKey key : componentKeys) {
for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
updateDependencies();
fillGapsWithPrediction();
bindItems(items, false, null);
}
private Bundle getAppPredictionContextExtra() {
@@ -390,41 +408,20 @@ public class HotseatPredictionController implements DragController.DragListener,
predictionLog.append("]");
if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
updateDependencies();
fillGapsWithPrediction();
fillGapsWithPrediction();
if (!isEduSeen() && mHotseatEduController != null) {
mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
}
// should invalidate cache if AiAi sends empty list of AppTargets
if (appTargets.isEmpty()) {
mRequiresCacheUpdate = true;
}
cachePredictionComponentKeys(componentKeys);
cachePredictionComponentKeysIfNecessary(componentKeys);
}
private void cachePredictionComponentKeys(ArrayList<ComponentKey> componentKeys) {
if (!mRequiresCacheUpdate) return;
StringBuilder builder = new StringBuilder();
for (ComponentKey componentKey : componentKeys) {
builder.append(componentKey);
builder.append("\n");
}
mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY,
builder.toString()).apply();
private void cachePredictionComponentKeysIfNecessary(ArrayList<ComponentKey> componentKeys) {
if (!mRequiresCacheUpdate && componentKeys.isEmpty() == mIsCacheEmpty) return;
mPredictionModel.cachePredictionComponentKeys(componentKeys);
mIsCacheEmpty = componentKeys.isEmpty();
mRequiresCacheUpdate = false;
}
private ArrayList<ComponentKey> getCachedComponentKeys() {
String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, "");
ArrayList<ComponentKey> results = new ArrayList<>();
for (String line : cachedBlob.split("\n")) {
ComponentKey key = ComponentKey.fromString(line);
if (key != null) {
results.add(key);
}
}
return results;
}
private void updateDependencies() {
mDynamicItemCache.updateDependencies(mComponentKeyMappers, mAllAppsStore, this,
mHotSeatItemsCount);
@@ -44,6 +44,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
@@ -58,6 +59,7 @@ import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchContro
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -70,6 +72,7 @@ import com.android.quickstep.views.TaskView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -177,6 +180,14 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
}
}
@Override
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) {
super.bindPredictedItems(appInfos, ranks);
if (mHotseatPredictionController != null) {
mHotseatPredictionController.showCachedItems(appInfos, ranks);
}
}
@Override
public void onDestroy() {
super.onDestroy();
+3
View File
@@ -2225,6 +2225,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
workspace.requestLayout();
}
@Override
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
/**
* Add the views for a widget to the workspace.
*/
@@ -33,6 +33,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.PredictionModel;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.InstallSessionTracker;
@@ -57,6 +58,7 @@ public class LauncherAppState {
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final PredictionModel mPredictionModel;
private SecureSettingsObserver mNotificationDotsObserver;
private InstallSessionTracker mInstallSessionTracker;
@@ -127,6 +129,7 @@ public class LauncherAppState {
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
mPredictionModel = new PredictionModel(mContext);
}
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
@@ -182,6 +185,10 @@ public class LauncherAppState {
return mModel;
}
public PredictionModel getPredictionModel() {
return mPredictionModel;
}
public WidgetPreviewLoader getWidgetCache() {
return mWidgetCache;
}
@@ -17,6 +17,7 @@
package com.android.launcher3.model;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import android.util.Log;
@@ -196,6 +197,10 @@ public abstract class BaseLoaderResults {
// Load items on the current page.
bindWorkspaceItems(currentWorkspaceItems, mainExecutor);
bindAppWidgets(currentAppWidgets, mainExecutor);
// Locate available spots for prediction using currentWorkspaceItems
IntArray gaps = getMissingHotseatRanks(currentWorkspaceItems, idp.numHotseatIcons);
bindPredictedItems(gaps, mainExecutor);
// In case of validFirstPage, only bind the first screen, and defer binding the
// remaining screens after first onDraw (and an optional the fade animation whichever
// happens later).
@@ -247,6 +252,11 @@ public abstract class BaseLoaderResults {
}
}
private void bindPredictedItems(IntArray ranks, final Executor executor) {
executeCallbacksTask(
c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
}
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
executor.execute(() -> {
if (mMyBindingId != mBgDataModel.lastBindId) {
@@ -92,6 +92,11 @@ public class BgDataModel {
*/
public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
/**
* List of all cached predicted items visible on home screen
*/
public final ArrayList<AppInfo> cachedPredictedItems = new ArrayList<>();
/**
* True if the launcher has permission to access deep shortcuts.
*/
@@ -366,5 +371,10 @@ public class BgDataModel {
void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
void bindAllApplications(AppInfo[] apps);
/**
* Binds predicted appInfos at at available prediction slots.
*/
void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks);
}
}
@@ -179,6 +179,7 @@ public class LoaderTask implements Runnable {
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
List<ShortcutInfo> allShortcuts = new ArrayList<>();
loadWorkspace(allShortcuts);
loadCachedPredictions();
logger.addSplit("loadWorkspace");
verifyNotStopped();
@@ -849,6 +850,23 @@ public class LoaderTask implements Runnable {
}
}
private List<AppInfo> loadCachedPredictions() {
List<ComponentKey> componentKeys = mApp.getPredictionModel().getPredictionComponentKeys();
List<AppInfo> results = new ArrayList<>();
if (componentKeys == null) return results;
List<LauncherActivityInfo> l;
mBgDataModel.cachedPredictedItems.clear();
for (ComponentKey key : componentKeys) {
l = mLauncherApps.getActivityList(key.componentName.getPackageName(), key.user);
if (l.size() == 0) continue;
boolean quietMode = mUserManager.isQuietModeEnabled(key.user);
AppInfo info = new AppInfo(l.get(0), key.user, quietMode);
mBgDataModel.cachedPredictedItems.add(info);
mIconCache.getTitleAndIcon(info, false);
}
return results;
}
private List<LauncherActivityInfo> loadAllApps() {
final List<UserHandle> profiles = mUserCache.getUserProfiles();
List<LauncherActivityInfo> allActivityList = new ArrayList<>();
@@ -880,6 +898,14 @@ public class LoaderTask implements Runnable {
PackageInstallInfo.fromInstallingState(info));
}
}
for (AppInfo item : mBgDataModel.cachedPredictedItems) {
List<LauncherActivityInfo> l = mLauncherApps.getActivityList(
item.componentName.getPackageName(), item.user);
for (LauncherActivityInfo info : l) {
boolean quietMode = mUserManager.isQuietModeEnabled(item.user);
mBgAllAppsList.add(new AppInfo(info, item.user, quietMode), info);
}
}
mBgAllAppsList.getAndResetChangeFlag();
return allActivityList;
@@ -19,11 +19,14 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.stream.IntStream;
/**
* Utils class for {@link com.android.launcher3.LauncherModel}.
@@ -109,4 +112,17 @@ public class ModelUtils {
}
});
}
/**
* Iterates though current workspace items and returns available hotseat ranks for prediction.
*/
public static IntArray getMissingHotseatRanks(List<ItemInfo> items, int len) {
IntSet seen = new IntSet();
items.stream().filter(
info -> info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)
.forEach(i -> seen.add(i.screenId));
IntArray result = new IntArray(len);
IntStream.range(0, len).filter(i -> !seen.contains(i)).forEach(result::add);
return result;
}
}
@@ -0,0 +1,93 @@
/*
* 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.model;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.UserHandle;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* Model helper for app predictions in workspace
*/
public class PredictionModel {
private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
private static final int MAX_CACHE_ITEMS = 5;
private final Context mContext;
private final SharedPreferences mDevicePrefs;
private ArrayList<ComponentKey> mCachedComponentKeys;
public PredictionModel(Context context) {
mContext = context;
mDevicePrefs = Utilities.getDevicePrefs(mContext);
}
/**
* Formats and stores a list of component key in device preferences.
*/
public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
StringBuilder builder = new StringBuilder();
int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
for (int i = 0; i < count; i++) {
builder.append(componentKeys.get(i));
builder.append("\n");
}
mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
mCachedComponentKeys = null;
}
/**
* parses and returns ComponentKeys saved by
* {@link PredictionModel#cachePredictionComponentKeys(List)}
*/
public List<ComponentKey> getPredictionComponentKeys() {
if (mCachedComponentKeys == null) {
mCachedComponentKeys = new ArrayList<>();
String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
for (String line : cachedBlob.split("\n")) {
ComponentKey key = ComponentKey.fromString(line);
if (key != null) {
mCachedComponentKeys.add(key);
}
}
}
return mCachedComponentKeys;
}
/**
* Remove uninstalled applications from model
*/
public void removePackage(String pkgName, UserHandle user, ArrayList<AppInfo> ids) {
for (int i = ids.size() - 1; i >= 0; i--) {
AppInfo info = ids.get(i);
if (info.user.equals(user) && pkgName.equals(info.componentName.getPackageName())) {
ids.remove(i);
}
}
cachePredictionComponentKeys(getPredictionComponentKeys().stream()
.filter(cn -> !(cn.user.equals(user) && cn.componentName.getPackageName().equals(
pkgName))).collect(Collectors.toList()));
}
}
@@ -195,6 +195,9 @@ public class SecondaryDisplayLauncher extends BaseDraggingActivity
@Override
public void bindItems(List<ItemInfo> shortcuts, boolean forceAnimateIcons) { }
@Override
public void bindPredictedItems(List<AppInfo> appInfos, IntArray ranks) { }
@Override
public void bindScreens(IntArray orderedScreenIds) { }