默认Launcher(全网最全、任意Android版本、任意应用)

本文详细介绍了如何在Android系统中,无论是内置Launcher还是普通应用,设置为默认启动器的方法。包括通过ResolverActivity.java和ActivityManagerService.java的多种改法,以及针对无android.intent.category.HOME应用的强制替换和添加属性策略,尤其针对Android 11的改动进行了讲解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

修改默认launcher是系统定制中非常常见的需求,但是客户所要默认的应用可能是有源码、无源码更甚至是无源码且连android.intent.category.HOME这个Category都没有。

在本文中,我将介绍所有在Android系统修改应用为默认launcher的方法。

目录

一、Launcher应用内置并设置为默认Launcher

通过ResolverActivity.java设置为默认Launcher

改法一:

改法二:

通过ActivityManagerService.java设置为默认Launcher

改法一(常规):

改法二(简化):

二、普通应用内置并设置为默认Launcher

一、强制替换

二、添加属性

Android11开始的

Android11之前的


一、Launcher应用内置并设置为默认Launcher

这个是最常见的情况,网上也有很多,但主要还是利用ResolverActivity.java或ActivityManagerService.java来实现,接下来我将分别举例说明:

通过ResolverActivity.java设置为默认Launcher

主要是由addPreferredActivity设置默认Launcher实现。

改法一:

代码路径:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java

以下基于Android11代码:

protected void onCreate(Bundle savedInstanceState, Intent intent,
            CharSequence title, int defaultTitleRes, Intent[] initialIntents,
            List<ResolveInfo> rList, boolean supportsAlwaysUseOption) {
        setTheme(appliedThemeResId());
        super.onCreate(savedInstanceState);
 
        //add
           if(mResolvingHome){
            setDefaultLauncher();
            finish();
            return;
        }
        //add
        ...
        }


private void setDefaultLauncher() {
            try {
                final PackageManager pm = getPackageManager();
                String defPackageName = "com.android.launcher3";//默认launcher包名
                String defClassName = "com.android.searchlauncher.SearchLauncher";//默认launcher类名
                IntentFilter filter = new IntentFilter();
                filter.addAction("android.intent.action.MAIN");
                filter.addCategory("android.intent.category.HOME");
                filter.addCategory("android.intent.category.DEFAULT");
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_HOME);
                List<ResolveInfo> list = new ArrayList<ResolveInfo>();
                list = pm.queryIntentActivities(intent, 0);
                final int N = list.size();
                ComponentName[] set = new ComponentName[N];
                int bestMatch = 0;
                for (int i = 0; i < N; i++) {
                    ResolveInfo r = list.get(i);
                    set[i] = new ComponentName(r.activityInfo.packageName,
                            r.activityInfo.name);
                    if (r.match > bestMatch) bestMatch = r.match;
                }
                ComponentName preActivity = new ComponentName(defPackageName, defClassName);
                pm.addPreferredActivity(filter, bestMatch, set, preActivity);
 
            } catch (Exception e) {
                e.printStackTrace();
            }
 
    }
改法二:

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

boolean startHomeActivityLocked(int userId, String reason) {
		setDefaultLauncher();//调用设置默认launcher方法
		SystemProperties.set("persist.sys.boot.bootcomplete","1");
        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;
        }

    ...
    ...
}


private void setDefaultLauncher() {
            boolean mFirstLaunch = true;
            String packageName = "包名";//launcher包名
            String className = "类名";
            if ((packageName != null && packageName.trim().length() > 1) && (className != null && className.trim().length() > 0)) {
                if(mFirstLaunch){
                    IPackageManager pm = ActivityThread.getPackageManager();
                    ArrayList<IntentFilter> intentList = new ArrayList<IntentFilter>();
                    ArrayList<ComponentName> cnList = new ArrayList<ComponentName>();
                    mContext.getPackageManager().getPreferredActivities(intentList, cnList, null);
                    IntentFilter dhIF;
                    for(int i = 0; i < cnList.size(); i++){
                        dhIF = intentList.get(i);
                        if(dhIF.hasAction(Intent.ACTION_MAIN) &&
                                dhIF.hasCategory(Intent.CATEGORY_HOME)) {
                            mContext.getPackageManager().clearPackagePreferredActivities(cnList.get(i).getPackageName());
                        }
                    }
                    Intent intent = new Intent(Intent.ACTION_MAIN);
                    intent.addCategory(Intent.CATEGORY_HOME);
                    List<ResolveInfo> list = new ArrayList<ResolveInfo>();
                    try {
                        list = pm.queryIntentActivities(intent,
                                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                                PackageManager.MATCH_DEFAULT_ONLY,UserHandle.getCallingUserId()).getList(); //add .getList()
                    }catch (RemoteException e) {
                        throw new RuntimeException("Package manager has died", e);
                    }
                    IntentFilter filter = new IntentFilter();
                    filter.addAction(Intent.ACTION_MAIN);
                    filter.addCategory(Intent.CATEGORY_HOME);
                    filter.addCategory(Intent.CATEGORY_DEFAULT);
                    final int N = list.size();
                    ComponentName[] set = new ComponentName[N];
                    int bestMatch = 0;
                    for (int i = 0; i < N; i++)
                    {
                        ResolveInfo r = list.get(i);
                        set[i] = new ComponentName(r.activityInfo.packageName,
                                r.activityInfo.name);
                        if (r.match > bestMatch) bestMatch = r.match;
                    }
                    ComponentName launcher = new ComponentName(packageName, className);
                    try{
                        pm.addPreferredActivity(filter, bestMatch, set, launcher,UserHandle.getCallingUserId());
                    } catch (RemoteException e) {
                        throw new RuntimeException("Package manager has died", e);
                    }
                }
            }
        }

通过ActivityManagerService.java设置为默认Launcher

主要修改getHomeIntent(),将其替换成需要默认的Launcher即可。

代码路径:frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

改法一(常规):
Intent getHomeIntent() {
//add
		Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
			String defStartPkg = "你的包名";
			Intent queryIntent = new Intent();
			final PackageManager mPm = mContext.getPackageManager();
			queryIntent.addCategory(Intent.CATEGORY_HOME);///没有home可去掉
			queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
			queryIntent.setAction(Intent.ACTION_MAIN);
			List<ResolveInfo> homeActivities = mPm.queryIntentActivities(queryIntent, 0);
			if(homeActivities != null) {
				int activityNum = homeActivities.size();
				ComponentName[] set = new ComponentName[activityNum];
				for(int i = 0; i < activityNum; i++){
					ResolveInfo info = homeActivities.get(i);
					set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
					if(defStartPkg.equals(info.activityInfo.packageName)){
						Intent intentaa = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
						intentaa.setComponent(set[i]);
						intentaa.addCategory(Intent.CATEGORY_HOME);
						return intentaa;
					}
				}
			}
//add
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }
改法二(简化):
Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        //add
        //intent.setComponent(mTopComponent);
        intent.setComponent(new ComponentName("包名", "类名"));
        //add
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

二、普通应用内置并设置为默认Launcher

因为这类应用通常没有android.intent.category.HOME这个Category,故无法使用上述常规方式来设置为默认应用,使用需要我们另辟蹊径,这边也是为各位提供两种方法。

一、强制替换

首先将应用设置为默认开机启动,但因为没有android.intent.category.HOME这个Category所以提供通过ResolverActivity.java设置是不行的,需要通过修改ActivityManagerService.java来实现,只要把改法一中的queryIntent.addCategory(Intent.CATEGORY_HOME);注释掉即可:

Intent getHomeIntent() {
//add
		Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
			String defStartPkg = "你的包名";
			Intent queryIntent = new Intent();
			final PackageManager mPm = mContext.getPackageManager();
			//queryIntent.addCategory(Intent.CATEGORY_HOME);///没有home可去掉
			queryIntent.addCategory(Intent.CATEGORY_LAUNCHER);
			queryIntent.setAction(Intent.ACTION_MAIN);
			List<ResolveInfo> homeActivities = mPm.queryIntentActivities(queryIntent, 0);
			if(homeActivities != null) {
				int activityNum = homeActivities.size();
				ComponentName[] set = new ComponentName[activityNum];
				for(int i = 0; i < activityNum; i++){
					ResolveInfo info = homeActivities.get(i);
					set[i] = new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
					if(defStartPkg.equals(info.activityInfo.packageName)){
						Intent intentaa = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
						intentaa.setComponent(set[i]);
						intentaa.addCategory(Intent.CATEGORY_HOME);
						return intentaa;
					}
				}
			}
//add
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

通过上面的修改就可以开机默认启动了,但是会出现按home、recent、back键会退出应用从而进入原生Launcher的情况,所以我们需要对这些按键进行拦截定制:

代码路径:frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

 mHomeIntent =  new Intent(Intent.ACTION_MAIN, null);
        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
//add
		ComponentName mHomecom = new ComponentName("包名", "类名"); 
        mHomeIntent.setComponent(mHomecom);
//add
        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 

@Override
    public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
....
....
//add
if(keyCode == KeyEvent.KEYCODE_BACK){	if(getClsName(mContext).equals("com.ryanheise.audioservice.AudioServiceFragmentActivity")
			&&(getInputMethodWindowVisibleHeightLw()==0)){
			return -1;
			}
		}
//add
....
....
}

private String getClsName(Context context) {
        try {
             ActivityManager am = context.getSystemService(ActivityManager.class);
             List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
             return tasks.get(0).topActivity.getClassName();
        } catch (Exception e) {
           //ignore
        }
        return "";
    }

代码路径:SystemUI\src\com\android\systemui\recents\RecentsActivity.java

import android.content.ComponentName;

 Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
         homeIntent.addCategory(Intent.CATEGORY_HOME);
         ComponentName mHomecom = new ComponentName("包名", "类名"); 
         homeIntent.setComponent(mHomecom);
         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
二、添加属性
Android11开始的

android11开始谷歌对pms的解析部分做了一些重构和优化,将一些类和方法从PackageManagerService和PackageParser中分离出来,放到了parsing包和component包下,路径为:frameworks/base/core/java/android/content/pm/

所以我们要修改的文件为frameworks/base/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java

 
      @NonNull
      private static ParseResult<ParsedActivity> parseActivityOrAlias(ParsedActivity activity,
              ParsingPackage pkg, String tag, XmlResourceParser parser, Resources resources,
              TypedArray array, boolean isReceiver, boolean isAlias, boolean visibleToEphemeral,
              ParseInput input, int parentActivityNameAttr, int permissionAttr,
              int exportedAttr) throws IOException, XmlPullParserException {
          String parentActivityName = array.getNonConfigurationString(parentActivityNameAttr, Configuration.NATIVE_CONFIG_VERSION);
          if (parentActivityName != null) {
              String packageName = pkg.getPackageName();
              String parentClassName = ParsingUtils.buildClassName(packageName, parentActivityName);
              if (parentClassName == null) {
                  Log.e(TAG, "Activity " + activity.getName()
                          + " specified invalid parentActivityName " + parentActivityName);
              } else {
                  activity.setParentActivity(parentClassName);
              }
          }
  
          String permission = array.getNonConfigurationString(permissionAttr, 0);
          if (isAlias) {
              // An alias will override permissions to allow referencing an Activity through its alias
              // without needing the original permission. If an alias needs the same permission,
              // it must be re-declared.
              activity.setPermission(permission);
          } else {
              activity.setPermission(permission != null ? permission : pkg.getPermission());
          }
  
          final boolean setExported = array.hasValue(exportedAttr);
          if (setExported) {
              activity.exported = array.getBoolean(exportedAttr, false);
          }
  
          final int depth = parser.getDepth();
          int type;
          while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                  && (type != XmlPullParser.END_TAG
                  || parser.getDepth() > depth)) {
              if (type != XmlPullParser.START_TAG) {
                  continue;
              }
  
              final ParseResult result;
              if (parser.getName().equals("intent-filter")) {
                  ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                          !isReceiver, visibleToEphemeral, resources, parser, input);
                  if (intentResult.isSuccess()) {
                      ParsedIntentInfo intent = intentResult.getResult();
                      if (intent != null) {
                          activity.order = Math.max(intent.getOrder(), activity.order);
 
//add core start
                    if ("类名".equals(activity.getName()))  {
                        intent.addCategory("android.intent.category.HOME");
                        intent.addCategory("android.intent.category.DEFAULT");
                        intent.setPriority(1000);
                    }
//add core end
 
                          activity.addIntent(intent);
                          if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver
                                  && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
                              int actionCount = intent.countActions();
                              for (int i = 0; i < actionCount; i++) {
                                  final String action = intent.getAction(i);
                                  if (action == null || !action.startsWith("android.")) {
                                      continue;
                                  }
  
                                  if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
                                      Slog.w(TAG,
                                              "Broadcast " + action + " may never be delivered to "
                                                      + pkg.getPackageName() + " as requested at: "
                                                      + parser.getPositionDescription());
                                  }
                              }
                          }
                      }
                  }
                  result = intentResult;
              } else if (parser.getName().equals("meta-data")) {
                  result = ParsedComponentUtils.addMetaData(activity, pkg, resources, parser, input);
              } else if (!isReceiver && !isAlias && parser.getName().equals("preferred")) {
                  ParseResult<ParsedIntentInfo> intentResult = parseIntentFilter(pkg, activity,
                          true /*allowImplicitEphemeralVisibility*/, visibleToEphemeral,
                          resources, parser, input);
                  if (intentResult.isSuccess()) {
                      ParsedIntentInfo intent = intentResult.getResult();
                      if (intent != null) {
                          pkg.addPreferredActivityFilter(activity.getClassName(), intent);
                      }
                  }
                  result = intentResult;
              } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) {
                  ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser,
                          input);
                  if (layoutResult.isSuccess()) {
                      activity.windowLayout = layoutResult.getResult();
                  }
                  result = layoutResult;
              } else {
                  result = ParsingUtils.unknownTag(tag, pkg, parser, input);
              }
  
              if (result.isError()) {
                  return input.error(result);
              }
          }
  
          ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input);
          if (layoutResult.isError()) {
              return input.error(layoutResult);
          }
          activity.windowLayout = layoutResult.getResult();
  
          if (!setExported) {
              activity.exported = activity.getIntents().size() > 0;
          }
  
          return input.success(activity);
      } 
Android11之前的

Android11之前修改的文件为:

frameworks\base\core\java\android\content\pm\PackageParser.java

private Activity parseActivityAlias(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
            throws XmlPullParserException, IOException {
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestActivityAlias);

        ...
        ...
        int outerDepth = parser.getDepth();
        int type;
        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
               && (type != XmlPullParser.END_TAG
                       || parser.getDepth() > outerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            if (parser.getName().equals("intent-filter")) {
                ActivityIntentInfo intent = new ActivityIntentInfo(a);
                if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
                    return null;
                }
//add
				android.util.Log.e("hqb","a.info.name="+a.info.name);
				android.util.Log.e("hqb","a.info.parentActivityName="+a.info.parentActivityName);
				if(a.info.name.equals("类名")){
                        intent.addCategory("android.intent.category.HOME");
                        intent.addCategory("android.intent.category.DEFAULT");
                        intent.setPriority(1000);
				}
//add
                if (intent.countActions() == 0) {
                    Slog.w(TAG, "No actions in intent filter at "
                            + mArchiveSourcePath + " "
                            + parser.getPositionDescription());
                } else {
                    a.intents.add(intent);
                }
            } else if (parser.getName().equals("meta-data")) {
                if ((a.metaData=parseMetaData(res, parser, attrs, a.metaData,
                        outError)) == null) {
                    return null;
                }
            } 
           ...
           ... 
    }

至此,文章结束。

如有错误或纰漏,请各位斧正。

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、付费专栏及课程。

余额充值