• 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.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