Launcher-安装应用时添加应用图标到Workspace的大概流程

按照客户需求添加了无抽屉模式,也就是把所有应用图标都添加到Workspace中去,虽说实现了该需求,但是留下了部分bug,正好我们可以在实现需求和解决bug的过程中不断学习~ 1 无抽屉模式时安装应用,应用图标没有添加到桌面,以及 adb shell pm hide(unhide) com.android.settings ,unhide后图标也没有恢复到桌面 解决方案如下:

--- a/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/packages/apps/Launcher3/src/com/android/launcher3/SessionCommitReceiver.java
@@ -66,7 +66,7 @@ public class SessionCommitReceiver extends BroadcastReceiver {

         InstallSessionHelper packageInstallerCompat = InstallSessionHelper.INSTANCE.get(context);
         if (TextUtils.isEmpty(info.getAppPackageName())
-                || info.getInstallReason() != PackageManager.INSTALL_REASON_USER
+                || (info.getInstallReason() != PackageManager.INSTALL_REASON_USER && info.getInstallReason() != PackageManager.INSTALL_REASON_UNKNOWN)
                 || packageInstallerCompat.promiseIconAddedForId(info.getSessionId())) {
             packageInstallerCompat.removePromiseIconId(info.getSessionId());
             return;

原理是:之前只有PackageManager.INSTALL_REASON_USER的应用才能添加应用图标到桌面,现在我们添加PackageManager.INSTALL_REASON_UNKNOWN的应用 (adb install(unhide),手动安装应用都是INSTALL_REASON_UNKNOWN) 2 HomeSettings 修复无抽屉模式安装拥有多个图标的应用时只添加了一个图标 这个就需要梳理下代码逻辑的大概流程了 首先是SessionCommitReceiver 收到SESSION_COMMITTED 广播

        <!-- Intent received when a session is committed -->
        <receiver
            android:name=com.android.launcher3.SessionCommitReceiver
            android:exported=true>
            <intent-filter>
                <action android:name=android.content.pm.action.SESSION_COMMITTED />
            </intent-filter>
        </receiver>

在做了一定的筛选之后,调用如下方法

ItemInstallQueue.INSTANCE.get(context)
                .queueItem(info.getAppPackageName(), user);

    public void queueItem(String packageName, UserHandle userHandle) {
        queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));//插眼
    }

    private void queuePendingShortcutInfo(PendingInstallShortcutInfo info) {

        final Exception stackTrace = new Exception();

        // Queue the item up for adding if launcher has not loaded properly yet
        MODEL_EXECUTOR.post(() -> {
        //插眼,getItemInfo方法很重要,这里后面有用而且可以学下如何创建WorkspaceItemInfo
            Pair<ItemInfo, Object> itemInfo = info.getItemInfo(mContext);
            if (itemInfo == null) {
                FileLog.d(LOG,
                        Adding PendingInstallShortcutInfo with no attached info to queue.,
                        stackTrace);
            } else {
                FileLog.d(LOG,
                        Adding PendingInstallShortcutInfo to queue. Attached info: 
                                + itemInfo.first,
                        stackTrace);
            }
            addToQueue(info);
        });
        flushInstallQueue();
    }

    @WorkerThread
    private void addToQueue(PendingInstallShortcutInfo info) {
        ensureQueueLoaded();
        if (!mItems.contains(info)) {
            mItems.add(info);
            mStorage.write(mContext, mItems);
        }
    }

    @WorkerThread
    private void flushQueueInBackground() {
        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
        if (launcher == null) {
            // Launcher not loaded
            return;
        }
        ensureQueueLoaded();
        if (mItems.isEmpty()) {
            return;
        }

        List<Pair<ItemInfo, Object>> installQueue = mItems.stream()
                .map(info -> info.getItemInfo(mContext))
                .collect(Collectors.toList());

        // Add the items and clear queue
        if (!installQueue.isEmpty()) {
            // add log
            launcher.getModel().addAndBindAddedWorkspaceItems(installQueue);
        }
        mItems.clear();
        mStorage.getFile(mContext).delete();
    }

也就是代码流程为 queueItem -》 queuePendingShortcutInfo -》addToQueue(info); -》 flushInstallQueue(); 在 flushInstallQueue()方法中调用LauncherModel.java的addAndBindAddedWorkspaceItems(installQueue);方法

    /**
     * Adds the provided items to the workspace.
     */
    public void addAndBindAddedWorkspaceItems(
            @NonNull final List<Pair<ItemInfo, Object>> itemList) {
        for (Callbacks cb : getCallbacks()) {
            cb.preAddApps();
        }
        enqueueModelUpdateTask(new AddWorkspaceItemsTask(itemList));
    }

AddWorkspaceItemsTask.java是非常重要的核心类,仅做核心代码展示

        // Add the shortcut to the db
        getModelWriter().addItemToDatabase(itemInfo,
        LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
        coords[1], coords[2]);

        // Save the WorkspaceItemInfo for binding in the workspace
        addedItemsFinal.add(itemInfo);

        // log bitmap and label
        FileLog.d(LOG, Adding item info to workspace:  + itemInfo);

        if (!addedItemsFinal.isEmpty()) {
            scheduleCallbackTask(new CallbackTask() {
                @Override
                public void execute(@NonNull Callbacks callbacks) {
                    final ArrayList<ItemInfo> addAnimated = new ArrayList<>();
                    final ArrayList<ItemInfo> addNotAnimated = new ArrayList<>();
                    if (!addedItemsFinal.isEmpty()) {
                        ItemInfo info = addedItemsFinal.get(addedItemsFinal.size() - 1);
                        int lastScreenId = info.screenId;
                        for (ItemInfo i : addedItemsFinal) {
                            if (i.screenId == lastScreenId) {
                                addAnimated.add(i);
                            } else {
                                addNotAnimated.add(i);
                            }
                        }
                    }
                    callbacks.bindAppsAdded(addedWorkspaceScreensFinal,
                            addNotAnimated, addAnimated);
                }
            });
        }

            InstallSessionTracker installSessionTracker =
                    InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);

InstallSessionTracker.java监听安装进度,在LauncherAppState.java 和InstallSessionHelper.java中进行注册
其中

   @Override
    public void onFinished(final int sessionId, final boolean success) {
        InstallSessionHelper helper = mWeakHelper.get();
        Callback callback = mWeakCallback.get();
        if (callback == null || helper == null) {
            return;
        }
        // For a finished session, we can't get the session info. So use the
        // packageName from our local cache.
        SparseArray<PackageUserKey> activeSessions = getActiveSessionMap(helper);
        PackageUserKey key = activeSessions.get(sessionId);
        activeSessions.remove(sessionId);

        if (key != null && key.mPackageName != null) {
            String packageName = key.mPackageName;
            PackageInstallInfo info = PackageInstallInfo.fromState(
                    success ? STATUS_INSTALLED : STATUS_FAILED,
                    packageName, key.mUser);
            callback.onPackageStateChanged(info);

            if (!success && helper.promiseIconAddedForId(sessionId)) {
                callback.onSessionFailure(packageName, key.mUser);
                // If it is successful, the id is removed in the the package added flow.
                helper.removePromiseIconId(sessionId);
            }
        }
    }

callback.onPackageStateChanged(info); callback是LaunchModel

    @Override
    public void onPackageStateChanged(@NonNull final PackageInstallInfo installInfo) {
        enqueueModelUpdateTask(new PackageInstallStateChangedTask(installInfo));
    }

PackageInstallStateChangedTask.java也很核心

if (mInstallInfo.state == PackageInstallInfo.STATUS_INSTALLED) {
            try {
                // For instant apps we do not get package-add. Use setting events to update
                // any pinned icons.
                ApplicationInfo ai = app.getContext()
                        .getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
                if (InstantAppResolver.newInstance(app.getContext()).isInstantApp(ai)) {
                    app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);//---------
                }
            } catch (PackageManager.NameNotFoundException e) {
                // Ignore
            }
            // Ignore install success events as they are handled by Package add events.
            return;
        }

        synchronized (apps) {
            List<AppInfo> updatedAppInfos = apps.updatePromiseInstallInfo(mInstallInfo);
            if (!updatedAppInfos.isEmpty()) {
                for (AppInfo appInfo : updatedAppInfos) {
                    scheduleCallbackTask(c -> c.bindIncrementalDownloadProgressUpdated(appInfo));
                }
            }
            bindApplicationsIfNeeded();//-----
        }

        synchronized (dataModel) {
            final HashSet<ItemInfo> updates = new HashSet<>();
            dataModel.forAllWorkspaceItemInfos(mInstallInfo.user, si -> {
                if (si.hasPromiseIconUi()
                        && mInstallInfo.packageName.equals(si.getTargetPackage())) {
                    si.setProgressLevel(mInstallInfo);
                    updates.add(si);
                }
            });

            for (LauncherAppWidgetInfo widget : dataModel.appWidgets) {
                if (widget.providerName.getPackageName().equals(mInstallInfo.packageName)) {
                    widget.installProgress = mInstallInfo.progress;
                    updates.add(widget); //-----
                }
            }

            if (!updates.isEmpty()) {
                scheduleCallbackTask(callbacks -> callbacks.bindRestoreItemsChange(updates));
            }
        }
    }

其中app.getModel().onPackageAdded(ai.packageName, mInstallInfo.user);
LauncherModel.java:

    @Override
    public void onPackageAdded(@NonNull final String packageName, @NonNull final UserHandle user) {
        int op = PackageUpdatedTask.OP_ADD;
        android.util.Log.e("ninini", " new PackageUpdatedTask(OP_ADD");
        enqueueModelUpdateTask(new PackageUpdatedTask(op, user, packageName));
    }
	

PackageUpdatedTask :

switch (mOp) {
            case OP_ADD: {
                for (int i = 0; i < N; i++) {
                    if (DEBUG) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
                    iconCache.updateIconsForPkg(packages[i], mUser);
                    if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
                        appsList.removePackage(packages[i], mUser);
                    }
                    activitiesLists.put(
                            packages[i], appsList.addPackage(context, packages[i], mUser));
                }
                flagOp = FlagOp.NO_OP.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                break;
            }

appsList.addPackage,appsList是AllAppsList

    /**
     * Add the icons for the supplied apk called packageName.
     */
    public List<LauncherActivityInfo> addPackage(
            Context context, String packageName, UserHandle user) {
        List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
                .getActivityList(packageName, user);
		//在这里,遍历LauncherActivityInfo,然后安装应用时把图标添加到了AppList中
        for (LauncherActivityInfo info : activities) {
            add(new AppInfo(context, info, user), info);
        }

        return activities;
    }
	
	/**
     * Add the supplied ApplicationInfo objects to the list, and enqueue it into the
     * list to broadcast when notify() is called.
     *
     * If the app is already in the list, doesn't add it.
     */
    public void add(AppInfo info, LauncherActivityInfo activityInfo) {
        add(info, activityInfo, true);
    }

    public void add(AppInfo info, LauncherActivityInfo activityInfo, boolean loadIcon) {
        if (!mAppFilter.shouldShowApp(info.componentName)) {
            return;
        }
        if (findAppInfo(info.componentName, info.user) != null) {
            return;
        }
        if (loadIcon) {
            mIconCache.getTitleAndIcon(info, activityInfo, false /* useLowResIcon */);
            info.sectionName = mIndex.computeSectionName(info.title);
        } else {
            info.title = "";
        }

        data.add(info);
        mDataChanged = true;
    }

修改方案如下

@@ -172,6 +174,17 @@ public class ItemInstallQueue {
      */
     public void queueItem(String packageName, UserHandle userHandle) {
         queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle));
+
+        if(MultiModeController.isSingleLayerMode(mContext)){
+            List<LauncherActivityInfo> activities = mContext.getSystemService(LauncherApps.class)
+                    .getActivityList(packageName, userHandle);
+            android.util.Log.e("queuePendingShortcutInfo ", activities.size()+"~" );
+            if(null != activities && activities.size() > 1){
+                for (int i = 1; i < activities.size(); i++) {
+                    queuePendingShortcutInfo(new PendingInstallShortcutInfo(packageName, userHandle,i));
+                }
+            }
+        }
     }
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值