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