1 /* 2 * Copyright (C) 2020 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 package com.android.launcher3.model; 17 18 import static android.text.format.DateUtils.DAY_IN_MILLIS; 19 import static android.text.format.DateUtils.formatElapsedTime; 20 21 import static com.android.launcher3.EncryptionType.ENCRYPTED; 22 import static com.android.launcher3.LauncherPrefs.nonRestorableItem; 23 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION; 24 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION; 25 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION; 26 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; 27 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT; 28 import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle; 29 import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo; 30 import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation; 31 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 32 33 import static java.util.stream.Collectors.toCollection; 34 35 import android.app.StatsManager; 36 import android.app.prediction.AppPredictionContext; 37 import android.app.prediction.AppPredictionManager; 38 import android.app.prediction.AppPredictor; 39 import android.app.prediction.AppTarget; 40 import android.app.prediction.AppTargetEvent; 41 import android.content.Context; 42 import android.content.Intent; 43 import android.content.pm.LauncherActivityInfo; 44 import android.content.pm.LauncherApps; 45 import android.content.pm.ShortcutInfo; 46 import android.os.Bundle; 47 import android.os.UserHandle; 48 import android.util.Log; 49 import android.util.StatsEvent; 50 51 import androidx.annotation.AnyThread; 52 import androidx.annotation.CallSuper; 53 import androidx.annotation.NonNull; 54 import androidx.annotation.Nullable; 55 import androidx.annotation.VisibleForTesting; 56 import androidx.annotation.WorkerThread; 57 58 import com.android.launcher3.ConstantItem; 59 import com.android.launcher3.InvariantDeviceProfile; 60 import com.android.launcher3.LauncherAppState; 61 import com.android.launcher3.LauncherPrefs; 62 import com.android.launcher3.config.FeatureFlags; 63 import com.android.launcher3.logger.LauncherAtom; 64 import com.android.launcher3.logging.InstanceId; 65 import com.android.launcher3.logging.InstanceIdSequence; 66 import com.android.launcher3.model.BgDataModel.FixedContainerItems; 67 import com.android.launcher3.model.data.AppInfo; 68 import com.android.launcher3.model.data.CollectionInfo; 69 import com.android.launcher3.model.data.ItemInfo; 70 import com.android.launcher3.model.data.WorkspaceItemInfo; 71 import com.android.launcher3.pm.UserCache; 72 import com.android.launcher3.shortcuts.ShortcutKey; 73 import com.android.launcher3.util.ApiWrapper; 74 import com.android.launcher3.util.Executors; 75 import com.android.launcher3.util.IntSparseArrayMap; 76 import com.android.launcher3.util.PackageManagerHelper; 77 import com.android.launcher3.util.PersistedItemArray; 78 import com.android.quickstep.logging.SettingsChangeLogger; 79 import com.android.quickstep.logging.StatsLogCompatManager; 80 import com.android.systemui.shared.system.SysUiStatsLog; 81 82 import java.util.ArrayList; 83 import java.util.Collections; 84 import java.util.List; 85 import java.util.Map; 86 import java.util.Objects; 87 import java.util.stream.IntStream; 88 89 /** 90 * Model delegate which loads prediction items 91 */ 92 public class QuickstepModelDelegate extends ModelDelegate { 93 94 private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets"; 95 private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20; 96 97 private static final boolean IS_DEBUG = false; 98 private static final String TAG = "QuickstepModelDelegate"; 99 100 private static final ConstantItem<Long> LAST_SNAPSHOT_TIME_MILLIS = 101 nonRestorableItem("LAST_SNAPSHOT_TIME_MILLIS", 0L, ENCRYPTED); 102 103 @VisibleForTesting 104 final PredictorState mAllAppsState = 105 new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions"); 106 @VisibleForTesting 107 final PredictorState mHotseatState = 108 new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions"); 109 @VisibleForTesting 110 final PredictorState mWidgetsRecommendationState = 111 new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction"); 112 113 private final InvariantDeviceProfile mIDP; 114 private final AppEventProducer mAppEventProducer; 115 private final StatsManager mStatsManager; 116 117 protected boolean mActive = false; 118 QuickstepModelDelegate(Context context)119 public QuickstepModelDelegate(Context context) { 120 super(context); 121 mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent); 122 123 mIDP = InvariantDeviceProfile.INSTANCE.get(context); 124 StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer); 125 mStatsManager = context.getSystemService(StatsManager.class); 126 } 127 128 @CallSuper 129 @Override loadAndBindWorkspaceItems(@onNull UserManagerState ums, @NonNull BgDataModel.Callbacks[] callbacks, @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts)130 public void loadAndBindWorkspaceItems(@NonNull UserManagerState ums, 131 @NonNull BgDataModel.Callbacks[] callbacks, 132 @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { 133 loadAndBindItems(ums, pinnedShortcuts, callbacks, mIDP.numDatabaseHotseatIcons, 134 mHotseatState); 135 } 136 137 @CallSuper 138 @Override loadAndBindAllAppsItems(@onNull UserManagerState ums, @NonNull BgDataModel.Callbacks[] callbacks, @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts)139 public void loadAndBindAllAppsItems(@NonNull UserManagerState ums, 140 @NonNull BgDataModel.Callbacks[] callbacks, 141 @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) { 142 loadAndBindItems(ums, pinnedShortcuts, callbacks, mIDP.numDatabaseAllAppsColumns, 143 mAllAppsState); 144 } 145 146 @WorkerThread loadAndBindItems(@onNull UserManagerState ums, @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, @NonNull BgDataModel.Callbacks[] callbacks, int numColumns, @NonNull PredictorState state)147 private void loadAndBindItems(@NonNull UserManagerState ums, 148 @NonNull Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, 149 @NonNull BgDataModel.Callbacks[] callbacks, 150 int numColumns, @NonNull PredictorState state) { 151 // TODO: Implement caching and preloading 152 153 WorkspaceItemFactory factory = 154 new WorkspaceItemFactory(mApp, ums, mPmHelper, pinnedShortcuts, numColumns, 155 state.containerId); 156 FixedContainerItems fci = new FixedContainerItems(state.containerId, 157 state.storage.read(mApp.getContext(), factory, ums.allUsers::get)); 158 if (FeatureFlags.CHANGE_MODEL_DELEGATE_LOADING_ORDER.get()) { 159 bindPredictionItems(callbacks, fci); 160 } 161 mDataModel.extraItems.put(state.containerId, fci); 162 } 163 164 @CallSuper 165 @Override loadAndBindOtherItems(@onNull BgDataModel.Callbacks[] callbacks)166 public void loadAndBindOtherItems(@NonNull BgDataModel.Callbacks[] callbacks) { 167 FixedContainerItems widgetPredictionFCI = new FixedContainerItems( 168 mWidgetsRecommendationState.containerId, new ArrayList<>()); 169 170 // Widgets prediction isn't used frequently. And thus, it is not persisted on disk. 171 mDataModel.extraItems.put(mWidgetsRecommendationState.containerId, widgetPredictionFCI); 172 173 bindPredictionItems(callbacks, widgetPredictionFCI); 174 loadStringCache(mDataModel.stringCache); 175 } 176 177 @AnyThread bindPredictionItems(@onNull BgDataModel.Callbacks[] callbacks, @NonNull FixedContainerItems fci)178 private void bindPredictionItems(@NonNull BgDataModel.Callbacks[] callbacks, 179 @NonNull FixedContainerItems fci) { 180 Executors.MAIN_EXECUTOR.execute(() -> { 181 for (BgDataModel.Callbacks c : callbacks) { 182 c.bindExtraContainerItems(fci); 183 } 184 }); 185 } 186 187 @Override 188 @WorkerThread bindAllModelExtras(@onNull BgDataModel.Callbacks[] callbacks)189 public void bindAllModelExtras(@NonNull BgDataModel.Callbacks[] callbacks) { 190 Iterable<FixedContainerItems> containerItems; 191 synchronized (mDataModel.extraItems) { 192 containerItems = mDataModel.extraItems.clone(); 193 } 194 Executors.MAIN_EXECUTOR.execute(() -> { 195 for (BgDataModel.Callbacks c : callbacks) { 196 for (FixedContainerItems fci : containerItems) { 197 c.bindExtraContainerItems(fci); 198 } 199 } 200 }); 201 } 202 markActive()203 public void markActive() { 204 super.markActive(); 205 mActive = true; 206 } 207 208 @Override workspaceLoadComplete()209 public void workspaceLoadComplete() { 210 super.workspaceLoadComplete(); 211 recreatePredictors(); 212 } 213 214 @Override 215 @WorkerThread modelLoadComplete()216 public void modelLoadComplete() { 217 super.modelLoadComplete(); 218 219 // Log snapshot of the model 220 LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext()); 221 long lastSnapshotTimeMillis = prefs.get(LAST_SNAPSHOT_TIME_MILLIS); 222 // Log snapshot only if previous snapshot was older than a day 223 long now = System.currentTimeMillis(); 224 if (now - lastSnapshotTimeMillis < DAY_IN_MILLIS) { 225 if (IS_DEBUG) { 226 String elapsedTime = formatElapsedTime((now - lastSnapshotTimeMillis) / 1000); 227 Log.d(TAG, String.format( 228 "Skipped snapshot logging since previous snapshot was %s old.", 229 elapsedTime)); 230 } 231 } else { 232 IntSparseArrayMap<ItemInfo> itemsIdMap; 233 synchronized (mDataModel) { 234 itemsIdMap = mDataModel.itemsIdMap.clone(); 235 } 236 InstanceId instanceId = new InstanceIdSequence().newInstanceId(); 237 for (ItemInfo info : itemsIdMap) { 238 CollectionInfo parent = getContainer(info, itemsIdMap); 239 StatsLogCompatManager.writeSnapshot(info.buildProto(parent), instanceId); 240 } 241 additionalSnapshotEvents(instanceId); 242 prefs.put(LAST_SNAPSHOT_TIME_MILLIS, now); 243 } 244 245 // Only register for launcher snapshot logging if this is the primary ModelDelegate 246 // instance, as there will be additional instances that may be destroyed at any time. 247 if (mIsPrimaryInstance) { 248 registerSnapshotLoggingCallback(); 249 } 250 } 251 additionalSnapshotEvents(InstanceId snapshotInstanceId)252 protected void additionalSnapshotEvents(InstanceId snapshotInstanceId){} 253 254 /** 255 * Registers a callback to log launcher workspace layout using Statsd pulled atom. 256 */ registerSnapshotLoggingCallback()257 protected void registerSnapshotLoggingCallback() { 258 if (mStatsManager == null) { 259 Log.d(TAG, "Failed to get StatsManager"); 260 } 261 262 try { 263 mStatsManager.setPullAtomCallback( 264 SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT, 265 null /* PullAtomMetadata */, 266 MODEL_EXECUTOR, 267 (i, eventList) -> { 268 InstanceId instanceId = new InstanceIdSequence().newInstanceId(); 269 IntSparseArrayMap<ItemInfo> itemsIdMap; 270 synchronized (mDataModel) { 271 itemsIdMap = mDataModel.itemsIdMap.clone(); 272 } 273 274 for (ItemInfo info : itemsIdMap) { 275 CollectionInfo parent = getContainer(info, itemsIdMap); 276 LauncherAtom.ItemInfo itemInfo = info.buildProto(parent); 277 Log.d(TAG, itemInfo.toString()); 278 StatsEvent statsEvent = StatsLogCompatManager.buildStatsEvent(itemInfo, 279 instanceId); 280 eventList.add(statsEvent); 281 } 282 Log.d(TAG, 283 String.format( 284 "Successfully logged %d workspace items with instanceId=%d", 285 itemsIdMap.size(), instanceId.getId())); 286 additionalSnapshotEvents(instanceId); 287 SettingsChangeLogger.INSTANCE.get(mContext).logSnapshot(instanceId); 288 return StatsManager.PULL_SUCCESS; 289 } 290 ); 291 Log.d(TAG, "Successfully registered for launcher snapshot logging!"); 292 } catch (RuntimeException e) { 293 Log.e(TAG, "Failed to register launcher snapshot logging callback with StatsManager", 294 e); 295 } 296 } 297 getContainer( ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap)298 private static CollectionInfo getContainer( 299 ItemInfo info, IntSparseArrayMap<ItemInfo> itemsIdMap) { 300 if (info.container > 0) { 301 ItemInfo containerInfo = itemsIdMap.get(info.container); 302 303 if (!(containerInfo instanceof CollectionInfo)) { 304 Log.e(TAG, String.format( 305 "Item info: %s found with invalid container: %s", 306 info, 307 containerInfo)); 308 } 309 // Allow crash to help debug b/173838775 310 return (CollectionInfo) containerInfo; 311 } 312 return null; 313 } 314 315 @Override validateData()316 public void validateData() { 317 super.validateData(); 318 if (mAllAppsState.predictor != null) { 319 mAllAppsState.predictor.requestPredictionUpdate(); 320 } 321 if (mWidgetsRecommendationState.predictor != null) { 322 mWidgetsRecommendationState.predictor.requestPredictionUpdate(); 323 } 324 } 325 326 @Override destroy()327 public void destroy() { 328 super.destroy(); 329 mActive = false; 330 StatsLogCompatManager.LOGS_CONSUMER.remove(mAppEventProducer); 331 if (mIsPrimaryInstance && mStatsManager != null) { 332 try { 333 mStatsManager.clearPullAtomCallback(SysUiStatsLog.LAUNCHER_LAYOUT_SNAPSHOT); 334 } catch (RuntimeException e) { 335 Log.e(TAG, "Failed to unregister snapshot logging callback with StatsManager", e); 336 } 337 } 338 destroyPredictors(); 339 } 340 destroyPredictors()341 private void destroyPredictors() { 342 mAllAppsState.destroyPredictor(); 343 mHotseatState.destroyPredictor(); 344 mWidgetsRecommendationState.destroyPredictor(); 345 } 346 347 @WorkerThread recreatePredictors()348 private void recreatePredictors() { 349 destroyPredictors(); 350 if (!mActive) { 351 return; 352 } 353 Context context = mApp.getContext(); 354 AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); 355 if (apm == null) { 356 return; 357 } 358 359 registerPredictor(mAllAppsState, apm.createAppPredictionSession( 360 new AppPredictionContext.Builder(context) 361 .setUiSurface("home") 362 .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns) 363 .build())); 364 365 // TODO: get bundle 366 registerHotseatPredictor(apm, context); 367 368 registerWidgetsPredictor(apm.createAppPredictionSession( 369 new AppPredictionContext.Builder(context) 370 .setUiSurface("widgets") 371 .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel)) 372 .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION) 373 .build())); 374 } 375 376 @WorkerThread recreateHotseatPredictor()377 private void recreateHotseatPredictor() { 378 mHotseatState.destroyPredictor(); 379 if (!mActive) { 380 return; 381 } 382 Context context = mApp.getContext(); 383 AppPredictionManager apm = context.getSystemService(AppPredictionManager.class); 384 if (apm == null) { 385 return; 386 } 387 registerHotseatPredictor(apm, context); 388 } 389 registerHotseatPredictor(AppPredictionManager apm, Context context)390 private void registerHotseatPredictor(AppPredictionManager apm, Context context) { 391 registerPredictor(mHotseatState, apm.createAppPredictionSession( 392 new AppPredictionContext.Builder(context) 393 .setUiSurface("hotseat") 394 .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons) 395 .setExtras(convertDataModelToAppTargetBundle(context, mDataModel)) 396 .build())); 397 } 398 registerPredictor(PredictorState state, AppPredictor predictor)399 private void registerPredictor(PredictorState state, AppPredictor predictor) { 400 state.setTargets(Collections.emptyList()); 401 state.predictor = predictor; 402 state.predictor.registerPredictionUpdates( 403 MODEL_EXECUTOR, t -> handleUpdate(state, t)); 404 state.predictor.requestPredictionUpdate(); 405 } 406 handleUpdate(PredictorState state, List<AppTarget> targets)407 private void handleUpdate(PredictorState state, List<AppTarget> targets) { 408 if (state.setTargets(targets)) { 409 // No diff, skip 410 return; 411 } 412 mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets)); 413 } 414 registerWidgetsPredictor(AppPredictor predictor)415 private void registerWidgetsPredictor(AppPredictor predictor) { 416 mWidgetsRecommendationState.predictor = predictor; 417 mWidgetsRecommendationState.predictor.registerPredictionUpdates( 418 MODEL_EXECUTOR, targets -> { 419 if (mWidgetsRecommendationState.setTargets(targets)) { 420 // No diff, skip 421 return; 422 } 423 mApp.getModel().enqueueModelUpdateTask( 424 new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets)); 425 }); 426 mWidgetsRecommendationState.predictor.requestPredictionUpdate(); 427 } 428 429 @VisibleForTesting onAppTargetEvent(AppTargetEvent event, int client)430 void onAppTargetEvent(AppTargetEvent event, int client) { 431 PredictorState state; 432 switch(client) { 433 case CONTAINER_PREDICTION: 434 state = mAllAppsState; 435 break; 436 case CONTAINER_WIDGETS_PREDICTION: 437 state = mWidgetsRecommendationState; 438 break; 439 case CONTAINER_HOTSEAT_PREDICTION: 440 default: 441 state = mHotseatState; 442 break; 443 } 444 if (state.predictor != null) { 445 state.predictor.notifyAppTargetEvent(event); 446 Log.d(TAG, "notifyAppTargetEvent action=" + event.getAction() 447 + " launchLocation=" + event.getLaunchLocation()); 448 if (state == mHotseatState 449 && (event.getAction() == AppTargetEvent.ACTION_PIN 450 || event.getAction() == AppTargetEvent.ACTION_UNPIN)) { 451 // Recreate hot seat predictor when we need to query for hot seat due to pin or 452 // unpin app icons. 453 recreateHotseatPredictor(); 454 } 455 } 456 } 457 getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel)458 private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) { 459 Bundle bundle = new Bundle(); 460 ArrayList<AppTargetEvent> widgetEvents = 461 dataModel.getAllWorkspaceItems().stream() 462 .filter(PredictionHelper::isTrackedForWidgetPrediction) 463 .map(item -> { 464 AppTarget target = getAppTargetFromItemInfo(context, item); 465 if (target == null) return null; 466 return wrapAppTargetWithItemLocation( 467 target, AppTargetEvent.ACTION_PIN, item); 468 }) 469 .filter(Objects::nonNull) 470 .collect(toCollection(ArrayList::new)); 471 bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, widgetEvents); 472 return bundle; 473 } 474 475 static class PredictorState { 476 477 public final int containerId; 478 public final PersistedItemArray<ItemInfo> storage; 479 public AppPredictor predictor; 480 481 private List<AppTarget> mLastTargets; 482 PredictorState(int containerId, String storageName)483 PredictorState(int containerId, String storageName) { 484 this.containerId = containerId; 485 storage = new PersistedItemArray<>(storageName); 486 mLastTargets = Collections.emptyList(); 487 } 488 destroyPredictor()489 public void destroyPredictor() { 490 if (predictor != null) { 491 predictor.destroy(); 492 predictor = null; 493 } 494 } 495 496 /** 497 * Sets the new targets and returns true if it was the same as before. 498 */ setTargets(List<AppTarget> newTargets)499 boolean setTargets(List<AppTarget> newTargets) { 500 List<AppTarget> oldTargets = mLastTargets; 501 mLastTargets = newTargets; 502 503 int size = oldTargets.size(); 504 return size == newTargets.size() && IntStream.range(0, size) 505 .allMatch(i -> areAppTargetsSame(oldTargets.get(i), newTargets.get(i))); 506 } 507 } 508 509 /** 510 * Compares two targets for the properties which we care about 511 */ areAppTargetsSame(AppTarget t1, AppTarget t2)512 private static boolean areAppTargetsSame(AppTarget t1, AppTarget t2) { 513 if (!Objects.equals(t1.getPackageName(), t2.getPackageName()) 514 || !Objects.equals(t1.getUser(), t2.getUser()) 515 || !Objects.equals(t1.getClassName(), t2.getClassName())) { 516 return false; 517 } 518 519 ShortcutInfo s1 = t1.getShortcutInfo(); 520 ShortcutInfo s2 = t2.getShortcutInfo(); 521 if (s1 != null) { 522 if (s2 == null || !Objects.equals(s1.getId(), s2.getId())) { 523 return false; 524 } 525 } else if (s2 != null) { 526 return false; 527 } 528 return true; 529 } 530 531 private static class WorkspaceItemFactory implements PersistedItemArray.ItemFactory<ItemInfo> { 532 533 private final LauncherAppState mAppState; 534 private final UserManagerState mUMS; 535 private final PackageManagerHelper mPmHelper; 536 private final Map<ShortcutKey, ShortcutInfo> mPinnedShortcuts; 537 private final int mMaxCount; 538 private final int mContainer; 539 540 private int mReadCount = 0; 541 WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums, PackageManagerHelper pmHelper, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, int maxCount, int container)542 protected WorkspaceItemFactory(LauncherAppState appState, UserManagerState ums, 543 PackageManagerHelper pmHelper, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts, 544 int maxCount, int container) { 545 mAppState = appState; 546 mUMS = ums; 547 mPmHelper = pmHelper; 548 mPinnedShortcuts = pinnedShortcuts; 549 mMaxCount = maxCount; 550 mContainer = container; 551 } 552 553 @Nullable 554 @Override createInfo(int itemType, UserHandle user, Intent intent)555 public ItemInfo createInfo(int itemType, UserHandle user, Intent intent) { 556 if (mReadCount >= mMaxCount) { 557 return null; 558 } 559 switch (itemType) { 560 case ITEM_TYPE_APPLICATION: { 561 LauncherActivityInfo lai = mAppState.getContext() 562 .getSystemService(LauncherApps.class) 563 .resolveActivity(intent, user); 564 if (lai == null) { 565 return null; 566 } 567 AppInfo info = new AppInfo( 568 lai, 569 UserCache.INSTANCE.get(mAppState.getContext()).getUserInfo(user), 570 ApiWrapper.INSTANCE.get(mAppState.getContext()), 571 mPmHelper, 572 mUMS.isUserQuiet(user)); 573 info.container = mContainer; 574 mAppState.getIconCache().getTitleAndIcon(info, lai, false); 575 mReadCount++; 576 return info.makeWorkspaceItem(mAppState.getContext()); 577 } 578 case ITEM_TYPE_DEEP_SHORTCUT: { 579 ShortcutKey key = ShortcutKey.fromIntent(intent, user); 580 if (key == null) { 581 return null; 582 } 583 ShortcutInfo si = mPinnedShortcuts.get(key); 584 if (si == null) { 585 return null; 586 } 587 WorkspaceItemInfo wii = new WorkspaceItemInfo(si, mAppState.getContext()); 588 wii.container = mContainer; 589 mAppState.getIconCache().getShortcutIcon(wii, si); 590 mReadCount++; 591 return wii; 592 } 593 } 594 return null; 595 } 596 } 597 } 598