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));
+                }
+            }
+        }
     }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/762870.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

硬件实用技巧:cadence Aleego创建焊盘过程

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140110911 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

windows USB 设备驱动开发-Host端和Device端

Windows 中的 USB 宿主端驱动程序 下图显示了适用于 Windows 的 USB 驱动程序堆栈的体系结构框图。 此图显示了适用于 USB 2.0 和 USB 3.0 的单独 USB 驱动程序堆栈。 当设备连接到 xHCI 控制器时&#xff0c;Windows 加载 USB 3.0 驱动程序堆栈。 Windows 为连接到 EHCI、OHC…

Windows/Linux/Mac 系统局域网服务发现协议及传输速度比较

简介 分析 / 验证对比常见局域网服务发现协议在 Windows/Linux/Mac 等不同系统下的支持和表现 在使用不同系统的智能硬件时&#xff0c;如常见的树莓派 / Openwrt 路由器 / Debian/Fedora/Windows/Mac 等系统是&#xff0c;系统间相互发现以及网络共享本应是系统的基础服务&a…

【RT摩拳擦掌】基于RT106L/S语音识别的百度云控制系统

【RT摩拳擦掌】基于RT106L/S语音识别的百度云控制系统 一 文档简介二 平台构建2.1 使用平台2.2 百度智能云2.2.1 物联网核心套件2.2.2 在线语音合成 2.3 playback语音数据准备与烧录2.4 开机语音准备与添加2.5 唤醒词识别词命令准备与添加 三 代码准备3.1 sln-local/2-iot 代码…

黑马点评下订单-小程序下单没问题但是Postman发送请求失败了,返回401

经过多方探索&#xff0c;这个✓8错误就是由于黑马点评使用了拦截器&#xff0c;我们直接发送请求是会被拦截器拦截下来的&#xff0c;我给出的解决方案是通过配置Postman解决&#xff0c;方法很简单&#xff01; 解决方案 右边的value写上Redis里面登录所用token值就可以了…

【TB作品】打地鼠游戏,ATMEGA16单片机,Proteus仿真 打地鼠游戏

11个按键LCD1602显示器9个灯蜂鸣器打地鼠小游戏就是九个灯泡&#xff0c;对应九个按键&#xff0c;灯泡有红黄蓝&#xff0c;每间隔一会儿就会亮一个灯&#xff0c;代表地鼠冒出来&#xff0c;按一下按键让灯泡灭掉代表打地鼠&#xff0c;红的三分&#xff0c;黄的两分&#xf…

开发自动发送国际短信的工具需要用到哪些源代码?

在当今数字化、全球化的时代&#xff0c;国际短信作为一种高效、便捷的沟通方式&#xff0c;在各个领域发挥着越来越重要的作用。 开发一款能够自动发送国际短信的工具&#xff0c;不仅能够帮助企业实现精准营销、客户服务&#xff0c;还能为个人提供便捷的跨国交流方式。 本…

手把手教你搭建PyTorch环境:MindStudio中PyTorch模型开发实战

本次实验的视频链接如下&#xff1a;​https://www.bilibili.com/video/BV1iA4y1f7o1/ 本次实验在MindStudio上进行&#xff0c;请先按照 教程 配置环境,安装MindStudio。 ​ MindStudio的是一套基于华为自研昇腾AI处理器开发的AI全栈开发工具平台&#xff0c;该IDE上功能很多…

Leetcode.1735 生成乘积数组的方案数

题目链接 Leetcode.1735 生成乘积数组的方案数 rating : 2500 题目描述 给你一个二维整数数组 q u e r i e s queries queries &#xff0c;其中 q u e r i e s [ i ] [ n i , k i ] queries[i] [n_i, k_i] queries[i][ni​,ki​] 。第 i i i 个查询 q u e r i e s [ i …

AI绘画工具Midjourney:和Discord互相成就

前言 提到文生图&#xff0c;很多人都会想到植根于根植于Discord社区的Midjourney&#xff0c;本篇文章就基于作者的使用体验思考&#xff0c;并结合了Discord来对Midjourney进行探讨&#xff0c;感兴趣的朋友一起来看看吧。 如果要说现在最火的文生图&#xff0c;不得不说到Mi…

深入理解 “androidx.databinding.DataBindingUtil“ 细节和使用

介绍 数据绑定&#xff08;Data Binding&#xff09;是 Android 中的一个强大功能&#xff0c;它允许你使用声明性格式而不是编程方式将布局中的 UI 组件绑定到应用中的数据源。androidx.databinding.DataBindingUtil 类是一个工具类&#xff0c;它提供了用于处理数据绑定的方…

单片机语音识别控制蓝牙通信

基于单片机语音识别控制&蓝牙控制 1、Arduino单片机语音控制1.1 直连1.2 蓝牙无线连接1.3 部分核心程序1.4 实物演示 2、51单片机语音控制2.1 直连2.2 蓝牙无线连接2.3 部分核心程序2.4 实物演示 3、STM32单片机语音控制3.1 直连3.2 蓝牙无线连接3.3 部分核心程序3.4 实物演…

数据结构之“刷链表题”

&#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;数据结构 目录 前言 一、相交链表 题目链接 大致思路 代码实现 二、环形链表1 题目链接 大致思路 代码实现 三、环形链表2 题目链接 大致思路 代码实…

RANSAC空间圆拟合实现

由初中的几何知识我们可以知道&#xff0c;确定一个三角形至少需要三个不共线的点&#xff0c;因此确定一个三角形的外接圆至少可用三个点。我们不妨假设三个点坐标为P1(x1,y1,z1),P2(x2,y2,z2),P3(x3,y3,z3)。 圆方程的标准形式为&#xff1a; (xi-x)2(yi-y)2R2 &#xff08;1…

8605 删数问题

这是一个典型的贪心算法问题。我们可以从高位开始&#xff0c;找到第一个比后面数字大的数字&#xff0c;删除它&#xff0c;然后继续这个过程&#xff0c;直到删除k个数字。如果我们已经删除了k个数字&#xff0c;但是还没有找到一个比后面数字大的数字&#xff0c;那么我们就…

专题六:Spring源码之初始化容器BeanFactory

上一篇咱们通过一个例子介绍初始化容器上下文相关内容&#xff0c;并通过两个示例代码看到了Spring在设计阶段为我预留的扩展点&#xff0c;和我们应该如何利用这两个扩展点在Spring初始化容器上下文阶段为我们提供服务。这一篇咱们接着往下看。 老这样子下回到refresh方法上来…

首款内置电源的迷你主机,不到千元的办公神器 | 零刻EQ13评测报告

零刻首款内置电源的迷你主机&#xff0c;不到千元的办公神器 | 零刻EQ13评测报告 哈喽小伙伴们好&#xff0c;我是Stark-C~ 众所周知&#xff0c;零刻作为目前国产迷你主机第一品牌&#xff0c;旗下系列众多&#xff0c;产线丰富&#xff0c;比如说它有针对游戏玩家的性能主机…

Transformer动画讲解 - 工作原理

Transformer模型在多模态数据处理中扮演着重要角色,其能够高效、准确地处理包含不同类型(如图像、文本、音频、视频等)的多模态数据。 Transformer工作原理四部曲:Embedding(向量化)、Attention(注意力机制)、MLPs(多层感知机)和Unembedding(模型输出)。 阶段一:…

JS数据处理(冒泡寻找对象里面有个Key相同的值并处理相关数据)

1.需要处理成的数据格式 [{ mpptNumber: 1, list:[{checked: false,pvEnableStatus: 0,pvSerialNumber: 1,},{checked: false,pvEnableStatus: 0,pvSerialNumber: 2,}] }, { mpptNumber: 2, list:[{checked: false,pvEnableStatus: 0,pvSerialNumber: 1,},{checked: false,pvE…

Cosine 余弦相似度并行计算的数学原理与Python实现

背景 Cosine 我在LLM与RAG系列课程已经讲了很多次了&#xff0c;这里不在熬述&#xff0c;它在LLM分析中&#xff0c;尤其是在语义相似度的计算中至关重要&#xff0c;在dot attention机制中&#xff0c;也会看到他的身影。这里讲的是纯数学上的运算与python是如何运用相关库进…