获取launcher桌面应用

发布时间 2023-06-06 17:24:08作者: 柒月下寻

有时候可能会需要用到查询所有安装并显示桌面的应用

一种方式是直接 通过PackageManager 来获取:

    @NonNull
    public static List<T> queryInstallAppList() {
        List<T> infoArrayList = new ArrayList<>();
        PackageManager packageManager = App.getAppContext().getPackageManager();
        try {
            List<PackageInfo> packageInfos = packageManager.getInstalledPackages(0);
            for (int i = 0; i < packageInfos.size(); i++) {
                PackageInfo packageInfo = packageInfos.get(i);
                ApplicationInfo applicationInfo = packageInfo.applicationInfo;
                // Application类名
                String name = applicationInfo.name;
                // 包名
                String packageName = applicationInfo.packageName;
                // app name
                String applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
                Log.i(TAG, "应用名:" + applicationName + "\t,包名:" + packageName+"\t,name:"+name);
                //判断系统app
                if ((ApplicationInfo.FLAG_SYSTEM & packageInfo.applicationInfo.flags) != 0) {
                    Log.w(TAG, "is system APP:   " + applicationName);
                }else {

                }
                T appNaviItemBean = new AppNaviItemBean();
                appNaviItemBean.setPkg(packageInfo.packageName);
                appNaviItemBean.setAppName(applicationName);
                Drawable drawable = packageInfo.applicationInfo.loadIcon(packageManager);
                if (drawable == null) {
                    Log.w(TAG, "Icon: == null");
                    continue;
                }
                appNaviItemBean.setIcon(drawable);
                infoArrayList.add(appNaviItemBean);
            }
        } catch (Exception e) {
            Log.i(TAG, "===============获取应用包信息失败");
        }
        return infoArrayList;
    }

这个是把系统所有的应用都能获取到,许多是系统自带是不展示给用户的;,会出现比如桌面上只有四十个app。此方法获取出来却是100多个,需要添加多种条件过滤

通过查询launcher.db 来获取

分析launcher 的启动流程会知道,launcher启动会根据配置文件和代码特殊逻辑来创建一个db文件,用来存储桌面图标、小组件的相应信息,包括位置、intent等信息,打开db 看下它的对应的字段
获取 db 的方法:

$ adb pull /data/data/com.android.launcher/databases/launcher.db

image

  • title-appname
  • intent-里面是拼接的一些intent信息
    intent=#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;launchFlags=0x10200000;component=com.example.aptdemo/.MainActivity;sourceBounds=422%20293%20720%20614;end

既然是数据库,有相应权限 应该就能拿到数据

 <!-- 读取launcher.db-->
     <uses-permission android:name="${packageName}.permission.READ_SETTINGS" />
    <uses-permission android:name="${packageName}.permission.WRITE_SETTINGS" />

提取方式可以去参考launcher 的配置;比如Android11的配置

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher3">
   ...
    <!--
    Permissions required for read/write access to the workspace data. These permission name
    should not conflict with that defined in other apps, as such an app should embed its package
    name in the permissions. eq com.mypackage.permission.READ_SETTINGS
    -->
    <permission
        android:name="${packageName}.permission.READ_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signatureOrSystem"
        android:label="@string/permlab_read_settings"
        android:description="@string/permdesc_read_settings"/>
    <permission
        android:name="${packageName}.permission.WRITE_SETTINGS"
        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
        android:protectionLevel="signatureOrSystem"
        android:label="@string/permlab_write_settings"
        android:description="@string/permdesc_write_settings"/>

    <uses-permission android:name="${packageName}.permission.READ_SETTINGS" />
    <uses-permission android:name="${packageName}.permission.WRITE_SETTINGS" />

    ...
</manifest>

接下来就是代码实现逻辑,

    public List<T> getAppList() {
        ContentResolver cr = App.getAppContext().getContentResolver();
        //在知道系统声明该权限 ${packageName} 的时候,简单拼上就行实现
        Uri uri = Uri.parse("content://com.android.launcher3.settings/favorites?notify=true");
        try {
            Cursor c = cr.query(uri, null, null, null, null);
            return parseMessage(c);
        } catch (Exception e) {
            Log.w(TAG, "getAppList: Exception " + e);
            return null;T
        } finally {
            Log.d(TAG, "getAppList: end");
        }
    }

    @NonNull
    private void parseMessage(Cursor cursor) {
        if (cursor == null) {
            Log.w(TAG, "parseMessage: null");
            return;
        }
        while (cursor.moveToNext()) {
            // 快捷方式的名称
            String title = cursor.getString(cursor.getColumnIndex("title"));
            // 快捷方式启动的对象
            String intent = cursor.getString(cursor.getColumnIndex("intent"));
            // 直接通过cursor 获取对应字段即可
       ... 
    }

有几个注意点:

  • 获取app 应用图标通过数据库直接取字段方式不准确(实际使用发现),对应表中iconResource 和 icon 字段都有大量的空,就需要换一种方式来获取应用icon
    // 包名是可以拿到的(普通情况下)
    private Drawable processIconByPkg() {
        if (TextUtils.isEmpty(pkg)) {
            return null;
        }
        PackageInfo packageInfo;
        try {
            packageInfo = App.getAppContext().getPackageManager().getPackageInfo(pkg, 0);
        } catch (PackageManager.NameNotFoundException e) {
            packageInfo = null;
        }
        if (packageInfo == null) {
            return null;
        }
        return packageInfo.applicationInfo.loadIcon(App.getAppContext().getPackageManager());
    }
  • 获取appame,会出现切换系统语言后,查询出来的title 没有及时匹配,换一种方式:
    private String processTitleByPkg(String pkg) {
        if (TextUtils.isEmpty(pkg)) {
            return null;
        }
        PackageInfo packageInfo;
        try {
            packageInfo = App.getAppContext().getPackageManager().getPackageInfo(pkg, 0);
        } catch (PackageManager.NameNotFoundException e) {
            packageInfo = null;
        }
        if (packageInfo == null) {
            return null;
        }
        String applicationName = (String) App.getAppContext().getPackageManager().getApplicationLabel(packageInfo.applicationInfo);
        Log.w(TAG, "processIconByPkg: " + applicationName);
        return applicationName;
    }

以上就是获取方法