Android13设置默认Launcher分析

概要

本文主要介绍Android13的版本下,设置默认Launcher的服务端代码调用过程,以及如何自己写代码设置默认应用

本文主要参考文章:

一、开发Launcher应用

相信大家都了解Launcher本身是一个app,只不过其功能比较特殊,能提供一系列的app管理、与用户交互的可视化界面。如何让普通应用成为Launcher呢:
其实就是在activity中增加两条category:

<activity
    android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
		<!-- 增加以下两条category -->
        <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

二、多Launcher环境下如何设置默认的Launcher

经过网上的资料,有以下几种方案:

  • 1、系统中如果存在多个Launcher,则开机会弹出一个选择器,让你选择哪个Launcher作为主Launcher,这个我有遇到过,概率性触发,但是不清楚其触发逻辑,如果有了解的小伙伴可以留言评论下~~
    在这里插入图片描述

  • 2、通过原生设置–>应用–>默认应用–>主屏幕 设置默认主屏幕即可
    在这里插入图片描述

  • 3、通过adb命令设置:adb shell pm set-home-activity com.android.launcher3

  • 4、通过系统配置,再编译系统刷机(尚未尝试)

  • 5、自己写代码控制,这个需要开发成系统app方可

三、查看方案2的过程

通过查看原生的Settings应用,设置默认Launcher的过程,学习其中的逻辑,自己也能手搓代码实现“设置默认应用”
在上述方案2中,设置默认主屏幕,由于我本人没咋看过Settings应用,里面的代码就不展开了,免得误人子弟。
在网上检索到,点击事件最终会调用到ManageRoleHolderStateLiveData#setRoleHolderAsUser()方法中,因此我在该方法里面打印下堆栈,最终发现其调用过程:

java.lang.Exception
    at com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData.setRoleHolderAsUser(ManageRoleHolderStateLiveData.java:109)
    at com.android.permissioncontroller.role.ui.DefaultAppViewModel.setDefaultApp(DefaultAppViewModel.java:87)
    at com.android.permissioncontroller.role.ui.DefaultAppChildFragment.setDefaultApp(DefaultAppChildFragment.java:254)
    at com.android.permissioncontroller.role.ui.DefaultAppChildFragment.onPreferenceClick(DefaultAppChildFragment.java:246)
    at androidx.preference.Preference.performClick(Preference.java:1186)
    at androidx.preference.Preference.performClick(Preference.java:1168)
    at androidx.preference.CheckBoxPreference.performClick(CheckBoxPreference.java:90)
    at androidx.preference.Preference$1.onClick(Preference.java:181)
    at android.view.View.performClick(View.java:7542)
    at android.view.View.performClickInternal(View.java:7519)
    at android.view.View.-$$Nest$mperformClickInternal(Unknown Source:0)
    at android.view.View$PerformClick.run(View.java:29476)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:201)
    at android.os.Looper.loop(Looper.java:288)
    at android.app.ActivityThread.main(ActivityThread.java:7963)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

View层面的话,就是DefaultAppChildFragment里面响应了点击事件,最终调用到了ManageRoleHolderStateLiveData.setRoleHolderAsUser(),本文不去探讨Settings应用的界面组成、调用过程。只关注于怎么设置的默认Launcher。

因此先看ManageRoleHolderStateLiveData.setRoleHolderAsUser()方法:

public void setRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
            boolean add, int flags, @NonNull UserHandle user, @NonNull Context context) {
            
        ......

        RoleManager roleManager = context.getSystemService(RoleManager.class);
        Executor executor = context.getMainExecutor();
        Consumer<Boolean> callback = successful -> {
            if (successful) {
                if (DEBUG) {
                    Log.i(LOG_TAG, "Package " + (add ? "added" : "removed")
                            + " as role holder, role: " + roleName + ", package: " + packageName);
                }
             
                setValue(STATE_SUCCESS);
            } else {
                if (DEBUG) {
                    Log.i(LOG_TAG, "Failed to " + (add ? "add" : "remove")
                            + " package as role holder, role: " + roleName + ", package: "
                            + packageName);
                }
				setValue(STATE_FAILURE);
            }
        };

        if (add) {
            roleManager.addRoleHolderAsUser(roleName, packageName, flags, user, executor, callback);     // 核心代码就是这里
        } else {
            roleManager.removeRoleHolderAsUser(roleName, packageName, flags, user, executor,
                    callback);
        }
    }

核心就这个:roleManager.addRoleHolderAsUser()。

因此如果我们想要自己写代码的去控制默认Launcher的话,直接这样写:

public void setDefaultHome(Context context){
        RoleManager mRoleManager = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
            mRoleManager = context.getSystemService(RoleManager.class);
        }
        final Executor executor = getMainExecutor();
        Consumer<Boolean> callBack = success -> {
            if (success){
                // 在这里可以保存到系统属性中,方便其他地方取出来用,看个人情况吧
                Settings.Global.putString(context.getContentResolver(), "defalut_home", "你的应用包名");
                Log.d(TAG, "setDefaultHome: success");
            }else{
                Log.d(TAG, "setDefaultHome: unSuccess");
            }
        };

        UserHandle userHandle = Process.myUserHandle();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            mRoleManager.addRoleHolderAsUser("android.app.role.HOME",
                    "com.example.app7", 0, userHandle, executor, callBack);
        }
    }

注意:这样写需要应用设置为系统应用,在AndroidManifest中,添加上android:sharedUserId="android.uid.system", 同时别忘了添加上权限:android.permission.MANAGE_ROLE_HOLDERS

四、系统应用层以及服务层的调用过程

mRoleManager.addRoleHolderAsUser()发起系统层的调用,往下的调用过程流程图如下:

在这里插入图片描述
也没什么难度,就是应用层于服务层之前的相互调用,具体调用代码也是很简单,小伙伴们感兴趣的话自行查看。有个问题要注意下,这里面的类在不同的包下,找起来比较麻烦,我这里给大家标注出来吧:
packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\role\ui 包下

  • ManageRoleHolderStateLiveData.java

packages\modules\Permission\framework-s\java\android\app\role 包下

  • RoleManager.java
  • IRoleManager.aidl
  • RoleControllerManager.java
  • RoleControllerService.java

packages\modules\Permission\service\java\com\android\role 包下

  • RoleService.java
  • RoleUserState.java

packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\role\service 包下:

  • RoleControllerServiceImpl.java

最终调用到RoleUserState#addRoleHolder()方法:

@CheckResult
public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
    boolean changed;

    synchronized (mLock) {
        ArraySet<String> roleHolders = mRoles.get(roleName);
        if (roleHolders == null) {
            Log.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
                    + ", package: " + packageName);
            return false;
        }
        // 核心1:添加到内存列表数据中
        changed = roleHolders.add(packageName);
        
        // 核心2:数据固化:保存到地址 /data/misc_de/0/apexdata/com.android.permission/roles.xml       
        if (changed) {
            scheduleWriteFileLocked();         
        }
    }

    // 3 回调通知下各单位,roleName这个属性的应用已经更新了
    if (changed) {
        mCallback.onRoleHoldersChanged(roleName, mUserId);
    }
    new Exception().printStackTrace();
    return true;
}

在注释2哪里,会把默认应用的包名固化到xml中,保存的路径是:/data/misc_de/0/apexdata/com.android.permission/roles.xml
注意这里的保存数据是异步的,有200ms的延迟,大家注意下实时性。

五、总结

从settings应用中,发起设置默认Launcher,关键的代码就是:roleManager.addRoleHolderAsUser()发起服务端的响应过程。因此我们在自己的代码中如果想设置默认应用的话,也通过这个方法即可。

注意:其中该方法有个String类型的RoleName需要注意下,就是你要设置的默认app的类型:
android.app.role.HOME: 设置默认Launcher
android.app.role.System_UI:设置默认的SystemUI
android.app.role.BROWSER:设置默认浏览器

你要设置什么默认类型,就填其对应的RoleName即可。

既然设置了默认的Launcher,那么开机启动的时候又是怎么选择默认的Launcher呢,后面继续~

Android设备的默认launcher设置是指系统默认的启动器(Launcher)应用程序。启动器是用户在设备上看到和操作的主屏幕,它提供了桌面、应用程序列表、小部件和壁纸等功能。 在Android设备上,不同的制造商可能会有不同的默认启动器设置,例如Samsung可能会有自己的启动器样式,而Google的Pixel设备则使用原生的Android启动器。 默认启动器有以下几个主要特点和功能: 1. 桌面:默认启动器会在设备主屏幕上显示桌面,用户可以在桌面上添加和管理应用程序、小部件和快捷方式等。 2. 应用程序列表:用户可以通过默认启动器访问设备上安装的所有应用程序的列表,并进行搜索和排序。 3. 小部件:默认启动器支持用户在桌面上添加各种小部件,例如天气、日历、音乐播放器等,以便用户更方便地获取相关信息。 4. 壁纸:默认启动器还允许用户更改桌面背景壁纸,可以选择自己喜欢的图片或者使用系统提供的预设壁纸。 对于用户来说,选择适合自己的默认启动器可以增加使用Android设备的个性化和便利性。用户可以根据自己的需求和偏好,选择不同功能和样式的启动器来进行设置。有些启动器还提供了更多的自定义选项,例如主题、图标包等,使用户能够将设备界面个性化到自己喜欢的样子。 总之,Android设备的默认启动器设置是一个重要的用户体验方面的设置选项,通过它可以实现设备主屏幕的个性化和功能拓展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值