android 设置默认launcher

本文介绍了如何在Android系统中设置默认的Launcher应用,通过修改系统代码实现开机自动启动指定Launcher,避免了用户选择的步骤。

本博客基于 Android 7.0,只作为沟通学习使用。

前言

平时使用android手机的时候我们可能会遇到下面的情况,比如:我们有多个浏览器,当我们没有设置哪一个为默认的浏览器并点击了一行网址时,就会弹出一个系统选择框,让用户选择一个浏览来打开这个网址,并且会让用户选择只使用一次还是将你选择的浏览器设为默认的浏览。那么问题就来了,如果我不想出现这个提示框,我就想第一次开机时,就自动帮我设置好默认的浏览器可以吗?答案是可以的。

对于浏览器、sms、邮箱等应用我们可以在 frameworks/base/core/res/res/values/config.xml 这个配置文件里面直接修改,类似于 <string name="default_browser" translatable="false">com.xxx.yyy.browser</string> 这种直接配置我们要设置成默认应用的包名就可以了,系统启动的时候会去读取这个配置文件里面的信息,但是我们在文件里面没有找到设置默认的 launcher target,那么我们怎么设置默认的launcher应用呢?

整体介绍

通过查看launcher的启动流程我们可以发现, SystemServer 调用 ActivityManagerService.systemReady 然后 systemReady 会调用 startHomeActivityLocked 去获取当前系统存在的launcher应用,最后通过 ActivityStarter.startHomeActivityLocked 去启动当前系统中launcher的主界面(如果有多个就会弹出对话框让用户选择)。

主要代码如下:

获取当前的launcher的信息:

    boolean startHomeActivityLocked(int userId, String reason) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                && mTopAction == null) {
            // We are running in factory test mode, but unable to find
            // the factory test app, so just sit around displaying the
            // error message and don't try to start anything.
            return false;
        }
        Intent intent = getHomeIntent();
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            // Don't do this if the home app is currently being
            // instrumented.
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
            }
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }

在ActivityStarter里面启动launcher的主界面:

    void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
        mSupervisor.moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason);
        startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/,
                null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/,
                null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/,
                0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/,
                0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/,
                false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/,
                null /*container*/, null /*inTask*/);
        if (mSupervisor.inResumeTopActivity) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
            mSupervisor.scheduleResumeTopActivities();
        }
    }

解决办法

我们已经知道系统会在 startHomeActivityLocked 这个方法里面去拿到当前系统的launcher 的信息,然后会通过ActivityStarter去启动界面,如果有多个就会显示选择框,如果有默认的那么就启动默认的launcher。所以,我们就解决办法就是 在 startHomeActivityLocked 方法里面就直接把某个launcher设置成默认的launcher,那么当开机完成后进入launcher就不会显示选择框了,具体代码如下:

修改systemReady.startHomeActivityLocked 如下:

boolean startHomeActivityLocked(int userId, String reason) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                && mTopAction == null) {
            // We are running in factory test mode, but unable to find
            // the factory test app, so just sit around displaying the
            // error message and don't try to start anything.
            return false;
        }

        final PackageManager mPm = mContext.getPackageManager();

        Intent homeIntent=new Intent();

        homeIntent.addCategory(Intent.CATEGORY_HOME);

        homeIntent.setAction(Intent.ACTION_MAIN);
        homeIntent.addCategory(Intent.CATEGORY_DEFAULT);
        ResolveInfo info = mPm.resolveActivity(homeIntent, PackageManager.MATCH_DEFAULT_ONLY);
        if("android".equals(info.activityInfo.packageName)){

            ComponentName DefaultLauncher=new ComponentName("com.xxx.xxx.launcher",
                    "com.xxx.xxx.launcher.activities.MainLauncherActivity");
            ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
            ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);

            ComponentName[]mHomeComponentSet = new ComponentName[homeActivities.size()];
            for (int i = 0; i < homeActivities.size(); i++) {
                final ResolveInfo candidate = homeActivities.get(i);
                Log.d(TAG,"homeActivitie: candidate = "+candidate);
                final ActivityInfo activityInfo= candidate.activityInfo;
                ComponentName activityName = new ComponentName(activityInfo.packageName, activityInfo.name);
                mHomeComponentSet[i] = activityName;
            }
            IntentFilter mHomeFilter = new IntentFilter(Intent.ACTION_MAIN);
            mHomeFilter.addCategory(Intent.CATEGORY_HOME);
            mHomeFilter.addCategory(Intent.CATEGORY_DEFAULT);
            List<ComponentName>Activities=new ArrayList();
            mPm.replacePreferredActivity(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY,mHomeComponentSet, DefaultLauncher);

        }


        Intent intent = getHomeIntent();
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            // Don't do this if the home app is currently being
            // instrumented.
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
            }
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }

其它

在MTK online上面搜到了同样的问题,但是上面多了一步针对预置了GMS套件的修改方案,如下:

在 PackageManagerService.systemReady 的方法的末尾加上如下代码:

if(isFirstBoot()) { //如果是刷机后第一次开机就进入
            String examplePackageName = "com.xxx.xxx.launcher";
            String exampleActivityName = "com.xxx.xxx.launcher.activities.MainLauncherActivity";
            Intent intent=new Intent(Intent.ACTION_MAIN);
            intent.addCategory(Intent.CATEGORY_HOME);
            final int callingUserId = UserHandle.getCallingUserId();

            List<ResolveInfo> resolveInfoList = queryIntentActivities(intent,null, PackageManager.GET_META_DATA,callingUserId).getList();
            if(resolveInfoList != null){
                int size = resolveInfoList.size();
                for(int j=0;j<size;){
                    final ResolveInfo r = resolveInfoList.get(j);
                    if(!r.activityInfo.packageName.equals(examplePackageName)) {
                        resolveInfoList.remove(j);
                        size -= 1;
                    } else {
                        j++;
                    }
                }
                ComponentName[] set = new ComponentName[size];
                ComponentName defaultLauncher=new ComponentName(examplePackageName, exampleActivityName);
                int defaultMatch=0;
                for(int i=0;i<size;i++){
                    final ResolveInfo resolveInfo = resolveInfoList.get(i);
                    Log.d(TAG,"resolveInfo = " + resolveInfo.toString());
                    set[i] = new ComponentName(resolveInfo.activityInfo.packageName,resolveInfo.activityInfo.name);
                    if(defaultLauncher.getClassName().equals(resolveInfo.activityInfo.name)){
                        defaultMatch = resolveInfo.match;
                    }
                }
                Log.d(TAG,"defaultMatch="+Integer.toHexString(defaultMatch));
                IntentFilter filter=new IntentFilter();
                filter.addAction(Intent.ACTION_MAIN);
                filter.addCategory(Intent.CATEGORY_HOME);
                filter.addCategory(Intent.CATEGORY_DEFAULT);

                addPreferredActivity2(filter, defaultMatch, set, defaultLauncher);
            }
        }
    public void addPreferredActivity2(IntentFilter filter, int match,ComponentName[] set, ComponentName activity) {
        synchronized (mPackages) {
            filter.dump(new LogPrinter(Log.INFO, TAG), "  ");
            mSettings.editPreferredActivitiesLPw(0).addFilter(new PreferredActivity(filter, match, set, activity, true));
            scheduleWriteSettingsLocked();
        }
    }

然后再修改 PackageManagerService.findPreferredActivity 中的部分代码如下:

                            /* if (removeMatches) {
                                pir.removeFilter(pa);
                                changed = true;
                                if (DEBUG_PREFERRED) {
                                    Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
                                }
                                break;
                            }

                            // Okay we found a previously set preferred or last chosen app.
                            // If the result set is different from when this
                            // was created, we need to clear it and re-ask the
                            // user their preference, if we're looking for an "always" type entry.
                            if (always && !pa.mPref.sameSet(query)) {
                                Slog.i(TAG, "Result set changed, dropping preferred activity for "
                                        + intent + " type " + resolvedType);
                                if (DEBUG_PREFERRED) {
                                    Slog.v(TAG, "Removing preferred activity since set changed "
                                            + pa.mPref.mComponent);
                                }
                                pir.removeFilter(pa);
                                // Re-add the filter as a "last chosen" entry (!always)
                                PreferredActivity lastChosen = new PreferredActivity(
                                        pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
                                pir.addFilter(lastChosen);
                                changed = true;
                                return null;
                            } */

                            if(!(intent.getAction() != null && intent.getAction().equals(intent.ACTION_MAIN) && intent.getCategories()!=null &&
intent.getCategories().contains(intent.CATEGORY_HOME))) {
                                Log.d(TAG,"Home");
                            }else {

                                if (removeMatches) {
                                    pir.removeFilter(pa);
                                    if (DEBUG_PREFERRED) {
                                        Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
                                    }
                                    break;
                                }

                            }

                            // Okay we found a previously set preferred or last chosen app.
                            // If the result set is different from when this
                            // was created, we need to clear it and re-ask the
                            // user their preference, if we're looking for an "always" type entry.
                            if (always && !pa.mPref.sameSet(query)) {
                                if(!(intent.getAction() != null && intent.getAction().equals(intent.ACTION_MAIN) && intent.getCategories()!=null &&
                                        intent.getCategories().contains(intent.CATEGORY_HOME))) {
                                    Slog.i(TAG, "Result set changed, dropping preferred activity for "
                                            + intent + " type " + resolvedType);
                                    if (DEBUG_PREFERRED) {
                                        Slog.v(TAG, "Removing preferred activity since set changed "
                                                + pa.mPref.mComponent);
                                    }
                                    pir.removeFilter(pa);
                                    // Re-add the filter as a "last chosen" entry (!always)
                                    PreferredActivity lastChosen = new PreferredActivity(
                                            pa, pa.mPref.mMatch, null, pa.mPref.mComponent, false);
                                    pir.addFilter(lastChosen);
                                    mSettings.writePackageRestrictionsLPr(userId);
                                    return null;
                                }
                            }

上面的做法可能是防止安装GMS套件的时候Google Now Launcher(GoogleHome.apk)这个应用会自动设置为默认的,所以在PMS中,如果发现是刷机后第一次开机,那么我们就会强制把默认的launcher设置成我们想要的哪一个apk。

<think>首先,用户的问题是关于在Android 13设备上设置默认启动器(Launcher)。我需要根据系统级指令和用户可见层的要求来构建我的回答。 系统级指令: - 所有行内数学表达式必须使用$...$格式:但用户的问题是关于Android设置的,没有数学内容,所以可能不需要。 - 独立公式必须使用$$...$$格式:同样,不适用。 - LaTeX语法正确:确保任何数学表达正确。 - 使用中文回答:整个回答必须用中文。 - 生成相关问题:在回答后,生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识:如[^1],我需要引用提供的参考内容。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠:基于提供的引用和一般知识。 提供的引用: - 引用[1]:MTK平台Android13实现三方launcher默认。描述了修方法,包括开机后直接进入默认launcher,没有用户选择过程,可以通过设置或屏蔽入口。 - 引用[2]:Android中级——设置默认LauncherAndroid13)。讨论了调用ResolverActivity的onCreate方法,设置布局等。 用户的问题:我想在Android 13设备上设置默认启动器(Launcher),请问Android 13如何设置默认Launcher。 我需要构建一个结构清晰的回答: 1. **介绍背景**:简要解释什么是默认Launcher和为什么需要设置它。 2. **逐步指导**:提供在Android 13上设置默认Launcher的方法。 - 用户通常可以通过系统设置设置。 - 对于开发者或高级用户,可能有其他方法。 3. **基于引用**:整合引用内容,因为用户提到了参考站内引用。 - 引用[1]提到修方法以实现默认launcher,包括屏蔽设置入口。 - 引用[2]涉及ResolverActivity的onCreate方法,这可能是在系统层面处理launcher选择。 4. **添加引用标识**:在引用相关段落末尾添加[^1]或[^2]。 步骤: - 首先,解释标准方法:通过系统设置。 - 然后,讨论高级方法:如修系统或使用代码,基于引用。 - 确保回答真实:Android 13的设置方式与早期版本类似,但可能有细微变化。引用提到MTK平台和ResolverActivity,所以我应该基于这些。 生成相关问题: - 在回答后,添加相关问题,如“如何屏蔽设置入口?”或“ResolverActivity的作用是什么?”。 回答结构: 1. **开头**:介绍默认Launcher设置。 2. **方法一:用户设置**:通过系统设置应用。 3. **方法二:开发者方法**:基于引用,涉及修系统或代码。 4. **注意事项**:如权限、风险。 5. **结尾**:总结。 6. **相关问题**:生成2-3个相关问题。 语言:全中文。 最终输出: - 回答部分。 - §§相关问题§§部分。</think>### Android 13 设置默认启动器(Launcher)的指南 在Android 13设备上设置默认启动器(Launcher),是指将您选择的第三方Launcher应用(如Nova Launcher或Microsoft Launcher)设为系统默认的主屏幕应用。这样,开机后系统会直接进入该Launcher,而不是系统自带的Launcher设置方法分为两种:用户通过系统设置界面(简单易用)和开发者通过代码或系统修(需高级权限)。下面我将一步步解释这些方法,确保您能安全可靠地操作。操作前,请备份数据,以免意外丢失。 #### 方法一:通过系统设置界面(推荐普通用户使用) 这是Android的标准方式,适用于大多数用户。Android 13保留了与早期版本类似的设置流程: 1. **安装第三方Launcher**:首先,从Google Play商店下载并安装您喜欢的Launcher应用(如Nova Launcher)。 2. **打开设置菜单**: - 进入设备的“设置”应用。 - 滚动到“应用”或“应用管理”选项。 - 选择“默认应用”或“默认应用设置”。 3. **设置默认Launcher**: - 在“默认应用”列表中,找到“主屏幕应用”或“Launcher”选项。 - 点击后,系统会列出所有已安装的Launcher应用。 - 选择您要设为默认的第三方Launcher。 4. **确认设置**: - 系统会弹出提示,询问是否设为默认。点击“确定”或“设置默认”。 - 完成后,按Home键或重启设备,系统会直接进入新Launcher。 此方法不需要root权限,操作简单。但如果您更换Launcher,系统可能会在下次启动时再次提示选择。如果希望避免提示,或永久锁定默认Launcher,可以参考开发者方法[^1]。 #### 方法二:通过开发者方法(适用于高级用户或定制系统) 如果您的设备是MTK平台或其他定制ROM,或者您希望完全屏蔽用户选择过程(例如在企业设备中),可以通过修系统配置或代码实现。这需要ADB调试权限或root权限,操作不当可能导致系统不稳定。以下是基于引用内容的步骤: 1. **修系统配置文件(MTK平台示例)**: - 在Android 13中,可以通过编辑系统文件(如`default_workspace.xml`或使用ADB命令)设置默认Launcher。例如,在MTK平台,开机后系统会直接加载指定的Launcher,无需用户选择[^1]。 - 具体步骤: - 启用USB调试:在“设置”>“开发者选项”中打开USB调试。 - 连接电脑,使用ADB命令获取当前Launcher列表:`adb shell pm list packages | grep launcher`。 - 设置默认Launcher:`adb shell cmd role set-default-launcher <package_name>`(替换`<package_name>`为您的Launcher包名,如`com.teslacoilsw.launcher`)。 - 重启设备生效。 2. **通过代码修(如ResolverActivity)**: - 对于开发者,Android 13使用`ResolverActivity`处理Launcher选择逻辑。引用[2]中提到,在`onCreate`方法中配置布局和intent,可以控制默认行为[^2]。 - 示例代码(在您的应用中实现): - 在AndroidManifest.xml中声明Launcher的intent-filter: ```xml <activity android:name=".YourLauncherActivity"> <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> </activity> ``` - 在Java/Kotlin代码中,调用系统API设置默认: ```java // Kotlin示例 val intent = Intent(Intent.ACTION_MAIN) intent.addCategory(Intent.CATEGORY_HOME) val resolveInfo = packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) if (resolveInfo != null) { val componentName = ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name) val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, componentName, 0, UserHandle.SYSTEM) } ``` - 此方法会绕过用户选择过程,开机后直接进入您的Launcher。引用[1]指出,如果需要屏蔽用户修入口,可以在系统设置中隐藏“默认应用”选项(例如通过修Settings应用代码)[^1]。 #### 注意事项 - **风险提示**:方法二需要技术知识,错误操作可能导致系统崩溃或安全漏洞。建议仅在开发或企业设备中使用。 - **兼容性**:Android 13的API可能因设备厂商(如Samsung或Xiaomi)而异。如果遇到问题,检查设备文档或使用Logcat调试。 - **测试**:设置后,测试Home键和开机流程是否正常。如果系统提示选择Launcher,说明设置未完全生效,需检查权限或重试。 - **引用总结**:基于MTK平台的实现,修后开机无用户选择过程,但可通过设置(除非屏蔽入口)[^1]。ResolverActivity的配置是核心机制,确保intent正确传递[^2]。 通过以上方法,您可以成功在Android 13上设置默认Launcher。如果问题复杂,建议咨询设备厂商或开发者社区。
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值