From 9abd8cbf141183c7acd46eff47f7ac65f228c6d8 Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Wed, 22 Feb 2023 23:43:33 +0000 Subject: [PATCH] Allow Binding the First Workspace Page before the Rest. The way WorkspaceBinder is currently setup, any calls to bindWorkspace will result in the entire workspace being bound all at the same time. These edits change that. Now callers like LoaderTask can bind the first workspace pages before the rest by splitting the WorkspaceBinding into 3 methods that need to be called in order, but can happen at a staggered cadence. Bug: 251502424 Test: Loaded and bound the workspace properly. Change-Id: I28fa721ea95dae2df03e27f600653ba5bebe3ef1 --- .../launcher3/config/FeatureFlags.java | 5 + .../launcher3/model/BaseLauncherBinder.java | 150 +++++++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index b1159cdf07..ffcc385280 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -363,6 +363,11 @@ public final class FeatureFlags { "Enables taskbar pinning to allow user to switch between transient and persistent " + "taskbar flavors"); + public static final BooleanFlag ENABLE_WORKSPACE_LOADING_OPTIMIZATION = getDebugFlag(251502424, + "ENABLE_WORKSPACE_LOADING_OPTIMIZATION", false, "load the current workspace screen " + + "visible to the user before the rest rather than loading all of them at once." + ); + public static final BooleanFlag ENABLE_GRID_ONLY_OVERVIEW = getDebugFlag(270397206, "ENABLE_GRID_ONLY_OVERVIEW", false, "Enable a grid-only overview without a focused task."); diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java index 8519a3ef52..91ace27459 100644 --- a/src/com/android/launcher3/model/BaseLauncherBinder.java +++ b/src/com/android/launcher3/model/BaseLauncherBinder.java @@ -27,6 +27,7 @@ import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherModel.CallbackTask; import com.android.launcher3.LauncherSettings; +import com.android.launcher3.Workspace; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.model.BgDataModel.Callbacks; import com.android.launcher3.model.BgDataModel.FixedContainerItems; @@ -42,8 +43,10 @@ import com.android.launcher3.util.RunnableList; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -77,6 +80,36 @@ public abstract class BaseLauncherBinder { * Binds all loaded data to actual views on the main thread. */ public void bindWorkspace(boolean incrementBindId) { + if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) { + DisjointWorkspaceBinder workspaceBinder = + initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens()); + workspaceBinder.bindCurrentWorkspacePages(); + workspaceBinder.bindOtherWorkspacePages(); + } else { + bindWorkspaceAllAtOnce(incrementBindId); + } + } + + /** + * Initializes the WorkspaceBinder for binding. + * + * @param incrementBindId this is used to stop previously started binding tasks that are + * obsolete but still queued. + * @param workspacePages this allows the Launcher to add the correct workspace screens. + */ + public DisjointWorkspaceBinder initWorkspaceBinder(boolean incrementBindId, + IntArray workspacePages) { + + synchronized (mBgDataModel) { + if (incrementBindId) { + mBgDataModel.lastBindId++; + } + mMyBindingId = mBgDataModel.lastBindId; + return new DisjointWorkspaceBinder(workspacePages); + } + } + + private void bindWorkspaceAllAtOnce(boolean incrementBindId) { // Save a copy of all the bg-thread collections ArrayList workspaceItems = new ArrayList<>(); ArrayList appWidgets = new ArrayList<>(); @@ -95,7 +128,7 @@ public abstract class BaseLauncherBinder { } for (Callbacks cb : mCallbacksList) { - new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId, + new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId, workspaceItems, appWidgets, extraItems, orderedScreenIds).bind(); } } @@ -180,7 +213,7 @@ public abstract class BaseLauncherBinder { return idleLock; } - private class WorkspaceBinder { + private class UnifiedWorkspaceBinder { private final Executor mUiExecutor; private final Callbacks mCallbacks; @@ -194,7 +227,7 @@ public abstract class BaseLauncherBinder { private final IntArray mOrderedScreenIds; private final ArrayList mExtraItems; - WorkspaceBinder(Callbacks callbacks, + UnifiedWorkspaceBinder(Callbacks callbacks, Executor uiExecutor, LauncherAppState app, BgDataModel bgDataModel, @@ -320,4 +353,115 @@ public abstract class BaseLauncherBinder { }); } } + + private class DisjointWorkspaceBinder { + private final IntArray mOrderedScreenIds; + private final IntSet mCurrentScreenIds = new IntSet(); + private final Set mBoundItemIds = new HashSet<>(); + + protected DisjointWorkspaceBinder(IntArray orderedScreenIds) { + mOrderedScreenIds = orderedScreenIds; + + for (Callbacks cb : mCallbacksList) { + mCurrentScreenIds.addAll(cb.getPagesToBindSynchronously(orderedScreenIds)); + } + if (mCurrentScreenIds.size() == 0) { + mCurrentScreenIds.add(Workspace.FIRST_SCREEN_ID); + } + } + + /** + * Binds the currently loaded items in the Data Model. Also signals to the Callbacks[] + * that these items have been bound and their respective screens are ready to be shown. + * + * If this method is called after all the items on the workspace screen have already been + * loaded, it will bind all workspace items immediately, and bindOtherWorkspacePages() will + * not bind any items. + */ + protected void bindCurrentWorkspacePages() { + // Save a copy of all the bg-thread collections + ArrayList workspaceItems; + ArrayList appWidgets; + + synchronized (mBgDataModel) { + workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems); + appWidgets = new ArrayList<>(mBgDataModel.appWidgets); + } + + workspaceItems.forEach(it -> mBoundItemIds.add(it.id)); + appWidgets.forEach(it -> mBoundItemIds.add(it.id)); + + sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems); + + // Tell the workspace that we're about to start binding items + executeCallbacksTask(c -> { + c.clearPendingBinds(); + c.startBinding(); + }, mUiExecutor); + + // Bind workspace screens + executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor); + + bindWorkspaceItems(workspaceItems); + bindAppWidgets(appWidgets); + + executeCallbacksTask(c -> { + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + c.onInitialBindComplete(mCurrentScreenIds, new RunnableList()); + }, mUiExecutor); + } + + protected void bindOtherWorkspacePages() { + // Save a copy of all the bg-thread collections + ArrayList workspaceItems; + ArrayList appWidgets; + + synchronized (mBgDataModel) { + workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems); + appWidgets = new ArrayList<>(mBgDataModel.appWidgets); + } + + workspaceItems.removeIf(it -> mBoundItemIds.contains(it.id)); + appWidgets.removeIf(it -> mBoundItemIds.contains(it.id)); + + sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems); + + bindWorkspaceItems(workspaceItems); + bindAppWidgets(appWidgets); + + executeCallbacksTask(c -> c.finishBindingItems(mCurrentScreenIds), mUiExecutor); + mUiExecutor.execute(() -> { + MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); + ItemInstallQueue.INSTANCE.get(mApp.getContext()) + .resumeModelPush(FLAG_LOADER_RUNNING); + }); + + for (Callbacks cb : mCallbacksList) { + cb.bindStringCache(mBgDataModel.stringCache.clone()); + } + } + + private void bindWorkspaceItems(final ArrayList workspaceItems) { + // Bind the workspace items + int count = workspaceItems.size(); + for (int i = 0; i < count; i += ITEMS_CHUNK) { + final int start = i; + final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i); + executeCallbacksTask( + c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false), + mUiExecutor); + } + } + + private void bindAppWidgets(List appWidgets) { + // Bind the widgets, one at a time + int count = appWidgets.size(); + for (int i = 0; i < count; i++) { + final ItemInfo widget = appWidgets.get(i); + executeCallbacksTask( + c -> c.bindItems(Collections.singletonList(widget), false), + mUiExecutor); + } + } + } }