android 修改默认启动项launcher(一)

本文解析了Android系统中选择Launcher的机制,介绍了如何通过修改ResolverActivity和ResolveListAdapter,使系统默认启动指定的Launcher应用,避免开机时的选择界面。
  • 需求:不去掉系统自带launcher的前提下,默认启动指定应用作为launcher
  • 现象:应用中带有属性"android.intent.category.HOME",开机会弹出选择界面
  • 思路:跳过选择界面,直接选中要启动的launcher并直接进入
1.选择界面流程分析

使用adb命令抓取到launcher选择提示框是一个activity,路径是frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

此Activity会获取系统中所有的Home app,并根据系统的设置情况显示如上界面。此类中有一个内部类ResolveListAdapter该类继承自BaseAdapter,该类是Home app选择界面的数据适配器。
ResolveListAdapter会在ResolverActivity的onCreate方法中被初始化并会传入一个ResolveInfo类型的List,ResolveListAdapter根据会传入的List初始化一个List mList ,用户的点击事件都会在ResolveListAdapter获取数据。
用户点击”ALWAYS”的事件发生在ResolverActivity的onButtonClick 方法中,此方法会获取选中的Item的position、或者获取用户上一次启动的Home app的,mAlwaysUseOption代表用户选中的是否为历史选择,并调用startSelected。
onButtonClick()方法

public void onButtonClick(View v) {
	final int id = v.getId();
	startSelected(mAlwaysUseOption ?mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
    id == R.id.button_always,
    mAlwaysUseOption);
}

startSeletced中通过ResolveListAdapter获取选择的item代表的Home app。并且finish此activity,onTargetSelected()会根据传入的ResolveInfo设置默认的Home

void startSelected(int which, boolean always, boolean filtered) {
   if (isFinishing()) {
      return;
   }
   ...
   ResolveInfo ri = mAdapter.resolveInfoForPosition(which,filtered);
   TargetInfo target = mAdapter.targetInfoForPosition(which,filtered);
   if(onTargetSelected(target,always)){
   	  finish();
   }
}

而startSelected在ItemClickListener也被调用到,

public void onItemClick(AdapterView<?> parent,View view,int position,long id){
   ...
   startSelected(position,true,true);
}

可以看出第一个参数是启动的HomeActivity在adapter中的位置,好分析完了,现在开始修改。

2.获取指定HomeActivity在adapter中的位置

首先在adapter类中添加获取指定HomeActivity在list中位置的方法

public int getDefaultHomePosition(String packageName){
  for (int i = 0; i < mDisplayList.size(); i++) {
       ResolveInfo info = mDisplayList.get(i).getResolveInfo();
       if (info.activityInfo.packageName.equals(packageName)) {
           return i;
       }
  }
  return -1;
}
3.启动定制launcher

在ResolverActivity中添加设置默认app的方法setupDefaultLauncher(),代码如下:

private void setupDefaultLauncher() {
   int position = mAdapter.getDefaultHomePosition("home app包名");
   //如果不存在则return
   if (position == -1) {
      if (DEBUG)
         Log.w(TAG,"not find default Home");
         return;
      }
   }
   startSelected(position, true, true);
   finish();
}

为了保证mAdapter被初始化 setupDefaultLauncher()的调用添加到ResolverActivity的onCreate函数中,代码如下:

void configureContentview(...){
	mAdapter = creatAdapter(...);
	...
	setupDefaultLauncher();
	...
}
  • 注意:你需要做成launcher的应用的manifest.xml中需要添加
<intent-filter>
	<action android:name="android.intent.action.MAIN" />
	<category android:name="android.intent.category.HOME"/>
	<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<think>我们正在处理的问题是:在Android 14中设置自定义Launcher默认启动器。 根据引用[4]的内容,我们知道要让个应用成为Launcher,需要在它的Activity中声明特定的intent-filter,即包含: <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> 但是,当系统中有多个应用都声明了这些category时,系统会弹出选择器让用户选择。我们的目标是在系统级设置默认启动器,避免弹出选择器。 引用[3]和[5]提到,在Android 9.0和10.0中,可以通过修改frameworks/base/core/java/com/android/internal/app/ResolverActivity.java来设置默认Launcher。具体来说,在rebuildList()方法中,通过遍历当前解析列表(rci:currentResolveList)并过滤掉非默认Launcher的包名,只保留默认Launcher,从而实现直接进入默认Launcher。 然而,在Android 14中,我们可能需要类似的方法,但ResolverActivity的代码可能有所变化。另外,我们也可以考虑另种方法:在系统第启动时,通过系统属性或者系统服务直接设置默认Launcher。 以下是两种方法: 方法修改ResolverActivity(类似于Android 9.0/10.0的方法) 步骤: 1. 找到ResolverActivity的源码(路径:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java) 2. 在rebuildList()方法中,找到处理HOME intent的地方(通常是通过mIntent的action是ACTION_MAIN,并且category包含CATEGORY_HOME) 3. 在构建列表之前,判断是否有我们指定默认Launcher,如果有,则直接启动它并finish当前Activity。 方法二:在系统启动时设置默认Launcher(通过PackageManagerService) 在系统第启动时,通过系统服务设置默认的Home组件。我们可以修改PackageManagerService,在初始化时设置默认Launcher。 这里我们采用方法,因为方法二需要更深的系统集成,并且可能涉及更复杂的权限问题。 具体修改步骤(针对Android 14): 1. 打开ResolverActivity.java文件。 2. 在rebuildList()方法中,我们可以在构建解析列表之前,判断是否有默认Launcher,如果有,则直接启动它并结束当前Activity。 注意:我们需要定义默认Launcher的包名,可以通过系统属性或者直接硬编码(不推荐,最好通过系统属性配置)。 修改代码示例: 在rebuildList()方法中,找到处理HOME intent的地方(通常是通过mIntent的action是Intent.ACTION_MAIN,并且category包含Intent.CATEGORY_HOME)。然后,我们尝试获取默认Launcher包名(例如,我们假设为"com.example.my.launcher"),然后检查这个包名的应用是否在解析列表中。如果在,则直接启动它。 代码修改如下: ```java // 在rebuildList()方法中,找到处理HOME intent的部分 if (mMultiProfilePagerAdapter.getActiveListController().getResolversForUser(mMultiProfilePagerAdapter.getCurrentUserHandle()).size() > 0) { // 新增:设置默认Launcher的包名 String defaultLauncherPackage = "com.example.my.launcher"; // 替换为你的Launcher包名 // 遍历当前解析列表,查找是否有默认Launcher for (ResolvedComponentInfo info : rciList) { ActivityInfo ai = info.getResolveInfoAt(0).activityInfo; if (defaultLauncherPackage.equals(ai.packageName)) { // 找到默认Launcher,直接启动并结束当前ResolverActivity onTargetSelected(info, false); finish(); return; } } } // 如果没有找到默认Launcher,则继续原来的流程,构建选择列表 ``` 但是,注意:在Android 14中,ResolverActivity可能已经重构,因此我们需要确保在正确的时机插入这段代码。另外,我们还需要考虑多用户的情况,因为解析列表可能是按用户分开的。 另种更健壮的方法是:在系统构建时,将默认Launcher的包名写入系统属性(例如persist.sys.default_launcher),然后在代码中读取这个属性。 修改为读取系统属性: ```java // 读取系统属性 String defaultLauncherPackage = SystemProperties.get("persist.sys.default_launcher"); if (defaultLauncherPackage != null && !defaultLauncherPackage.isEmpty()) { // 遍历解析列表... } ``` 这样,我们就可以在设备启动时通过设置系统属性来指定默认Launcher。 注意:这种方法需要系统签名,因为ResolverActivity是系统级应用。 另外,我们还需要处理用户手动切换Launcher的情况。如果用户手动切换了Launcher,那么系统属性可能会被覆盖?实际上,系统属性是持久化的,但用户选择后,系统会记住用户的选择。所以,我们这种方法实际上是强制设置默认的,即使用户没有选择。因此,这种方法适用于企业设备或者固定用途设备。 如果希望用户仍然可以选择,但第启动时不弹出选择器,我们可以这样:只在第启动时(即没有设置过默认Launcher时)使用我们的默认设置。但是,ResolverActivity本身并不记录是否是第次,所以我们需要在设置中记录。 更常见的做法是:在系统第启动时,通过PackageManagerService设置默认Launcher。这通常在设备出厂时完成。 由于时间关系,我们这里先提供修改ResolverActivity的方法,因为它相对简单。 但是,请注意:在Android 14中,ResolverActivity可能已经改名为ResolveActivity,或者逻辑被移动到其他类中。因此,我们需要根据实际代码调整。 另外,引用[2]中提到,可以通过调用设置Launcher接口来设置默认Launcher。具体来说,我们可以使用以下代码: ```java PackageManager pm = context.getPackageManager(); ComponentName cn = new ComponentName("com.example.my.launcher", "com.example.my.launcher.MainActivity"); pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); pm.addPreferredActivity(filter, IntentFilter.MATCH_CATEGORY_EMPTY, new ComponentName[]{cn}, cn); ``` 但是,这段代码需要系统权限,并且只能在系统应用中使用。我们可以在系统启动时(例如在SystemUI中)执行这段代码来设置默认Launcher。 然而,用户的问题是如何在系统级设置,因此修改系统源码是必要的。 综合以上,我们提供两种方案: 方案修改ResolverActivity): 修改ResolverActivity,在出现多个Launcher时,优先使用我们指定默认Launcher(通过系统属性配置包名),并直接启动它,跳过选择界面。 方案二(在系统启动时设置默认Launcher): 在系统启动的某个阶段(例如SystemServer中),通过PackageManagerService设置默认Launcher。这需要系统权限,并且需要知道Launcher的ComponentName。 由于方案二更彻底,且符合出厂设置,我们推荐方案二。 方案二具体步骤: 1. 在系统启动过程中,在PackageManagerService初始化完成后,设置默认Launcher。 2. 我们可以创建个系统服务(或者修改SystemServer)来执行这个操作。 但是,在Android 14中,设置默认Home应用的标准方法是使用RoleManager。在AOSP中,RoleManager是管理角色(如默认浏览器、短信应用、Launcher等)的系统服务。我们可以通过RoleManager来设置默认Launcher。 在系统应用中使用RoleManager的示例: ```java RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE); if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME)) { if (!roleManager.isRoleHeld(RoleManager.ROLE_HOME, myComponentName.getPackageName())) { Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME); // 由于是系统应用,我们可以直接赋予角色,不需要用户交互 roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, myComponentName, 0, UserHandle.SYSTEM); } } ``` 但是,addRoleHolderAsUser是系统API,需要系统权限。而且,我们可以在系统启动时由系统应用(如SystemUI)来执行这个操作。 然而,在系统启动时,我们可能还没有用户交互,所以需要等待用户解锁?或者,我们可以直接在SystemServer中设置,但SystemServer没有Context。 实际上,我们可以通过SystemServer启动个服务来执行这个操作。 考虑到这些复杂性,对于定制ROM,我们可以在SystemUI启动时设置默认Launcher(因为SystemUI具有系统权限)。 具体步骤(在SystemUI中设置): 1. 在SystemUI的某个初始化点(例如,在BootComplete之后),检查当前默认Launcher是否是我们想要的,如果不是,则设置它。 2. 在SystemUI的代码中,添加以下代码: ```java // 在合适的时机,例如在SystemUIApplication的onCreate()的某个阶段 new Thread(() -> { // 等待系统稳定 SystemClock.sleep(10000); // 等待10秒 setDefaultLauncher(); }).start(); private void setDefaultLauncher() { RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE); if (roleManager == null) return; ComponentName defaultLauncher = new ComponentName("com.example.my.launcher", "com.example.my.launcher.MainActivity"); // 检查当前默认Launcher String currentDefaultHome = roleManager.getRoleHolders(RoleManager.ROLE_HOME).get(0); if (currentDefaultHome != null && currentDefaultHome.equals(defaultLauncher.getPackageName())) { return; } // 请求设置默认Launcher roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, defaultLauncher, 0, UserHandle.SYSTEM); } ``` 注意:addRoleHolderAsUser需要系统权限,因此SystemUI必须具有系统签名。 但是,在Android 10及以上,RoleManager的addRoleHolderAsUser方法需要系统权限(android.permission.MANAGE_ROLE_HOLDERS),这个权限只授予系统签名应用。 因此,我们可以在SystemUI的AndroidManifest.xml中添加这个权限: ```xml <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> ``` 然后,在SystemUI的代码中执行上述操作。 但是,这种方法需要修改SystemUI,并且可能不是最标准的做法。 标准做法:在设备出厂时,通过预置默认Launcher,并在系统配置中指定默认Launcher。 在AOSP中,我们可以通过overlay来覆盖默认的配置。具体来说,在framework/base/core/res/res/values/config.xml中,有个配置项: ```xml <string name="default_home" translatable="false">com.android.launcher3/com.android.launcher3.Launcher</string> ``` 我们可以通过overlay机制,将这个值覆盖为我们自定义Launcher的ComponentName。 例如,在overlay的config.xml中: ```xml <string name="default_home" translatable="false">com.example.my.launcher/com.example.my.launcher.MainActivity</string> ``` 这样,系统在启动时会自动将这个Launcher设置为默认。 因此,最推荐的方法是:使用overlay覆盖config_default_home的值。 步骤: 1. 在AOSP中创建个overlay项目(例如packages/overlays/MyLauncherOverlay)。 2. 在该项目的res/values/config.xml中,添加: <resources> <string name="config_default_home" translatable="false">com.example.my.launcher/com.example.my.launcher.MainActivity</string> </resources> 3. 将该项目添加到产品配置中(例如在device.mk中添加PRODUCT_PACKAGE_OVERLAYS += packages/overlays/MyLauncherOverlay)。 这样,编译系统后,系统就会使用自定义的Launcher作为默认启动器。 总结: 推荐使用overlay覆盖config_default_home的方法,因为这是AOSP的标准做法,无需修改Java代码,且稳定可靠。 因此,用户的问题“设置Android 14自定义Launcher默认启动器”的答案是: **通过overlay覆盖config_default_home配置项,将其值设置为自定义Launcher的ComponentName。** 具体步骤: 1. 创建overlay项目,目录结构如下: MyLauncherOverlay/ ├── Android.mk └── res/ └── values/ └── config.xml 2. 在config.xml中写入: <?xml version="1.0" encoding="utf-8"?> <resources> <string name="config_default_home">com.example.my.launcher/com.example.my.launcher.MainActivity</string> </resources> 3. Android.mk内容: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := MyLauncherOverlay LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME := MyLauncherOverlay LOCAL_SDK_VERSION := current LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay/MyLauncherOverlay include $(BUILD_PACKAGE) 4. 将overlay添加到产品配置中(在设备的device.mk或类似文件中): PRODUCT_PACKAGE_OVERLAYS += packages/overlays/MyLauncherOverlay 重新编译系统并刷机,即可生效。 注意:自定义Launcher必须已经预置到系统中(在/system/priv-app/下),并且具有系统签名。 如果用户需要动态切换,那么可能需要结合RoleManager,但出厂设置默认推荐overlay方式。 因此,我们最终推荐使用overlay方式。</think>### 在 Android 14 中设置自定义 Launcher默认启动器 #### 核心原理 Android 系统通过 `ResolverActivity` 处理多个 Launcher 的选择逻辑。当系统检测到多个应用声明了 `CATEGORY_HOME` 时,会弹出选择界面。要设置默认启动器,需修改系统源码过滤非默认 Launcher 的选项[^3][^5]。 #### 实现步骤 1. **修改 AndroidManifest.xml** 在自定义 Launcher 的 MainActivity 中添加必要的 intent-filter: ```xml <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <!-- 关键声明 --> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ``` [^4] 2. **修改 ResolverActivity.java** 在 `frameworks/base/core/java/com/android/internal/app/ResolverActivity.java` 的 `rebuildList()` 方法中添加过滤逻辑: ```java // 文件路径: // frameworks/base/core/java/com/android/internal/app/ResolverActivity.java private void rebuildList() { // ... 原有代码 final String DEFAULT_LAUNCHER = "com.example.mylauncher"; // 替换为你的包名 // 过滤非默认Launcher Iterator<ResolvedComponentInfo> iterator = rciList.iterator(); while (iterator.hasNext()) { ResolvedComponentInfo info = iterator.next(); ActivityInfo ai = info.getResolveInfoAt(0).activityInfo; if (!DEFAULT_LAUNCHER.equals(ai.packageName)) { iterator.remove(); // 移除非默认Launcher } } // 如果只剩Launcher,直接启动 if (rciList.size() == 1) { onTargetSelected(rciList.get(0), false); finish(); return; } // ... 后续原有代码 } ``` [^3][^5] 3. **预置自定义 Launcher 到系统分区** 将自定义 Launcher 编译进系统镜像: ```makefile # 在设备mk文件中添加 PRODUCT_PACKAGES += MyCustomLauncher ``` 4. **重新编译系统** ```bash mmm frameworks/base mmm packages/apps/MyCustomLauncher make snod ``` #### 验证方法 1. 设备重启后,按 Home 键应直接进入自定义 Launcher 2. 检查当前默认 Launcher: ```bash adb shell cmd role get-default-holders --user 0 ``` 应输出你的 Launcher 包名 #### 注意事项 1. **签名要求**:自定义 Launcher 必须使用系统签名 2. **多用户支持**:需在 `rebuildList()` 中添加多用户处理逻辑 3. **系统更新**:修改系统核心文件需重新编译 ROM 4. **兼容性**:Android 14 的 RoleManager 机制可能影响设置,需确保在 `rebuildList()` 中优先处理[^4] > 此方法适用于 AOSP 或可定制 ROM,普通应用无法直接设置默认启动器。系统级修改需要完整编译环境支持[^1][^2]。
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值