android13#launcher3#data load

1.简介

launcher的数据如何获取的

1.1.备注

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

>itemtype:数据类型

  • 0:application,应用图标
  • 2:foler,文件夹
  • 4:appwidget,小部件
  • 6:deep shortcut,就是应用的快捷方式,长按应用图标,弹框里显示的,可以拖动到桌面显示的
arduino 复制代码
        /**
         * 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

ini 复制代码
        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,

2.LauncherModel.java

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

2.0.构造方法

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

less 复制代码
    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

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

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

        }
    }

调用的地方有如下两处:

>Launcher.java

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

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

        setContentView(getRootView());

>TaskbarViewController.java

Taskbar初始化的时候

scss 复制代码
    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

scss 复制代码
    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方法

less 复制代码
    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的改变,强制重新获取最新的数据。

less 复制代码
    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

强制重新获取数据

scss 复制代码
    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方法了

java 复制代码
public class LoaderTask implements Runnable {

3.1.构造方法

ini 复制代码
    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

scss 复制代码
    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

  • 好像没有地方监听这个广播
ini 复制代码
    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"
scss 复制代码
    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

ini 复制代码
    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

scss 复制代码
    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

ini 复制代码
    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.构造方法

scss 复制代码
    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);    
ini 复制代码
    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);
    }

>单例模式

arduino 复制代码
    // 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上

scss 复制代码
    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传递给回调。

ini 复制代码
    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实现

java 复制代码
    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目录里实现)

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

5.5.executeCallbacksTask

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

scss 复制代码
    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

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

csharp 复制代码
    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.构造方法

ini 复制代码
    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

scss 复制代码
    public void workspaceLoadComplete() {
        super.workspaceLoadComplete();
        recreatePredictors();
    }

6.4.recreatePredictors

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

scss 复制代码
    //-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

scss 复制代码
    private void destroyPredictors() {
        mAllAppsState.destroyPredictor();
        mHotseatState.destroyPredictor();
        mWidgetsRecommendationState.destroyPredictor();
    }

>registerPredictor

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

typescript 复制代码
    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不一样,

typescript 复制代码
    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
java 复制代码
    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

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

>bindUpdatedWorkspaceItems

绑定workspace数据

less 复制代码
    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

绑定扩展的数据

less 复制代码
    public void bindExtraContainerItems(@NonNull final FixedContainerItems item) {

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

7.1.PredictionUpdateTask.java

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

scala 复制代码
public class PredictionUpdateTask extends BaseModelUpdateTask {

>execute

scss 复制代码
    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

scala 复制代码
public final class WidgetsPredictionUpdateTask extends BaseModelUpdateTask {

>execute

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

less 复制代码
    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里的回调

scss 复制代码
    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

ini 复制代码
    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上边的容器

>CONTAINER_HOTSEAT_PREDICTION

红线那个带个类似文件夹背景的就是推荐的app

>CONTAINER_WIDGETS_PREDICTION

暂时不清楚布局在哪里。

7.5.PredictState

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

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

>all_apps_predictions.xml

ini 复制代码
<?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

ini 复制代码
<?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操作,没有的话其实啥也没干

ini 复制代码
    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

scss 复制代码
        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

java 复制代码
        @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.构造方法

ini 复制代码
    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

scss 复制代码
    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

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

11.2.fillGapsWithPrediction

scss 复制代码
   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));
            //对应位置没有child或者不是predicted icon,返回
            if (child != null && !isPredictedIcon(child)) {
                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

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

>preparePredictionInfo

ini 复制代码
    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

scss 复制代码
    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();
        }
    }
相关推荐
CYRUS_STUDIO8 分钟前
Android 反调试攻防实战:多重检测手段解析与内核级绕过方案
android·操作系统·逆向
黄林晴4 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我4 小时前
flutter 之真手势冲突处理
android·flutter
法的空间4 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止4 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭5 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
jctech5 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户2018792831675 小时前
为何Handler的postDelayed不适合精准定时任务?
android
叽哥5 小时前
Kotlin学习第 8 课:Kotlin 进阶特性:简化代码与提升效率
android·java·kotlin
Cui晨5 小时前
Android RecyclerView展示List<View> Adapter的数据源使用View
android