1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.launcher3.model; 18 19 import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED; 20 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle; 21 import static com.android.launcher3.Flags.enableWorkspaceInflation; 22 import static com.android.launcher3.model.ItemInstallQueue.FLAG_LOADER_RUNNING; 23 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems; 24 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 25 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 26 27 import android.os.Process; 28 import android.os.Trace; 29 import android.util.Log; 30 import android.util.Pair; 31 import android.view.View; 32 33 import androidx.annotation.NonNull; 34 35 import com.android.launcher3.InvariantDeviceProfile; 36 import com.android.launcher3.LauncherAppState; 37 import com.android.launcher3.LauncherModel.CallbackTask; 38 import com.android.launcher3.LauncherSettings; 39 import com.android.launcher3.Workspace; 40 import com.android.launcher3.celllayout.CellPosMapper; 41 import com.android.launcher3.config.FeatureFlags; 42 import com.android.launcher3.model.BgDataModel.Callbacks; 43 import com.android.launcher3.model.BgDataModel.FixedContainerItems; 44 import com.android.launcher3.model.data.AppInfo; 45 import com.android.launcher3.model.data.ItemInfo; 46 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 47 import com.android.launcher3.util.ComponentKey; 48 import com.android.launcher3.util.IntArray; 49 import com.android.launcher3.util.IntSet; 50 import com.android.launcher3.util.ItemInflater; 51 import com.android.launcher3.util.LooperExecutor; 52 import com.android.launcher3.util.LooperIdleLock; 53 import com.android.launcher3.util.PackageUserKey; 54 import com.android.launcher3.util.RunnableList; 55 import com.android.launcher3.widget.model.WidgetsListBaseEntry; 56 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collections; 60 import java.util.HashMap; 61 import java.util.HashSet; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.Objects; 65 import java.util.Set; 66 import java.util.concurrent.Executor; 67 import java.util.stream.Collectors; 68 69 /** 70 * Binds the results of {@link com.android.launcher3.model.LoaderTask} to the Callbacks objects. 71 */ 72 public class BaseLauncherBinder { 73 74 protected static final String TAG = "LauncherBinder"; 75 private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons 76 77 protected final LooperExecutor mUiExecutor; 78 79 protected final LauncherAppState mApp; 80 protected final BgDataModel mBgDataModel; 81 private final AllAppsList mBgAllAppsList; 82 83 final Callbacks[] mCallbacksList; 84 85 private int mMyBindingId; 86 BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel, AllAppsList allAppsList, Callbacks[] callbacksList)87 public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel, 88 AllAppsList allAppsList, Callbacks[] callbacksList) { 89 mUiExecutor = MAIN_EXECUTOR; 90 mApp = app; 91 mBgDataModel = dataModel; 92 mBgAllAppsList = allAppsList; 93 mCallbacksList = callbacksList; 94 } 95 96 /** 97 * Binds all loaded data to actual views on the main thread. 98 */ bindWorkspace(boolean incrementBindId, boolean isBindSync)99 public void bindWorkspace(boolean incrementBindId, boolean isBindSync) { 100 Trace.beginSection("BaseLauncherBinder#bindWorkspace"); 101 try { 102 if (FeatureFlags.ENABLE_WORKSPACE_LOADING_OPTIMIZATION.get()) { 103 DisjointWorkspaceBinder workspaceBinder = 104 initWorkspaceBinder(incrementBindId, mBgDataModel.collectWorkspaceScreens()); 105 workspaceBinder.bindCurrentWorkspacePages(isBindSync); 106 workspaceBinder.bindOtherWorkspacePages(); 107 } else { 108 bindWorkspaceAllAtOnce(incrementBindId, isBindSync); 109 } 110 } finally { 111 Trace.endSection(); 112 } 113 } 114 115 /** 116 * Initializes the WorkspaceBinder for binding. 117 * 118 * @param incrementBindId this is used to stop previously started binding tasks that are 119 * obsolete but still queued. 120 * @param workspacePages this allows the Launcher to add the correct workspace screens. 121 */ initWorkspaceBinder(boolean incrementBindId, IntArray workspacePages)122 public DisjointWorkspaceBinder initWorkspaceBinder(boolean incrementBindId, 123 IntArray workspacePages) { 124 125 synchronized (mBgDataModel) { 126 if (incrementBindId) { 127 mBgDataModel.lastBindId++; 128 mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); 129 } 130 mMyBindingId = mBgDataModel.lastBindId; 131 return new DisjointWorkspaceBinder(workspacePages); 132 } 133 } 134 bindWorkspaceAllAtOnce(boolean incrementBindId, boolean isBindSync)135 private void bindWorkspaceAllAtOnce(boolean incrementBindId, boolean isBindSync) { 136 // Save a copy of all the bg-thread collections 137 ArrayList<ItemInfo> workspaceItems = new ArrayList<>(); 138 ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>(); 139 final IntArray orderedScreenIds = new IntArray(); 140 ArrayList<FixedContainerItems> extraItems = new ArrayList<>(); 141 final int workspaceItemCount; 142 synchronized (mBgDataModel) { 143 workspaceItems.addAll(mBgDataModel.workspaceItems); 144 appWidgets.addAll(mBgDataModel.appWidgets); 145 orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens()); 146 mBgDataModel.extraItems.forEach(extraItems::add); 147 if (incrementBindId) { 148 mBgDataModel.lastBindId++; 149 mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId(); 150 } 151 mMyBindingId = mBgDataModel.lastBindId; 152 workspaceItemCount = mBgDataModel.itemsIdMap.size(); 153 } 154 155 for (Callbacks cb : mCallbacksList) { 156 new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId, 157 workspaceItems, appWidgets, extraItems, orderedScreenIds) 158 .bind(isBindSync, workspaceItemCount); 159 } 160 } 161 162 /** 163 * BindDeepShortcuts is abstract because it is a no-op for the go launcher. 164 */ bindDeepShortcuts()165 public void bindDeepShortcuts() { 166 if (!WIDGETS_ENABLED) { 167 return; 168 } 169 final HashMap<ComponentKey, Integer> shortcutMapCopy; 170 synchronized (mBgDataModel) { 171 shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap); 172 } 173 executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor); 174 } 175 176 /** 177 * Binds the all apps results from LoaderTask to the callbacks UX. 178 */ bindAllApps()179 public void bindAllApps() { 180 // shallow copy 181 AppInfo[] apps = mBgAllAppsList.copyData(); 182 int flags = mBgAllAppsList.getFlags(); 183 Map<PackageUserKey, Integer> packageUserKeytoUidMap = Arrays.stream(apps).collect( 184 Collectors.toMap( 185 appInfo -> new PackageUserKey(appInfo.componentName.getPackageName(), 186 appInfo.user), appInfo -> appInfo.uid, (a, b) -> a)); 187 executeCallbacksTask(c -> c.bindAllApplications(apps, flags, packageUserKeytoUidMap), 188 mUiExecutor); 189 } 190 191 /** 192 * bindWidgets is abstract because it is a no-op for the go launcher. 193 */ bindWidgets()194 public void bindWidgets() { 195 if (!WIDGETS_ENABLED) { 196 return; 197 } 198 final List<WidgetsListBaseEntry> widgets = 199 mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext()); 200 executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor); 201 } 202 203 /** 204 * bindWidgets is abstract because it is a no-op for the go launcher. 205 */ bindSmartspaceWidget()206 public void bindSmartspaceWidget() { 207 if (!WIDGETS_ENABLED) { 208 return; 209 } 210 executeCallbacksTask(c -> c.bindSmartspaceWidget(), mUiExecutor); 211 } 212 213 /** 214 * Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right) 215 */ sortWorkspaceItemsSpatially(InvariantDeviceProfile profile, ArrayList<ItemInfo> workspaceItems)216 protected void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile, 217 ArrayList<ItemInfo> workspaceItems) { 218 final int screenCols = profile.numColumns; 219 final int screenCellCount = profile.numColumns * profile.numRows; 220 Collections.sort(workspaceItems, (lhs, rhs) -> { 221 if (lhs.container == rhs.container) { 222 // Within containers, order by their spatial position in that container 223 switch (lhs.container) { 224 case LauncherSettings.Favorites.CONTAINER_DESKTOP: { 225 int lr = (lhs.screenId * screenCellCount + lhs.cellY * screenCols 226 + lhs.cellX); 227 int rr = (rhs.screenId * screenCellCount + +rhs.cellY * screenCols 228 + rhs.cellX); 229 return Integer.compare(lr, rr); 230 } 231 case LauncherSettings.Favorites.CONTAINER_HOTSEAT: { 232 // We currently use the screen id as the rank 233 return Integer.compare(lhs.screenId, rhs.screenId); 234 } 235 default: 236 if (FeatureFlags.IS_STUDIO_BUILD) { 237 throw new RuntimeException( 238 "Unexpected container type when sorting workspace items."); 239 } 240 return 0; 241 } 242 } else { 243 // Between containers, order by hotseat, desktop 244 return Integer.compare(lhs.container, rhs.container); 245 } 246 }); 247 } 248 executeCallbacksTask(CallbackTask task, Executor executor)249 protected void executeCallbacksTask(CallbackTask task, Executor executor) { 250 executor.execute(() -> { 251 if (mMyBindingId != mBgDataModel.lastBindId) { 252 Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind"); 253 return; 254 } 255 for (Callbacks cb : mCallbacksList) { 256 task.execute(cb); 257 } 258 }); 259 } 260 261 /** 262 * Only used in LoaderTask. 263 */ newIdleLock(Object lock)264 public LooperIdleLock newIdleLock(Object lock) { 265 LooperIdleLock idleLock = new LooperIdleLock(lock, mUiExecutor.getLooper()); 266 // If we are not binding or if the main looper is already idle, there is no reason to wait 267 if (mUiExecutor.getLooper().getQueue().isIdle()) { 268 idleLock.queueIdle(); 269 } 270 return idleLock; 271 } 272 273 private class UnifiedWorkspaceBinder { 274 275 private final Executor mUiExecutor; 276 private final Callbacks mCallbacks; 277 278 private final LauncherAppState mApp; 279 private final BgDataModel mBgDataModel; 280 281 private final int mMyBindingId; 282 private final ArrayList<ItemInfo> mWorkspaceItems; 283 private final ArrayList<LauncherAppWidgetInfo> mAppWidgets; 284 private final IntArray mOrderedScreenIds; 285 private final ArrayList<FixedContainerItems> mExtraItems; 286 UnifiedWorkspaceBinder(Callbacks callbacks, Executor uiExecutor, LauncherAppState app, BgDataModel bgDataModel, int myBindingId, ArrayList<ItemInfo> workspaceItems, ArrayList<LauncherAppWidgetInfo> appWidgets, ArrayList<FixedContainerItems> extraItems, IntArray orderedScreenIds)287 UnifiedWorkspaceBinder(Callbacks callbacks, 288 Executor uiExecutor, 289 LauncherAppState app, 290 BgDataModel bgDataModel, 291 int myBindingId, 292 ArrayList<ItemInfo> workspaceItems, 293 ArrayList<LauncherAppWidgetInfo> appWidgets, 294 ArrayList<FixedContainerItems> extraItems, 295 IntArray orderedScreenIds) { 296 mCallbacks = callbacks; 297 mUiExecutor = uiExecutor; 298 mApp = app; 299 mBgDataModel = bgDataModel; 300 mMyBindingId = myBindingId; 301 mWorkspaceItems = workspaceItems; 302 mAppWidgets = appWidgets; 303 mExtraItems = extraItems; 304 mOrderedScreenIds = orderedScreenIds; 305 } 306 bind(boolean isBindSync, int workspaceItemCount)307 private void bind(boolean isBindSync, int workspaceItemCount) { 308 final IntSet currentScreenIds = 309 mCallbacks.getPagesToBindSynchronously(mOrderedScreenIds); 310 Objects.requireNonNull(currentScreenIds, "Null screen ids provided by " + mCallbacks); 311 312 // Separate the items that are on the current screen, and all the other remaining items 313 ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>(); 314 ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>(); 315 ArrayList<ItemInfo> currentAppWidgets = new ArrayList<>(); 316 ArrayList<ItemInfo> otherAppWidgets = new ArrayList<>(); 317 318 filterCurrentWorkspaceItems(currentScreenIds, mWorkspaceItems, currentWorkspaceItems, 319 otherWorkspaceItems); 320 filterCurrentWorkspaceItems(currentScreenIds, mAppWidgets, currentAppWidgets, 321 otherAppWidgets); 322 final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile(); 323 sortWorkspaceItemsSpatially(idp, currentWorkspaceItems); 324 sortWorkspaceItemsSpatially(idp, otherWorkspaceItems); 325 326 // Tell the workspace that we're about to start binding items 327 executeCallbacksTask(c -> { 328 c.clearPendingBinds(); 329 c.startBinding(); 330 if (enableSmartspaceRemovalToggle()) { 331 c.setIsFirstPagePinnedItemEnabled( 332 mBgDataModel.isFirstPagePinnedItemEnabled); 333 } 334 }, mUiExecutor); 335 336 // Bind workspace screens 337 executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor); 338 339 ItemInflater inflater = mCallbacks.getItemInflater(); 340 341 // Load items on the current page. 342 if (enableWorkspaceInflation() && inflater != null) { 343 inflateAsyncAndBind(currentWorkspaceItems, inflater, mUiExecutor); 344 inflateAsyncAndBind(currentAppWidgets, inflater, mUiExecutor); 345 } else { 346 bindItemsInChunks(currentWorkspaceItems, ITEMS_CHUNK, mUiExecutor); 347 bindItemsInChunks(currentAppWidgets, 1, mUiExecutor); 348 } 349 if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { 350 mExtraItems.forEach(item -> 351 executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor)); 352 } 353 354 RunnableList pendingTasks = new RunnableList(); 355 Executor pendingExecutor = pendingTasks::add; 356 357 RunnableList onCompleteSignal = new RunnableList(); 358 359 if (enableWorkspaceInflation() && inflater != null) { 360 MODEL_EXECUTOR.execute(() -> { 361 inflateAsyncAndBind(otherWorkspaceItems, inflater, pendingExecutor); 362 inflateAsyncAndBind(otherAppWidgets, inflater, pendingExecutor); 363 setupPendingBind(currentScreenIds, pendingExecutor); 364 365 // Wait for the async inflation to complete and then notify the completion 366 // signal on UI thread. 367 MAIN_EXECUTOR.execute(onCompleteSignal::executeAllAndDestroy); 368 }); 369 } else { 370 bindItemsInChunks(otherWorkspaceItems, ITEMS_CHUNK, pendingExecutor); 371 bindItemsInChunks(otherAppWidgets, 1, pendingExecutor); 372 setupPendingBind(currentScreenIds, pendingExecutor); 373 onCompleteSignal.executeAllAndDestroy(); 374 } 375 376 executeCallbacksTask( 377 c -> { 378 if (!enableWorkspaceInflation()) { 379 MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 380 } 381 c.onInitialBindComplete(currentScreenIds, pendingTasks, onCompleteSignal, 382 workspaceItemCount, isBindSync); 383 }, mUiExecutor); 384 } 385 setupPendingBind( IntSet currentScreenIds, Executor pendingExecutor)386 private void setupPendingBind( 387 IntSet currentScreenIds, 388 Executor pendingExecutor) { 389 StringCache cacheClone = mBgDataModel.stringCache.clone(); 390 executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor); 391 392 executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor); 393 pendingExecutor.execute( 394 () -> { 395 MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 396 ItemInstallQueue.INSTANCE.get(mApp.getContext()) 397 .resumeModelPush(FLAG_LOADER_RUNNING); 398 }); 399 } 400 401 /** 402 * Tries to inflate the items asynchronously and bind. Returns true on success or false if 403 * async-binding is not supported in this case. 404 */ inflateAsyncAndBind( List<ItemInfo> items, @NonNull ItemInflater inflater, Executor executor)405 private void inflateAsyncAndBind( 406 List<ItemInfo> items, @NonNull ItemInflater inflater, Executor executor) { 407 if (mMyBindingId != mBgDataModel.lastBindId) { 408 Log.d(TAG, "Too many consecutive reloads, skipping obsolete view inflation"); 409 return; 410 } 411 412 ModelWriter writer = mApp.getModel() 413 .getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null); 414 List<Pair<ItemInfo, View>> bindItems = items.stream().map(i -> 415 Pair.create(i, inflater.inflateItem(i, writer, null))).toList(); 416 executeCallbacksTask(c -> c.bindInflatedItems(bindItems), executor); 417 } 418 bindItemsInChunks( List<ItemInfo> workspaceItems, int chunkCount, Executor executor)419 private void bindItemsInChunks( 420 List<ItemInfo> workspaceItems, int chunkCount, Executor executor) { 421 // Bind the workspace items 422 int count = workspaceItems.size(); 423 for (int i = 0; i < count; i += chunkCount) { 424 final int start = i; 425 final int chunkSize = (i + chunkCount <= count) ? chunkCount : (count - i); 426 executeCallbacksTask( 427 c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false), 428 executor); 429 } 430 } 431 executeCallbacksTask(CallbackTask task, Executor executor)432 protected void executeCallbacksTask(CallbackTask task, Executor executor) { 433 executor.execute(() -> { 434 if (mMyBindingId != mBgDataModel.lastBindId) { 435 Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind"); 436 return; 437 } 438 task.execute(mCallbacks); 439 }); 440 } 441 } 442 443 private class DisjointWorkspaceBinder { 444 private final IntArray mOrderedScreenIds; 445 private final IntSet mCurrentScreenIds = new IntSet(); 446 private final Set<Integer> mBoundItemIds = new HashSet<>(); 447 DisjointWorkspaceBinder(IntArray orderedScreenIds)448 protected DisjointWorkspaceBinder(IntArray orderedScreenIds) { 449 mOrderedScreenIds = orderedScreenIds; 450 451 for (Callbacks cb : mCallbacksList) { 452 mCurrentScreenIds.addAll(cb.getPagesToBindSynchronously(orderedScreenIds)); 453 } 454 if (mCurrentScreenIds.size() == 0) { 455 mCurrentScreenIds.add(Workspace.FIRST_SCREEN_ID); 456 } 457 } 458 459 /** 460 * Binds the currently loaded items in the Data Model. Also signals to the Callbacks[] 461 * that these items have been bound and their respective screens are ready to be shown. 462 * 463 * If this method is called after all the items on the workspace screen have already been 464 * loaded, it will bind all workspace items immediately, and bindOtherWorkspacePages() will 465 * not bind any items. 466 */ bindCurrentWorkspacePages(boolean isBindSync)467 protected void bindCurrentWorkspacePages(boolean isBindSync) { 468 // Save a copy of all the bg-thread collections 469 ArrayList<ItemInfo> workspaceItems; 470 ArrayList<LauncherAppWidgetInfo> appWidgets; 471 ArrayList<FixedContainerItems> fciList = new ArrayList<>(); 472 final int workspaceItemCount; 473 synchronized (mBgDataModel) { 474 workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems); 475 appWidgets = new ArrayList<>(mBgDataModel.appWidgets); 476 if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { 477 mBgDataModel.extraItems.forEach(fciList::add); 478 } 479 workspaceItemCount = mBgDataModel.itemsIdMap.size(); 480 } 481 482 workspaceItems.forEach(it -> mBoundItemIds.add(it.id)); 483 appWidgets.forEach(it -> mBoundItemIds.add(it.id)); 484 if (!FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { 485 fciList.forEach(item -> 486 executeCallbacksTask(c -> c.bindExtraContainerItems(item), mUiExecutor)); 487 } 488 489 sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems); 490 491 // Tell the workspace that we're about to start binding items 492 executeCallbacksTask(c -> { 493 c.clearPendingBinds(); 494 c.startBinding(); 495 }, mUiExecutor); 496 497 // Bind workspace screens 498 executeCallbacksTask(c -> c.bindScreens(mOrderedScreenIds), mUiExecutor); 499 500 bindWorkspaceItems(workspaceItems); 501 bindAppWidgets(appWidgets); 502 executeCallbacksTask(c -> { 503 MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 504 505 RunnableList onCompleteSignal = new RunnableList(); 506 onCompleteSignal.executeAllAndDestroy(); 507 c.onInitialBindComplete(mCurrentScreenIds, new RunnableList(), onCompleteSignal, 508 workspaceItemCount, isBindSync); 509 }, mUiExecutor); 510 } 511 bindOtherWorkspacePages()512 protected void bindOtherWorkspacePages() { 513 // Save a copy of all the bg-thread collections 514 ArrayList<ItemInfo> workspaceItems; 515 ArrayList<LauncherAppWidgetInfo> appWidgets; 516 517 synchronized (mBgDataModel) { 518 workspaceItems = new ArrayList<>(mBgDataModel.workspaceItems); 519 appWidgets = new ArrayList<>(mBgDataModel.appWidgets); 520 } 521 522 workspaceItems.removeIf(it -> mBoundItemIds.contains(it.id)); 523 appWidgets.removeIf(it -> mBoundItemIds.contains(it.id)); 524 525 sortWorkspaceItemsSpatially(mApp.getInvariantDeviceProfile(), workspaceItems); 526 527 bindWorkspaceItems(workspaceItems); 528 bindAppWidgets(appWidgets); 529 530 executeCallbacksTask(c -> c.finishBindingItems(mCurrentScreenIds), mUiExecutor); 531 mUiExecutor.execute(() -> { 532 MODEL_EXECUTOR.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); 533 ItemInstallQueue.INSTANCE.get(mApp.getContext()) 534 .resumeModelPush(FLAG_LOADER_RUNNING); 535 }); 536 537 StringCache cacheClone = mBgDataModel.stringCache.clone(); 538 executeCallbacksTask(c -> c.bindStringCache(cacheClone), mUiExecutor); 539 } 540 bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems)541 private void bindWorkspaceItems(final ArrayList<ItemInfo> workspaceItems) { 542 // Bind the workspace items 543 int count = workspaceItems.size(); 544 for (int i = 0; i < count; i += ITEMS_CHUNK) { 545 final int start = i; 546 final int chunkSize = (i + ITEMS_CHUNK <= count) ? ITEMS_CHUNK : (count - i); 547 executeCallbacksTask( 548 c -> c.bindItems(workspaceItems.subList(start, start + chunkSize), false), 549 mUiExecutor); 550 } 551 } 552 bindAppWidgets(List<LauncherAppWidgetInfo> appWidgets)553 private void bindAppWidgets(List<LauncherAppWidgetInfo> appWidgets) { 554 // Bind the widgets, one at a time 555 int count = appWidgets.size(); 556 for (int i = 0; i < count; i++) { 557 final ItemInfo widget = appWidgets.get(i); 558 executeCallbacksTask( 559 c -> c.bindItems(Collections.singletonList(widget), false), 560 mUiExecutor); 561 } 562 } 563 } 564 } 565