android13#launcher3#data load

527 阅读20分钟

1.简介

launcher的数据如何获取的

1.1.备注

这里记录下一些常用的类型id,方便查看数据库的时候知道代表啥

>itemtype:数据类型

  • 0:application,应用图标
  • 2:foler,文件夹
  • 4:appwidget,小部件
  • 6:deep shortcut,就是应用的快捷方式,长按应用图标,弹框里显示的,可以拖动到桌面显示的
        /**
         * The gesture is a package
         */
        public static final int ITEM_TYPE_NON_ACTIONABLE = -1;
        /**
         * The gesture is an application
         */
        public static final int ITEM_TYPE_APPLICATION = 0;

        /**
         * The gesture is an application created shortcut
         */
        public static final int ITEM_TYPE_SHORTCUT = 1;

        /**
         * The favorite is a user created folder
         */
        public static final int ITEM_TYPE_FOLDER = 2;

        /**
         * The favorite is a widget
         */
        public static final int ITEM_TYPE_APPWIDGET = 4;

        /**
         * The favorite is a custom widget provided by the launcher
         */
        public static final int ITEM_TYPE_CUSTOM_APPWIDGET = 5;

        /**
         * The gesture is an application created deep shortcut
         */
        public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;


        // *** Below enum values are used for metrics purpose but not used in Favorites DB ***

        /**
         * Type of the item is recents task.
         */
        public static final int ITEM_TYPE_TASK = 7;

        /**
         * The item is QSB
         */
        public static final int ITEM_TYPE_QSB = 8;

        /**
         * The favorite is a search action
         */
        public static final int ITEM_TYPE_SEARCH_ACTION = 9;

>container id,容器id

        public static final int CONTAINER_DESKTOP = -100;
        public static final int CONTAINER_HOTSEAT = -101;
        public static final int CONTAINER_PREDICTION = -102;
        public static final int CONTAINER_WIDGETS_PREDICTION = -111;
        public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
        public static final int CONTAINER_ALL_APPS = -104;
        public static final int CONTAINER_WIDGETS_TRAY = -105;
        public static final int CONTAINER_BOTTOM_WIDGETS_TRAY = -112;
        public static final int CONTAINER_PIN_WIDGETS = -113;
        public static final int CONTAINER_WALLPAPERS = -114;
        public static final int CONTAINER_SHORTCUTS = -107;
        public static final int CONTAINER_SETTINGS = -108;
        public static final int CONTAINER_TASKSWITCHER = -109;

        // Represents any of the extended containers implemented in non-AOSP variants.
        public static final int EXTENDED_CONTAINERS = -200;

>举例

如下数据库:

  • 前4条数据,container是-101表示是hotseat数据
  • 第五条数据itemType是2,表示是个文件夹,container是-100,表示显示在桌面
  • 6到15条数据的container是5,不在我们上边列出的静态变量里,那么这个5表示的就是第五条数据的id了,他们都是放在文件夹里的
  • 16,17条数据,就是普通的应用图标,显示在桌面
  • 18,19是settings的快捷方式,itemType是6
  • 20是个小部件,itemType是4,

image.png

2.LauncherModel.java

数据加载逻辑就在这个类里,小节4的LauncherAppState是个单例模式,而我们这个类在其构造方法里实例化了一个对象

2.0.构造方法

小节4的构造方法里实例化对象

    LauncherModel(@NonNull final Context context, @NonNull final LauncherAppState app,
            @NonNull final IconCache iconCache, @NonNull final AppFilter appFilter,
            final boolean isPrimaryInstance) {
        mApp = app;
        mBgAllAppsList = new AllAppsList(iconCache, appFilter);
        mModelDelegate = ModelDelegate.newInstance(context, app, mBgAllAppsList, mBgDataModel,
                isPrimaryInstance);
    }

2.1.addCallbacksAndLoad

添加回调并加载数据,数据请求就从这里开始

    public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
        synchronized (mLock) {
            addCallbacks(callbacks);
            //见2.2
            return startLoader(new Callbacks[] { callbacks });

        }
    }

调用的地方有如下两处:

>Launcher.java

桌面启动的时候,onCreate方法里

    protected void onCreate(Bundle savedInstanceState) {
    
        LauncherAppState app = LauncherAppState.getInstance(this);
        mModel = app.getModel();
    //..
        if (!mModel.addCallbacksAndLoad(this)) {
        //..
        }    

        setContentView(getRootView());

>TaskbarViewController.java

Taskbar初始化的时候

    public void init(TaskbarControllers controllers) {
//...
        if (mActivity.isUserSetupComplete()) {
            // Only load the callbacks if user setup is completed
            LauncherAppState.getInstance(mActivity).getModel().addCallbacksAndLoad(mModelCallbacks);
        }

2.2.startLoader

    private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        ItemInstallQueue.INSTANCE.get(mApp.getContext())
                .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
        synchronized (mLock) {
            //stop旧的loader
            boolean wasRunning = stopLoader();
            //数据加载完成
            boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
            boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
            final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;

            if (callbacksList.length > 0) {
                //先清除callback的bind意图
                for (Callbacks cb : callbacksList) {
                    MAIN_EXECUTOR.execute(cb::clearPendingBinds);
                }

                LoaderResults loaderResults = new LoaderResults(
                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
                if (bindDirectly) {
                    //走到这里说明数据已经记载完成,直接用,具体逻辑见小节5对应的方法
                    loaderResults.bindWorkspace(bindAllCallbacks);.
                    loaderResults.bindAllApps();
                    loaderResults.bindDeepShortcuts();
                    loaderResults.bindWidgets();
                    return true;
                } else {
                    stopLoader();
                    //数据的获取都在这个task里,见小节3的run方法
                    mLoaderTask = new LoaderTask(
                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, loaderResults);

                    //执行新的task
                    MODEL_EXECUTOR.post(mLoaderTask);
                }
            }
        }
        return false;
    }

2.3.enqueueModelUpdateTask

把task加入队列,会先执行init方法

    public void enqueueModelUpdateTask(@NonNull final ModelUpdateTask task) {
        if (mModelDestroyed) {
            return;
        }
        task.init(mApp, this, mBgDataModel, mBgAllAppsList, MAIN_EXECUTOR);
        MODEL_EXECUTOR.execute(task);
    }

2.4.onBroadcastIntent

小节4里注册的广播,这里处理下Local的改变,强制重新获取最新的数据。

    public void onBroadcastIntent(@NonNull final Intent intent) {
        final String action = intent.getAction();
        if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
            // If we have changed locale we need to clear out the labels in all apps/workspace.
            forceReload();
        } 

2.5.forceReload

强制重新获取数据

    public void forceReload() {
        synchronized (mLock) {
            // Stop any existing loaders first, so they don't set mModelLoaded to true later
            stopLoader();
            mModelLoaded = false;
        }

        // Start the loader if launcher is already running, otherwise the loader will run,
        // the next time launcher starts
        if (hasCallbacks()) {
            startLoader();
        }
    }

3.LoaderTask

就是个Runnable,所以核心就是run方法了

public class LoaderTask implements Runnable {

3.1.构造方法

    public LoaderTask(LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel dataModel,
            ModelDelegate modelDelegate, LoaderResults results) {
        mApp = app;
        mBgAllAppsList = bgAllAppsList;
        mBgDataModel = dataModel;
        mModelDelegate = modelDelegate;
        mResults = results;

        mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
        mUserManager = mApp.getContext().getSystemService(UserManager.class);
        mUserCache = UserCache.INSTANCE.get(mApp.getContext());
        mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
        mIconCache = mApp.getIconCache();
    }

3.2.run

    public void run() {
        synchronized (this) {
            // Skip fast if we are already stopped.
            if (mStopped) {
                return;
            }
        }
        //见2.2
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            List<ShortcutInfo> allShortcuts = new ArrayList<>();

            try { //加载workspace数据,见3.3
                loadWorkspace(allShortcuts, memoryLogger);
            }

            if (mApp.getInvariantDeviceProfile().dbFile.equals(mDbName)) {
               //清理一些无用的数据
                sanitizeData();
            }

            //workSpace数据加载完成,绑定数据,见5.1
            mResults.bindWorkspace(true /* incrementBindId */);
            //见小节6.2
            mModelDelegate.workspaceLoadComplete();
            // Notify the installer packages of packages with active installs on the first screen.
            sendFirstScreenActiveInstallsBroadcast();


            // second step
            // 第二步:获取allApps数据,绑定数据,加载图标
            List<LauncherActivityInfo> allActivityList;
            try {
               allActivityList = loadAllApps();//见3.4
            }

            mResults.bindAllApps();//见5.2

            IconCacheUpdateHandler updateHandler = mIconCache.getUpdateHandler();
            setIgnorePackages(updateHandler);
            //更新列表里app的图标
            updateHandler.updateIcons(allActivityList,
                    LauncherActivityCachingLogic.newInstance(mApp.getContext()),
                    mApp.getModel()::onPackageIconsUpdated);

            //更新shortcut的图标,集合的数据是在loadWorkSpace里获取的
            updateHandler.updateIcons(allShortcuts, new ShortcutCachingLogic(),
                    mApp.getModel()::onPackageIconsUpdated);


            // third step 
            //第三步: 加载shortcuts数据,绑定数据,更新图标
            List<ShortcutInfo> allDeepShortcuts = loadDeepShortcuts();//见补充代码
            //最终交给对应的callback处理
            mResults.bindDeepShortcuts();
            updateHandler.updateIcons(allDeepShortcuts,
                    new ShortcutCachingLogic(), (pkgs, user) -> { });

            // fourth step
            //第四步:加载widget数据,绑定数据,更新图标
            List<ComponentWithLabelAndIcon> allWidgetsList =
                    mBgDataModel.widgetsModel.update(mApp, null);
            mResults.bindWidgets();
            updateHandler.updateIcons(allWidgetsList,
                    new ComponentWithIconCachingLogic(mApp.getContext(), true),
                    mApp.getModel()::onWidgetLabelsUpdated);


            // fifth step
            //第五步:加载文件夹数据
            loadFolderNames();
            //见小节8对应的方法
            updateHandler.finish();

            mModelDelegate.modelLoadComplete();//空实现
            transaction.commit();//修改数据加载完成的标志

        }
    }

>sendFirstScreenActiveInstallsBroadcast

  • 好像没有地方监听这个广播
    private void sendFirstScreenActiveInstallsBroadcast() {
        ArrayList<ItemInfo> firstScreenItems = new ArrayList<>();
        ArrayList<ItemInfo> allItems = mBgDataModel.getAllWorkspaceItems();

        // Screen set is never empty
        IntArray allScreens = mBgDataModel.collectWorkspaceScreens();
        //获取首屏id
        final int firstScreen = allScreens.get(0);
        IntSet firstScreens = IntSet.wrap(firstScreen);
    //把allItems数据分组,显示在首屏的放到firstScreenItems里,
        filterCurrentWorkspaceItems(firstScreens, allItems, firstScreenItems,
                new ArrayList<>() /* otherScreenItems are ignored */);
        mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
    }

3.3.loadWorkspace

这个是核心方法,获取数据,具体分析见之前的帖子

  • LauncherSettings.Settings.call执行的操作,最终在小节9里实现,可以搜索方法名找到对应的操作
  • 这里用到的Uri是是 : "content://com.android.launcher3.settings/favorites"
    private void loadWorkspace(List<ShortcutInfo> allDeepShortcuts, LoaderMemoryLogger logger) {
        loadWorkspace(allDeepShortcuts, LauncherSettings.Favorites.CONTENT_URI,
                null /* selection */, logger);
}

    protected void loadWorkspace(
            List<ShortcutInfo> allDeepShortcuts,
            Uri contentUri,
            String selection,
            @Nullable LoaderMemoryLogger logger) {
        final Context context = mApp.getContext();
        final ContentResolver contentResolver = context.getContentResolver();
        final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
        final boolean isSafeMode = pmHelper.isSafeMode();
        final boolean isSdCardReady = Utilities.isBootCompleted();
        final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);

        boolean clearDb = false;
        if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
            // Migration failed. Clear workspace.
            clearDb = true;
        }

        if (clearDb) {
            LauncherSettings.Settings.call(contentResolver,
                    LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
        }

        //具体逻辑见[之前的帖子]的2.2小节
        LauncherSettings.Settings.call(contentResolver,
                LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES);

        synchronized (mBgDataModel) {
            mBgDataModel.clear();
            mPendingPackages.clear();
            //获取正在安装中的包信息
            final HashMap<PackageUserKey, SessionInfo> installingPkgs =
                    mSessionHelper.getActiveSessions();
            installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);

            final PackageUserKey tempPackageKey = new PackageUserKey(null, null);
            mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);

            Map<ShortcutKey, ShortcutInfo> shortcutKeyToPinnedShortcuts = new HashMap<>();
            //根据Uri获取cursor
            final LoaderCursor c = new LoaderCursor(
                    contentResolver.query(contentUri, null, selection, null, null), contentUri,
                    mApp, mUserManagerState);
            final Bundle extras = c.getExtras();
            mDbName = extras == null
                    ? null : extras.getString(LauncherSettings.Settings.EXTRA_DB_NAME);
            try {
                final int appWidgetIdIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.APPWIDGET_ID);
                final int appWidgetProviderIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.APPWIDGET_PROVIDER);
                final int spanXIndex = c.getColumnIndexOrThrow
                        (LauncherSettings.Favorites.SPANX);
                final int spanYIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.SPANY);
                final int rankIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.RANK);
                final int optionsIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.OPTIONS);
                final int sourceContainerIndex = c.getColumnIndexOrThrow(
                        LauncherSettings.Favorites.APPWIDGET_SOURCE);

                final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();

                mUserManagerState.init(mUserCache, mUserManager);

                for (UserHandle user : mUserCache.getUserProfiles()) {
                    long serialNo = mUserCache.getSerialNumberForUser(user);
                    boolean userUnlocked = mUserManager.isUserUnlocked(user);

                    // We can only query for shortcuts when the user is unlocked.
                    if (userUnlocked) {
                    //查找所有pinned状态的快捷方式存入map,后续用
                    //pinned的就是已经在桌面上的,
                        QueryResult pinnedShortcuts = new ShortcutRequest(context, user)
                                .query(ShortcutRequest.PINNED);
                        if (pinnedShortcuts.wasSuccess()) {
                            for (ShortcutInfo shortcut : pinnedShortcuts) {
                                shortcutKeyToPinnedShortcuts.put(ShortcutKey.fromInfo(shortcut),
                                        shortcut);
                            }
                        } else {
                            // Shortcut manager can fail due to some race condition when the
                            // lock state changes too frequently. For the purpose of the loading
                            // shortcuts, consider the user is still locked.
                            userUnlocked = false;
                        }
                    }
                    //记录对应的用户是否已经unlock,只有unlock的才处理数据
                    unlockedUsers.put(serialNo, userUnlocked);
                }

                WorkspaceItemInfo info;
                LauncherAppWidgetInfo appWidgetInfo;
                LauncherAppWidgetProviderInfo widgetProviderInfo;
                Intent intent;
                String targetPkg;
                List<IconRequestInfo<WorkspaceItemInfo>> iconRequestInfos = new ArrayList<>();
        //根据数据库的cursor,依次读取处理数据
                while (!mStopped && c.moveToNext()) {
                    try {
                        if (c.user == null) {
                            // User has been deleted, remove the item.
                            c.markDeleted("User has been deleted");
                            continue;
                        }

                        boolean allowMissingTarget = false;
                        //下边就是根据itemType封装不同的info
                        switch (c.itemType) {
                        case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
                        case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
                        case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
                        //下边就是解析数据,判断是否有效,最终拿到info
                            intent = c.parseIntent();
                            if (intent == null) {
                                c.markDeleted("Invalid or null intent");
                                continue;
                            }

                            int disabledState = mUserManagerState.isUserQuiet(c.serialNumber)
                                    ? WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER : 0;
                            ComponentName cn = intent.getComponent();
                            targetPkg = cn == null ? intent.getPackage() : cn.getPackageName();

                            if (TextUtils.isEmpty(targetPkg) &&
                                    c.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) {
                                c.markDeleted("Only legacy shortcuts can have null package");
                                continue;
                            }

                            // If there is no target package, its an implicit intent
                            // (legacy shortcut) which is always valid
                            boolean validTarget = TextUtils.isEmpty(targetPkg) ||
                                    mLauncherApps.isPackageEnabled(targetPkg, c.user);

                            // If it's a deep shortcut, we'll use pinned shortcuts to restore it
                            if (cn != null && validTarget && c.itemType
                                    != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                                // If the apk is present and the shortcut points to a specific
                                // component.

                                // If the component is already present
                                if (mLauncherApps.isActivityEnabled(cn, c.user)) {
                                    // no special handling necessary for this item
                                    c.markRestored();
                                } else {
                                    // Gracefully try to find a fallback activity.
                                    intent = pmHelper.getAppLaunchIntent(targetPkg, c.user);
                                    if (intent != null) {
                                        c.restoreFlag = 0;
                                        c.updater().put(
                                                LauncherSettings.Favorites.INTENT,
                                                intent.toUri(0)).commit();
                                        cn = intent.getComponent();
                                    } else {
                                        c.markDeleted("Unable to find a launch target");
                                        continue;
                                    }
                                }
                            }
                            // else if cn == null => can't infer much, leave it
                            // else if !validPkg => could be restored icon or missing sd-card

                            if (!TextUtils.isEmpty(targetPkg) && !validTarget) {
                                // Points to a valid app (superset of cn != null) but the apk
                                // is not available.

                                if (c.restoreFlag != 0) {
                                    // Package is not yet available but might be
                                    // installed later.
                                    FileLog.d(TAG, "package not yet restored: " + targetPkg);

                                    tempPackageKey.update(targetPkg, c.user);
                                    if (c.hasRestoreFlag(WorkspaceItemInfo.FLAG_RESTORE_STARTED)) {
                                        // Restore has started once.
                                    } else if (installingPkgs.containsKey(tempPackageKey)) {
                                        // App restore has started. Update the flag
                                        c.restoreFlag |= WorkspaceItemInfo.FLAG_RESTORE_STARTED;
                                        c.updater().put(LauncherSettings.Favorites.RESTORED,
                                                c.restoreFlag).commit();
                                    } else {
                                        c.markDeleted("Unrestored app removed: " + targetPkg);
                                        continue;
                                    }
                                } else if (pmHelper.isAppOnSdcard(targetPkg, c.user)) {
                                    // Package is present but not available.
                                    disabledState |= WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE;
                                    // Add the icon on the workspace anyway.
                                    allowMissingTarget = true;
                                } else if (!isSdCardReady) {
                                    // SdCard is not ready yet. Package might get available,
                                    // once it is ready.
                                    Log.d(TAG, "Missing pkg, will check later: " + targetPkg);
                                    mPendingPackages.add(new PackageUserKey(targetPkg, c.user));
                                    // Add the icon on the workspace anyway.
                                    allowMissingTarget = true;
                                } else {
                                    // Do not wait for external media load anymore.
                                    c.markDeleted("Invalid package removed: " + targetPkg);
                                    continue;
                                }
                            }

                            if ((c.restoreFlag & WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI) != 0) {
                                validTarget = false;
                            }

                            if (validTarget) {
                                // The shortcut points to a valid target (either no target
                                // or something which is ready to be used)
                                c.markRestored();
                            }

                            boolean useLowResIcon = !c.isOnWorkspaceOrHotseat();

                            if (c.restoreFlag != 0) {
                                // Already verified above that user is same as default user
                                info = c.getRestoredItemInfo(intent);
                            } else if (c.itemType ==
                                    LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
                                info = c.getAppShortcutInfo(
                                        intent,
                                        allowMissingTarget,
                                        useLowResIcon,
                                        //最后一个参数loadicon是false,所以这里不加载图标和title
                                        !FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get());
                            } else if (c.itemType ==
                                    LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {

                                ShortcutKey key = ShortcutKey.fromIntent(intent, c.user);
                                //上边记录的用户unlock状态,为true才处理数据
                                if (unlockedUsers.get(c.serialNumber)) {
                                //这个map也是前边unlock状态的时候存入的,
                                    ShortcutInfo pinnedShortcut =
                                            shortcutKeyToPinnedShortcuts.get(key);
                                    if (pinnedShortcut == null) {
                                        // The shortcut is no longer valid.
                                        c.markDeleted("Pinned shortcut not found");
                                        continue;
                                    }
                                    info = new WorkspaceItemInfo(pinnedShortcut, context);
                                    // If the pinned deep shortcut is no longer published,
                                    // use the last saved icon instead of the default.
                                    mIconCache.getShortcutIcon(info, pinnedShortcut, c::loadIcon);

                                    if (pmHelper.isAppSuspended(
                                            pinnedShortcut.getPackage(), info.user)) {
                                        info.runtimeStatusFlags |= FLAG_DISABLED_SUSPENDED;
                                    }
                                    intent = info.getIntent();
                                    allDeepShortcuts.add(pinnedShortcut);
                                } else {
                                    // Create a shortcut info in disabled mode for now.
                                    info = c.loadSimpleWorkspaceItem();
                                    info.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
                                }
                            } else { // item type == ITEM_TYPE_SHORTCUT
                            //最新的代码已经移除这种type了,所以不看了。
                                info = c.loadSimpleWorkspaceItem();

                                // Shortcuts are only available on the primary profile
                                if (!TextUtils.isEmpty(targetPkg)
                                        && pmHelper.isAppSuspended(targetPkg, c.user)) {
                                    disabledState |= FLAG_DISABLED_SUSPENDED;
                                }
                                info.options = c.getInt(optionsIndex);

                                // App shortcuts that used to be automatically added to Launcher
                                // didn't always have the correct intent flags set, so do that
                                // here
                                if (intent.getAction() != null &&
                                    intent.getCategories() != null &&
                                    intent.getAction().equals(Intent.ACTION_MAIN) &&
                                    intent.getCategories().contains(Intent.CATEGORY_LAUNCHER)) {
                                    intent.addFlags(
                                        Intent.FLAG_ACTIVITY_NEW_TASK |
                                        Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
                                }
                            }

                            if (info != null) {
                                if (info.itemType
                                        != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
                                    // Skip deep shortcuts; their title and icons have already been
                                    // loaded above.
                                    //图标的请求加入集合,后续一起处理
                                    iconRequestInfos.add(
                                            c.createIconRequestInfo(info, useLowResIcon));
                                }

                                c.applyCommonProperties(info);

                                info.intent = intent;
                                info.rank = c.getInt(rankIndex);
                                info.spanX = 1;
                                info.spanY = 1;
                                info.runtimeStatusFlags |= disabledState;
                                if (isSafeMode && !isSystemApp(context, intent)) {
                                    info.runtimeStatusFlags |= FLAG_DISABLED_SAFEMODE;
                                }
                                    LauncherActivityInfo activityInfo = c.getLauncherActivityInfo();
                                    if (activityInfo != null) {
                                        info.setProgressLevel(
                                                PackageManagerHelper
                                                    .getLoadingProgress(activityInfo),
                                                PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
                                    }

                                if (c.restoreFlag != 0 && !TextUtils.isEmpty(targetPkg)) {
                                    tempPackageKey.update(targetPkg, c.user);
                                    SessionInfo si = installingPkgs.get(tempPackageKey);
                                        if (si == null) {
                                            info.runtimeStatusFlags &=
                                                ~ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE;
                                        } else if (activityInfo == null) {
                                            int installProgress = (int) (si.getProgress() * 100);

                                            info.setProgressLevel(
                                                    installProgress,
                                                    PackageInstallInfo.STATUS_INSTALLING);
                                        }
                                }
            //数据放入model,方便后续使用
                                c.checkAndAddItem(info, mBgDataModel, logger);
                            } else {
                                throw new RuntimeException("Unexpected null WorkspaceItemInfo");
                            }
                            break;

                        case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
                            FolderInfo folderInfo = mBgDataModel.findOrMakeFolder(c.id);
                            //把cursor读取的信息放入info里
                            c.applyCommonProperties(folderInfo);

                            // Do not trim the folder label, as is was set by the user.
                            folderInfo.title = c.getString(c.titleIndex);
                            folderInfo.spanX = 1;
                            folderInfo.spanY = 1;
                            folderInfo.options = c.getInt(optionsIndex);

                            // no special handling required for restored folders
                            c.markRestored();
                    //数据放入model
                            c.checkAndAddItem(folderInfo, mBgDataModel, logger);
                            break;

                        case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
                        //小部件类型的
                            if (WidgetsModel.GO_DISABLE_WIDGETS) {
                                c.markDeleted("Only legacy shortcuts can have null package");
                                continue;
                            }
                            // Follow through
                        case LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
                            // Read all Launcher-specific widget details
                            boolean customWidget = c.itemType ==
                                LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;

                            int appWidgetId = c.getInt(appWidgetIdIndex);
                            String savedProvider = c.getString(appWidgetProviderIndex);
                            final ComponentName component;

                            boolean isSearchWidget = (c.getInt(optionsIndex)
                                    & LauncherAppWidgetInfo.OPTION_SEARCH_WIDGET) != 0;
                            if (isSearchWidget) {
                            //如果标记的是搜索小部件的,那获取默认的搜索组件
                                component  = QsbContainerView.getSearchComponentName(context);
                                if (component == null) {
                                    c.markDeleted("Discarding SearchWidget without packagename ");
                                    continue;
                                }
                            } else {
                            //否则根据提供的provider获取组件
                                component = ComponentName.unflattenFromString(savedProvider);
                            }
                            final boolean isIdValid = !c.hasRestoreFlag(
                                    LauncherAppWidgetInfo.FLAG_ID_NOT_VALID);
                            final boolean wasProviderReady = !c.hasRestoreFlag(
                                    LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY);

                            ComponentKey providerKey = new ComponentKey(component, c.user);
                            if (!mWidgetProvidersMap.containsKey(providerKey)) {
                                mWidgetProvidersMap.put(providerKey,
                                        widgetHelper.findProvider(component, c.user));
                            }
                            final AppWidgetProviderInfo provider =
                                    mWidgetProvidersMap.get(providerKey);

                            final boolean isProviderReady = isValidProvider(provider);
                            if (!isSafeMode && !customWidget &&
                                    wasProviderReady && !isProviderReady) {
                                c.markDeleted(
                                        "Deleting widget that isn't installed anymore: "
                                        + provider);
                            } else {
                                if (isProviderReady) {
                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                            provider.provider);

                                    // The provider is available. So the widget is either
                                    // available or not available. We do not need to track
                                    // any future restore updates.
                                    int status = c.restoreFlag &
                                            ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED &
                                            ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
                                    if (!wasProviderReady) {
                                        // If provider was not previously ready, update the
                                        // status and UI flag.

                                        // Id would be valid only if the widget restore broadcast was received.
                                        if (isIdValid) {
                                            status |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
                                        }
                                    }
                                    appWidgetInfo.restoreStatus = status;
                                } else {
                                    appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
                                            component);
                                    appWidgetInfo.restoreStatus = c.restoreFlag;

                                    tempPackageKey.update(component.getPackageName(), c.user);
                                    SessionInfo si =
                                            installingPkgs.get(tempPackageKey);
                                    Integer installProgress = si == null
                                            ? null
                                            : (int) (si.getProgress() * 100);

                                    if (c.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_RESTORE_STARTED)) {
                                        // Restore has started once.
                                    } else if (installProgress != null) {
                                        // App restore has started. Update the flag
                                        appWidgetInfo.restoreStatus |=
                                                LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
                                    } else if (!isSafeMode) {
                                        c.markDeleted("Unrestored widget removed: " + component);
                                        continue;
                                    }

                                    appWidgetInfo.installProgress =
                                            installProgress == null ? 0 : installProgress;
                                }
                                if (appWidgetInfo.hasRestoreFlag(
                                        LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG)) {
                                    appWidgetInfo.bindOptions = c.parseIntent();
                                }

                                c.applyCommonProperties(appWidgetInfo);
                                appWidgetInfo.spanX = c.getInt(spanXIndex);
                                appWidgetInfo.spanY = c.getInt(spanYIndex);
                                appWidgetInfo.options = c.getInt(optionsIndex);
                                appWidgetInfo.user = c.user;
                                appWidgetInfo.sourceContainer = c.getInt(sourceContainerIndex);

                                if (appWidgetInfo.spanX <= 0 || appWidgetInfo.spanY <= 0) {
                                //跨度不正常,删除
                                    c.markDeleted("Widget has invalid size: "
                                            + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
                                    continue;
                                }
                                widgetProviderInfo =
                                        widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
                                if (widgetProviderInfo != null
                                        && (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
                                        || appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
                                    logWidgetInfo(mApp.getInvariantDeviceProfile(),
                                            widgetProviderInfo);
                                }
                                if (!c.isOnWorkspaceOrHotseat()) {
                                //小部件的容器只能是workspace或者hotseat,不是的话,删除数据
                                    c.markDeleted("Widget found where container != " +
                                            "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
                                    continue;
                                }

                                if (!customWidget) {
                                    String providerName =
                                            appWidgetInfo.providerName.flattenToString();
                                    if (!providerName.equals(savedProvider) ||
                                            (appWidgetInfo.restoreStatus != c.restoreFlag)) {
                                        c.updater()
                                                .put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
                                                        providerName)
                                                .put(LauncherSettings.Favorites.RESTORED,
                                                        appWidgetInfo.restoreStatus)
                                                .commit();
                                    }
                                }

                                if (appWidgetInfo.restoreStatus !=
                                        LauncherAppWidgetInfo.RESTORE_COMPLETED) {
                                    appWidgetInfo.pendingItemInfo = WidgetsModel.newPendingItemInfo(
                                            mApp.getContext(),
                                            appWidgetInfo.providerName,
                                            appWidgetInfo.user);
                                    mIconCache.getTitleAndIconForApp(
                                            appWidgetInfo.pendingItemInfo, false);
                                }

                                c.checkAndAddItem(appWidgetInfo, mBgDataModel);
                            }
                            break;
                        }
                    } 
                }
                //这个值前边itemType是application的时候用过,为false才加载图标,为true不加载,默认为ture
                if (FeatureFlags.ENABLE_BULK_WORKSPACE_ICON_LOADING.get()) {
                    //这里统一加载图标以及title,放在app_icons.db数据库里,具体见IconCache.java类
                    try {
                        mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
                        for (IconRequestInfo<WorkspaceItemInfo> iconRequestInfo :
                                iconRequestInfos) {
                            WorkspaceItemInfo wai = iconRequestInfo.itemInfo;
                            if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
                                iconRequestInfo.loadWorkspaceIcon(mApp.getContext());
                            }
                        }
                    } finally {
                        Trace.endSection();
                    }
                }
            } finally {
                IOUtils.closeSilently(c);
            }

            // Load delegate items,见6.3,加载本地的extra数据,
            mModelDelegate.loadItems(mUserManagerState, shortcutKeyToPinnedShortcuts);

            // Load string cache
            mModelDelegate.loadStringCache(mBgDataModel.stringCache);

            // Break early if we've stopped loading
            if (mStopped) {
                mBgDataModel.clear();
                return;
            }

            // Remove dead items
            mItemsDeleted = c.commitDeleted();

            // Sort the folder items, update ranks, and make sure all preview items are high res.
            FolderGridOrganizer verifier =
                    new FolderGridOrganizer(mApp.getInvariantDeviceProfile());
            for (FolderInfo folder : mBgDataModel.folders) {
                Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
                verifier.setFolderInfo(folder);
                int size = folder.contents.size();

                // Update ranks here to ensure there are no gaps caused by removed folder items.
                // Ranks are the source of truth for folder items, so cellX and cellY can be ignored
                // for now. Database will be updated once user manually modifies folder.
                for (int rank = 0; rank < size; ++rank) {
                    WorkspaceItemInfo info = folder.contents.get(rank);
                    info.rank = rank;

                    if (info.usingLowResIcon()
                            && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
                            && verifier.isItemInPreview(info.rank)) {
                        mIconCache.getTitleAndIcon(info, false);
                    }
                }
            }

            c.commitRestoredItems();
        }
    }

>pinned shortcut

长按应用图标,比如settings,弹出的菜单里可以看到wifi ,battery等快捷方式,长按就可以拖动放到桌面上,这个图标就是上边的pinned shortcut

3.4.loadAllApps

    private List<LauncherActivityInfo> loadAllApps() {
        final List<UserHandle> profiles = mUserCache.getUserProfiles();
        List<LauncherActivityInfo> allActivityList = new ArrayList<>();
        // Clear the list of apps
        mBgAllAppsList.clear();

        List<IconRequestInfo<AppInfo>> iconRequestInfos = new ArrayList<>();
        for (UserHandle user : profiles) {
            // 查找对应用户的app列表
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            if (apps == null || apps.isEmpty()) {
                return allActivityList;
            }
            boolean quietMode = mUserManagerState.isUserQuiet(user);
            // Create the ApplicationInfos
            for (int i = 0; i < apps.size(); i++) {
                LauncherActivityInfo app = apps.get(i);
                AppInfo appInfo = new AppInfo(app, user, quietMode);
                //这个是app图标加载的请求集合
                iconRequestInfos.add(new IconRequestInfo<>(
                        appInfo, app, /* useLowResIcon= */ false));
                mBgAllAppsList.add(
                        appInfo, app, !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());
            }
            //app列表加入集合,
            allActivityList.addAll(apps);
        }


        if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
            // get all active sessions and add them to the all apps list
            for (PackageInstaller.SessionInfo info :
                    mSessionHelper.getAllVerifiedSessions()) {
                AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
                        mApp.getContext(),
                        PackageInstallInfo.fromInstallingState(info),
                        !FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get());

                if (promiseAppInfo != null) {
                    iconRequestInfos.add(new IconRequestInfo<>(
                            promiseAppInfo,
                            /* launcherActivityInfo= */ null,
                            promiseAppInfo.usingLowResIcon()));
                }
            }
        }

        if (FeatureFlags.ENABLE_BULK_ALL_APPS_ICON_LOADING.get()) {
            try {
            //请求加载图标
                mIconCache.getTitlesAndIconsInBulk(iconRequestInfos);
                iconRequestInfos.forEach(iconRequestInfo ->
                        mBgAllAppsList.updateSectionName(iconRequestInfo.itemInfo));
            } 
        }

    //设置对应的flag
        mBgAllAppsList.setFlags(FLAG_QUIET_MODE_ENABLED,
                mUserManagerState.isAnyProfileQuietModeEnabled());
        mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
                hasShortcutsPermission(mApp.getContext()));
        mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
                mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
                        == PackageManager.PERMISSION_GRANTED);

        mBgAllAppsList.getAndResetChangeFlag();
        return allActivityList;
    }

3.5.loadDeepShortcuts

    private List<ShortcutInfo> loadDeepShortcuts() {
        List<ShortcutInfo> allShortcuts = new ArrayList<>();
        mBgDataModel.deepShortcutMap.clear();
        //先判断是否有获取shortcut的权限,3.4末尾设置的flag
        if (mBgAllAppsList.hasShortcutHostPermission()) {
            for (UserHandle user : mUserCache.getUserProfiles()) {
                if (mUserManager.isUserUnlocked(user)) {
                    //查找所有的app支持的shortcuts
                    List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
                            .query(ShortcutRequest.ALL);
                    allShortcuts.addAll(shortcuts);
                    mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
                }
            }
        }
        return allShortcuts;
    }

3.6.loadFolderNames

    private void loadFolderNames() {
        FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
                mBgAllAppsList.data, mBgDataModel.folders);

        synchronized (mBgDataModel) {
            for (int i = 0; i < mBgDataModel.folders.size(); i++) {
                FolderNameInfos suggestionInfos = new FolderNameInfos();
                FolderInfo info = mBgDataModel.folders.valueAt(i);
                if (info.suggestedFolderNames == null) {
                    provider.getSuggestedFolderName(mApp.getContext(), info.contents,
                            suggestionInfos);
                    info.suggestedFolderNames = suggestionInfos;
                }
            }
        }
    }

4.LauncherAppState.java

4.1.构造方法

    public LauncherAppState(Context context) {
    //监听配置改变
        mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> {
            if (modelPropertiesChanged) {
                refreshAndReloadLauncher();
            }
        });
    //注册app改变监听
        mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
    //注册广播,监听语言改变,见2.4
        SimpleBroadcastReceiver modelChangeReceiver =
                new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
        modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
                Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                Intent.ACTION_MANAGED_PROFILE_UNLOCKED,
                ACTION_DEVICE_POLICY_RESOURCE_UPDATED);    
    public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
        mContext = context;

        mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
        mIconProvider = new LauncherIconProvider(context);
        mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
                iconCacheFileName, mIconProvider);
        //这里实例化小节2的model
        mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
                iconCacheFileName != null);
        mOnTerminateCallback.add(mIconCache::close);
    }

>单例模式

    // We do not need any synchronization for this variable as its only written on UI thread.
    public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
            new MainThreadInitializedObject<>(LauncherAppState::new);

    public static LauncherAppState getInstance(final Context context) {
        return INSTANCE.get(context);
    }

5.BaseLoaderResults.java

5.1.bindWorkspace

在ui线程里加载数据到对应的view上

    public void bindWorkspace(boolean incrementBindId) {
        // Save a copy of all the bg-thread collections
        ArrayList<ItemInfo> workspaceItems = new ArrayList<>();
        ArrayList<LauncherAppWidgetInfo> appWidgets = new ArrayList<>();
        final IntArray orderedScreenIds = new IntArray();
        //这个就是预测的items的集合,比如常用一个app,在hotseat第一个位置图标可能就变成这个app了
        ArrayList<FixedContainerItems> extraItems = new ArrayList<>();

        synchronized (mBgDataModel) {
        //copy相关的数据到新的集合里
            workspaceItems.addAll(mBgDataModel.workspaceItems);
            appWidgets.addAll(mBgDataModel.appWidgets);
            orderedScreenIds.addAll(mBgDataModel.collectWorkspaceScreens());
            mBgDataModel.extraItems.forEach(extraItems::add);
            if (incrementBindId) {
                mBgDataModel.lastBindId++;
            }
            mMyBindingId = mBgDataModel.lastBindId;
        }
        //循环所有的回调
        for (Callbacks cb : mCallbacksList) {
        //把数据交给对应的callback处理
            new WorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
                    workspaceItems, appWidgets, extraItems, orderedScreenIds).bind();
        }
    }

5.2.bindAllApps

把allapps数据以及对应的flags传递给回调。

    public void bindAllApps() {
        // shallow copy
        AppInfo[] apps = mBgAllAppsList.copyData();
        int flags = mBgAllAppsList.getFlags();
        executeCallbacksTask(c -> c.bindAllApplications(apps, flags), mUiExecutor);
    }

5.3.bindDeepShortcuts

把数据给到对应的callback里处理,父类LoaderResults实现

    public void bindDeepShortcuts() {
        final HashMap<ComponentKey, Integer> shortcutMapCopy;
        synchronized (mBgDataModel) {
            shortcutMapCopy = new HashMap<>(mBgDataModel.deepShortcutMap);
        }
        executeCallbacksTask(c -> c.bindDeepShortcutMap(shortcutMapCopy), mUiExecutor);
    }

5.4.bindWidgets

把数据给到对应的callback里处理,子类LoaderResults实现(有两个同名类,默认的空实现,override目录里实现)

    public void bindWidgets() {
        final List<WidgetsListBaseEntry> widgets =
                mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext());
        executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
    }

5.5.executeCallbacksTask

这个方法的作用,就是在给定的executor里执行所有callback的task里的方法

    protected void executeCallbacksTask(CallbackTask task, Executor executor) {
        executor.execute(() -> {
            if (mMyBindingId != mBgDataModel.lastBindId) {
                Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
                return;
            }
            for (Callbacks cb : mCallbacksList) {
                task.execute(cb);
            }
        });
    }

6.QuickstepModelDelegate.java

父类有个静态方法实例化的,

    public static ModelDelegate newInstance(
            Context context, LauncherAppState app, AllAppsList appsList, BgDataModel dataModel,
            boolean isPrimaryInstance) {
            //model_delegate_class这个string里配置的类名,反射实例化的
        ModelDelegate delegate = Overrides.getObject(
                ModelDelegate.class, context, R.string.model_delegate_class);
        delegate.init(context, app, appsList, dataModel, isPrimaryInstance);
        return delegate;
    }

6.1.构造方法

    public QuickstepModelDelegate(Context context) {
        mContext = context;
        mAppEventProducer = new AppEventProducer(context, this::onAppTargetEvent);

        mIDP = InvariantDeviceProfile.INSTANCE.get(context);
        StatsLogCompatManager.LOGS_CONSUMER.add(mAppEventProducer);
        mStatsManager = context.getSystemService(StatsManager.class);
    }

6.2.workspaceLoadComplete

    public void workspaceLoadComplete() {
        super.workspaceLoadComplete();
        recreatePredictors();
    }

6.4.recreatePredictors

重新创建新的预测器,简单理解为常用的应用,排名较高,就会推荐你使用

    //-102 ,后边的参数就是本地保存的文件名,见小节7
    private final PredictorState mAllAppsState =
            new PredictorState(CONTAINER_PREDICTION, "all_apps_predictions");
    //-103
    private final PredictorState mHotseatState =
            new PredictorState(CONTAINER_HOTSEAT_PREDICTION, "hotseat_predictions");
    //-111
    private final PredictorState mWidgetsRecommendationState =
            new PredictorState(CONTAINER_WIDGETS_PREDICTION, "widgets_prediction");

    private void recreatePredictors() {
        destroyPredictors();
        if (!mActive) {
            return;
        }
        Context context = mApp.getContext();
        AppPredictionManager apm = context.getSystemService(AppPredictionManager.class);
        if (apm == null) {
            return;
        }

        registerPredictor(mAllAppsState, apm.createAppPredictionSession(
                new AppPredictionContext.Builder(context)
                        .setUiSurface("home")
                        .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns)
                        .build()));

        //
        registerPredictor(mHotseatState, apm.createAppPredictionSession(
                new AppPredictionContext.Builder(context)
                        .setUiSurface("hotseat")
                        .setPredictedTargetCount(mIDP.numDatabaseHotseatIcons)
                        .setExtras(convertDataModelToAppTargetBundle(context, mDataModel))
                        .build()));

        registerWidgetsPredictor(apm.createAppPredictionSession(
                new AppPredictionContext.Builder(context)
                        .setUiSurface("widgets")
                        .setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel))
                        .setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
                        .build()));
    }

>destroyPredictors

    private void destroyPredictors() {
        mAllAppsState.destroyPredictor();
        mHotseatState.destroyPredictor();
        mWidgetsRecommendationState.destroyPredictor();
    }

>registerPredictor

allApps,hotseat用这个,他们的update方法一样

    private void registerPredictor(PredictorState state, AppPredictor predictor) {
        state.predictor = predictor;
        state.predictor.registerPredictionUpdates(
                MODEL_EXECUTOR, t -> handleUpdate(state, t));
        state.predictor.requestPredictionUpdate();
    }
    //都是这个update方法
    private void handleUpdate(PredictorState state, List<AppTarget> targets) {
        if (state.setTargets(targets)) {
            //数据未发生变化,targets的个数,以及里边的包名,类名,user,shorcutinfo都得一样
            return;
        }
        //把task加入队列,见2.3,具体的task逻辑7.1
        mApp.getModel().enqueueModelUpdateTask(new PredictionUpdateTask(state, targets));
    }

>registerWidgetsPredictor

小部件用的,里边的updates方法不一样,其实就是task不一样,

    private void registerWidgetsPredictor(AppPredictor predictor) {
        mWidgetsRecommendationState.predictor = predictor;
        mWidgetsRecommendationState.predictor.registerPredictionUpdates(
                MODEL_EXECUTOR, targets -> {
                
                    if (mWidgetsRecommendationState.setTargets(targets)) {
                        // No diff, skip
                        return;
                    }
                    mApp.getModel().enqueueModelUpdateTask(
                    //7.2
                            new WidgetsPredictionUpdateTask(mWidgetsRecommendationState, targets));
                });
        mWidgetsRecommendationState.predictor.requestPredictionUpdate();
    }

6.3.loadItems

  • 加载本地保存的extra数据,allapps以及hotseat,另外widgets没有本地存储,默认数据为空集合
  • 文件位置见小节7,文件名见6.4
    public void loadItems(UserManagerState ums, Map<ShortcutKey, ShortcutInfo> pinnedShortcuts) {
        super.loadItems(ums, pinnedShortcuts);
        //获取allapps数据,放入extraItems
        WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
                mIDP.numDatabaseAllAppsColumns, mAllAppsState.containerId);
        FixedContainerItems allAppsPredictionItems = new FixedContainerItems(
                mAllAppsState.containerId, mAllAppsState.storage.read(mApp.getContext(),
                allAppsFactory, ums.allUsers::get));
        mDataModel.extraItems.put(mAllAppsState.containerId, allAppsPredictionItems);
    //获取hotseat数据,放入extraItems
        WorkspaceItemFactory hotseatFactory = new WorkspaceItemFactory(mApp, ums, pinnedShortcuts,
                mIDP.numDatabaseHotseatIcons, mHotseatState.containerId);
        FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
                mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
        mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
        //空的widgets数据,放入extraItems
        // Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
        mDataModel.extraItems.put(mWidgetsRecommendationState.containerId,
                new FixedContainerItems(mWidgetsRecommendationState.containerId,
                        new ArrayList<>()));
        mActive = true;
    }

7.BaseModelUpdateTask.java

就是个runnable

//run方法里调用抽象方法execute,子类需要实现具体的操作
public abstract class BaseModelUpdateTask implements ModelUpdateTask {
// 接口里带一个init方法,用来初始化要用到的参数
    public interface ModelUpdateTask extends Runnable {

>bindUpdatedWorkspaceItems

绑定workspace数据

    public void bindUpdatedWorkspaceItems(@NonNull final List<WorkspaceItemInfo> allUpdates) {
        // Bind workspace items,过滤下没有id的
        List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
                .filter(info -> info.id != ItemInfo.NO_ID)
                .collect(Collectors.toList());
        if (!workspaceUpdates.isEmpty()) {
        //数据传递给对应的回调
            scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates));
        }

        // Bind extra items if any
        allUpdates.stream()
                .mapToInt(info -> info.container)
                .distinct()
                .mapToObj(mDataModel.extraItems::get)
                .filter(Objects::nonNull)
                .forEach(this::bindExtraContainerItems);
    }

>bindExtraContainerItems

绑定扩展的数据,就是系统推荐的app

    public void bindExtraContainerItems(@NonNull final FixedContainerItems item) {

        scheduleCallbackTask(c -> c.bindExtraContainerItems(item));
    }

7.1.PredictionUpdateTask.java

小节6.4里使用,prediction数据变化的监听里添加这个task

public class PredictionUpdateTask extends BaseModelUpdateTask {

>execute

    public void execute(@NonNull final LauncherAppState app, @NonNull final BgDataModel dataModel,
            @NonNull final AllAppsList apps) {
        Context context = app.getContext();

        // TODO: remove this
        LauncherPrefs.getDevicePrefs(context).edit()
                .putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
        //查找对应容器id的数据,并过滤对应的type,最终获取有快捷方式发生变化的所有用户
        Set<UserHandle> usersForChangedShortcuts =
                dataModel.extraItems.get(mPredictorState.containerId).items.stream()
                        .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
                        .map(info -> info.user)
                        .collect(Collectors.toSet());

        List<ItemInfo> items = new ArrayList<>(mTargets.size());
        for (AppTarget target : mTargets) {
            WorkspaceItemInfo itemInfo;
            ShortcutInfo si = target.getShortcutInfo();
            //打印了日志,这个si全都是空的,
            if (si != null) {
                
                usersForChangedShortcuts.add(si.getUserHandle());
                //封装itemInfo并加载icon
                itemInfo = new WorkspaceItemInfo(si, context);
                app.getIconCache().getShortcutIcon(itemInfo, si);
            } else {
                String className = target.getClassName();
                if (COMPONENT_CLASS_MARKER.equals(className)) {
                    //instant app,忽略
                    continue;
                }
                ComponentName cn = new ComponentName(target.getPackageName(), className);
                UserHandle user = target.getUser();
                //从apps里查找,user和组件名一样的
                itemInfo = apps.data.stream()
                        .filter(info -> user.equals(info.user) && cn.equals(info.componentName))
                        .map(ai -> {
                        //缓存title和icon并封装为WorkspaceItemInfo
                            app.getIconCache().getTitleAndIcon(ai, false);
                            return ai.makeWorkspaceItem(context);
                        })
                        .findAny()
                        .orElseGet(() -> {
                        //这里是上边没有找到数据走这里,正常不会走这里。
                            LauncherActivityInfo lai = context.getSystemService(LauncherApps.class)
                                    .resolveActivity(AppInfo.makeLaunchIntent(cn), user);
                            if (lai == null) {
                                return null;
                            }
                            AppInfo ai = new AppInfo(context, lai, user);
                            app.getIconCache().getTitleAndIcon(ai, lai, false);
                            return ai.makeWorkspaceItem(context);
                        });

                if (itemInfo == null) {
                    continue;
                }
            }

            itemInfo.container = mPredictorState.containerId;
            items.add(itemInfo);
        }
        //更新预测的数据
        FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId, items);
        dataModel.extraItems.put(fci.containerId, fci);
        //具体见上边父类实现,对应的回调处理数据。
        bindExtraContainerItems(fci);
        //更新快捷方式发生变化的用户的数据
        usersForChangedShortcuts.forEach(
                u -> dataModel.updateShortcutPinnedState(app.getContext(), u));

        //把items数据写入本地
        mPredictorState.storage.write(context, fci.items);
    }

7.2.WidgetsPredictionUpdateTask

小节6.4里使用,prediction数据变化的监听里添加这个task

public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {

>execute

使用应用程序预测结果来推断用户可能想要使用的小部件

    public void execute(@NonNull final LauncherAppState appState,
            @NonNull final BgDataModel dataModel, @NonNull final AllAppsList apps) {
        Set<ComponentKey> widgetsInWorkspace = dataModel.appWidgets.stream().map(
                widget -> new ComponentKey(widget.providerName, widget.user)).collect(
                Collectors.toSet());
        Predicate<WidgetItem> notOnWorkspace = w -> !widgetsInWorkspace.contains(w);
        Map<PackageUserKey, List<WidgetItem>> allWidgets =
                dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();

        List<WidgetItem> servicePredictedItems = new ArrayList<>();
        List<WidgetItem> localFilteredWidgets = new ArrayList<>();

        for (AppTarget app : mTargets) {
            PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
            List<WidgetItem> widgets = allWidgets.get(packageUserKey);
            if (widgets == null || widgets.isEmpty()) {
                continue;
            }
            String className = app.getClassName();
            if (!TextUtils.isEmpty(className)) {
                WidgetItem item = widgets.stream()
                        .filter(w -> className.equals(w.componentName.getClassName()))
                        .filter(notOnWorkspace)
                        .findFirst()
                        .orElse(null);
                if (item != null) {
                    servicePredictedItems.add(item);
                    continue;
                }
            }
            // No widget was added by the service, try local filtering
            widgets.stream().filter(notOnWorkspace).findFirst()
                    .ifPresent(localFilteredWidgets::add);
        }
        if (servicePredictedItems.isEmpty()) {
            servicePredictedItems.addAll(localFilteredWidgets);
        }

        List<ItemInfo> items = servicePredictedItems.stream()
                .map(it -> new PendingAddWidgetInfo(it.widgetInfo, CONTAINER_WIDGETS_PREDICTION))
                .collect(Collectors.toList());
        FixedContainerItems fixedContainerItems =
                new FixedContainerItems(mPredictorState.containerId, items);
        //更新数据
        dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
        //绑定数据,见父类实现
        bindExtraContainerItems(fixedContainerItems);
        //widgets的prediction数据不用本地保存
        // Don't store widgets prediction to disk because it is not used frequently.
    }

7.3.bindExtraContainerItems

看下具体的回调都干啥了

>QuickStepLauncher里的回调

    public void bindExtraContainerItems(FixedContainerItems item) {
        if (item.containerId == Favorites.CONTAINER_PREDICTION) {
            mAllAppsPredictions = item;
            //找到对应的view
            PredictionRowView<?> predictionRowView =
                    getAppsView().getFloatingHeaderView().findFixedRowByType(
                            PredictionRowView.class);
            //设置数据
            predictionRowView.setPredictedApps(item.items);
        } else if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
        //见小节11.1
            mHotseatPredictionController.setPredictedItems(item);
        } else if (item.containerId == Favorites.CONTAINER_WIDGETS_PREDICTION) {
            getPopupDataProvider().setRecommendedWidgets(item.items);
        }
    }

>TaskbarModelCallbacks.java

    public void bindExtraContainerItems(FixedContainerItems item) {
        if (item.containerId == Favorites.CONTAINER_HOTSEAT_PREDICTION) {
            mPredictedItems = item.items;
            commitItemsToUI();
        } else if (item.containerId == Favorites.CONTAINER_PREDICTION) {
            mControllers.taskbarAllAppsController.setPredictedApps(item.items);
        }
    }

7.4.图片

>CONTAINER_PREDICTION

就是allapps上边的容器 image.png

>CONTAINER_HOTSEAT_PREDICTION

红线那个带背景的就是推荐的app image.png

>CONTAINER_WIDGETS_PREDICTION

暂时不清楚布局在哪里。

7.5.PredictState

数据存储位置:/data/user/0/com.android.launcher3/files

//文件存储位置所用的方法如下
getFileStreamPath("xxxx.xml")

>all_apps_predictions.xml

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<items>
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.chrome/com.google.android.apps.chrome.Main;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.messaging/.ui.ConversationListActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.dialer/.extensions.GoogleDialtactsActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.vending/.AssetBrowserActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.video/.VideoBrowserActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.deskclock/com.android.deskclock.DeskClock;sourceBounds=195%20470%20297%20628;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.photos/.home.HomeActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.keep/.activities.BrowseActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calculator/com.android.calculator2.Calculator;end" />
</items>

>hotseat_predictions.xml

<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<items>
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.deskclock/com.android.deskclock.DeskClock;sourceBounds=195%20470%20297%20628;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.video/.VideoBrowserActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.chrome/com.google.android.apps.chrome.Main;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.messaging/.ui.ConversationListActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.dialer/.extensions.GoogleDialtactsActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.android.vending/.AssetBrowserActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.youtube/.app.honeycomb.Shell%24HomeActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.photos/.home.HomeActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.keep/.activities.BrowseActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.contacts/com.android.contacts.activities.PeopleActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calculator/com.android.calculator2.Calculator;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.calendar/com.android.calendar.AllInOneActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.maps/com.google.android.maps.MapsActivity;end" />
    <entry  itemType="0" profileId="0" intent="#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.google.android.apps.tachyon/.MainActivity;end" />
</items>

8.IconCacheUpdateHandler.java

8.1.finish

可以看到,有要删除的item才会执行db操作,没有的话其实啥也没干

    public void finish() {
        // Commit all deletes
        int deleteCount = 0;
        StringBuilder queryBuilder = new StringBuilder()
                .append(IconDB.COLUMN_ROWID)
                .append(" IN (");

        int count = mItemsToDelete.size();
        for (int i = 0;  i < count; i++) {
            if (mItemsToDelete.valueAt(i)) {
                if (deleteCount > 0) {
                    queryBuilder.append(", ");
                }
                queryBuilder.append(mItemsToDelete.keyAt(i));
                deleteCount++;
            }
        }
        queryBuilder.append(')');

        if (deleteCount > 0) {
            mIconCache.mIconDb.delete(queryBuilder.toString(), null);
        }
    }

9.LauncherProvider.java

9.1.createEmptyDB

        public void createEmptyDB(SQLiteDatabase db) {
            try (SQLiteTransaction t = new SQLiteTransaction(db)) {
            //删除两个表,重新create
                dropTable(db, Favorites.TABLE_NAME);
                dropTable(db, "workspaceScreens");
                onCreate(db);
                t.commit();
            }
        }

9.2.loadFavorites

        @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
            // TODO: Use multiple loaders with fall-back and transaction.
            int count = loader.loadLayout(db, new IntArray());

            // Ensure that the max ids are initialized
            mMaxItemId = initializeMaxItemId(db);
            return count;
        }

10.LoaderCursor.java

10.1.构造方法

    public LoaderCursor(Cursor cursor, Uri contentUri, LauncherAppState app,
            UserManagerState userManagerState) {
        super(cursor);

        allUsers = userManagerState.allUsers;
        mContentUri = contentUri;
        mContext = app.getContext();
        mIconCache = app.getIconCache();
        mIDP = app.getInvariantDeviceProfile();
        mPM = mContext.getPackageManager();

        // Init column indices 获取对应列的index
        iconIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
        iconPackageIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
        iconResourceIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
        titleIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);

        idIndex = getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
        containerIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
        itemTypeIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
        screenIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
        cellXIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
        cellYIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
        profileIdIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
        restoredIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.RESTORED);
        intentIndex = getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
    }

11.HotseatPredictionController.java

11.1.setPredictedItems

    public void setPredictedItems(FixedContainerItems items) {
        boolean shouldIgnoreVisibility = FeatureFlags.ENABLE_APP_PREDICTIONS_WHILE_VISIBLE.get()
                || mLauncher.isWorkspaceLoading()
                || mPredictedItems.equals(items.items)
                || mHotseat.getShortcutsAndWidgets().getChildCount() < mHotSeatItemsCount;
        if (!shouldIgnoreVisibility
                && mHotseat.isShown()
                && mHotseat.getWindowVisibility() == View.VISIBLE) {
            mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
                if (isVisible) {
                    return;
                }
                mHotseat.setOnVisibilityAggregatedCallback(null);

                applyPredictedItems(items);
            });
        } else {
            mHotseat.setOnVisibilityAggregatedCallback(null);
            //核心是这个
            applyPredictedItems(items);
        }
    }

>applyPredictedItems

    private void applyPredictedItems(FixedContainerItems items) {
        mPredictedItems = new ArrayList(items.items);
        if (mPredictedItems.isEmpty()) {
            HotseatRestoreHelper.restoreBackup(mLauncher);
        }
        fillGapsWithPrediction();
    }

11.2.fillGapsWithPrediction

  • hotseat里如果普通图标已经占满,是不会显示推荐图标的
  • 当有空位的时候,才会在空位那里显示推荐图标
   private void fillGapsWithPrediction(boolean animate) {
        if (mPauseFlags != 0) {
            return;
        }

        int predictionIndex = 0;
        int numViewsAnimated = 0;
        ArrayList<WorkspaceItemInfo> newItems = new ArrayList<>();
        // make sure predicted icon removal and filling predictions don't step on each other
        //这个就是如果有旧的view移除动画正在执行,监听动画结束再开始新的fill
        if (mIconRemoveAnimators != null && mIconRemoveAnimators.isRunning()) {
            mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
                @Override
                public void onAnimationSuccess(Animator animator) {
                //旧的动画结束再重新开始执行这个方法
                    fillGapsWithPrediction(animate);
                    mIconRemoveAnimators.removeListener(this);
                }
            });
            //有旧的动画,返回,监听动画结束
            return;
        }

        //修改flag,防止多次进入
        mPauseFlags |= FLAG_FILL_IN_PROGRESS;
        //从0开始循环hotseat
        for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
        //查找对应位置的child
            View child = mHotseat.getChildAt(
                    mHotseat.getCellXFromOrder(rank),
                    mHotseat.getCellYFromOrder(rank));
            if (child != null && !isPredictedIcon(child)) {
            //对应位置已有非推荐的图标,也就是普通的hotseat
                continue;
            }
           
            //predictionIndex后边查找数据的时候会自增
            if (mPredictedItems.size() <= predictionIndex) {
             //predictedItems 数据已经读取完了,后边还有predicted类型的child,移除
                // Remove predicted apps from the past
                if (isPredictedIcon(child)) {
                    mHotseat.removeView(child);
                }
                continue;
            }
            WorkspaceItemInfo predictedItem =
                    (WorkspaceItemInfo) mPredictedItems.get(predictionIndex++);
            走到这里说明,child为null或者是predictedIcon
            if (isPredictedIcon(child) && child.isEnabled()) {
                PredictedAppIcon icon = (PredictedAppIcon) child;
                boolean animateIconChange = icon.shouldAnimateIconChange(predictedItem);
                //更新child的数据为新的
                icon.applyFromWorkspaceItem(predictedItem, animateIconChange, numViewsAnimated);
                if (animateIconChange) {
                    numViewsAnimated++;
                }
                icon.finishBinding(mPredictionLongClickListener);
            } else {
            //这里说明child为null
                newItems.add(predictedItem);
            }
            //更新数据
            preparePredictionInfo(predictedItem, rank);
        }
        //有新的item需要添加
        bindItems(newItems, animate);

        mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
    }

>isPredictedIcon

判断旧的view是否是predictedIcon

    private static boolean isPredictedIcon(View view) {
        return view instanceof PredictedAppIcon && view.getTag() instanceof WorkspaceItemInfo
                && ((WorkspaceItemInfo) view.getTag()).container
                == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
    }

>preparePredictionInfo

    private void preparePredictionInfo(WorkspaceItemInfo itemInfo, int rank) {
        itemInfo.container = LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
        itemInfo.rank = rank;
        itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
        itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
        itemInfo.screenId = rank;
    }

>bindItems

    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
        AnimatorSet animationSet = new AnimatorSet();
        for (WorkspaceItemInfo item : itemsToAdd) {
            //创建新的icon
            PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
            //绑定到hotseat里
            mLauncher.getWorkspace().addInScreenFromBind(icon, item);
            icon.finishBinding(mPredictionLongClickListener);
            if (animate) {
                animationSet.play(ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 0.2f, 1));
            }
        }
        if (animate) {
            animationSet.addListener(
                    forSuccessCallback(this::removeOutlineDrawings));
            animationSet.start();
        } else {
            removeOutlineDrawings();
        }
    }