android ROM设置默认Launcher(主屏幕应用)

本文深入探讨了如何在Android ROM中设置默认的Launcher应用,即主屏幕应用。通过分析ResolverActivity的工作原理,揭示了选择默认启动器的过程及其背后的机制。

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


修改线2016年9月29日 17:59:31
+++++++++++++++++++++++
有个国外推荐的标准方案 stackoverflow
+++++++++++++++++++++++



当系统初始化完毕后会进入homeactivity:
具体代码流程:
ActivityManagerService.java -->
public void systemReady(final Runnable goingCallback) {
...
mMainStack.resumeTopActivityLocked(null);
...
}

ActivityStack.java
final boolean resumeTopActivityLocked(ActivityRecord prev) {
        return resumeTopActivityLocked(prev, null);
    }

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
...
return mService.startHomeActivityLocked(mCurrentUser); //开始启动homeactivity了,
...
}


然后我们可以在最开始加入一个自己写的接口,来设置默认launcher:
boolean startHomeActivityLocked(int userId) {
    ...
    setDefaultLauncher();
    ...
}
private void setDefaultLauncher() {
// get default component 
    String packageName = "com.android.launcher";//默认launcher包名
    String className = "com.android.launcher2.Launcher";////默认launcher入口
    Slog.i(TAG, "defautl packageName = " + packageName + ", default className = " + className);
    if ((packageName != null && packageName.trim().length() > 1) && (className != null && className.trim().length() > 0)) {
         boolean firstLaunch = SystemProperties.getBoolean("persist.sys.sw.firstLaunch", true); //只做一次 可以注意这几个变量firstLaunch mFirstLaunch
    Slog.d(TAG, "firstLaunch = " + firstLaunch);
    if(firstLaunch){
       mFirstLaunch = true;
       // do this only for the first boot 
       SystemProperties.set("persist.sys.sw.firstLaunch", "false");
    }
    Slog.d(TAG, "firstLaunch = " + firstLaunch);
    if(mFirstLaunch){
        IPackageManager pm = ActivityThread.getPackageManager();
        //清除当前默认launcher
        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());
      }
        }
     //获取所有launcher activity
       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());
              }catch (RemoteException e) {
              throw new RuntimeException("Package manager has died", e);
         }
              // get all components and the best match 
              IntentFilter filter = new IntentFilter();
                filter.addAction(Intent.ACTION_MAIN);
                  filter.addCategory(Intent.CATEGORY_HOME);
                 filter.addCategory(Intent.CATEGORY_DEFAULT);
                final int N = list.size();
                   Slog.d(TAG, "N:::::hyhyhyhy:::: = " + N);
                      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);    
                    Slog.d(TAG, "r.activityInfo.packageName:::::hyhyhyhy:::: = " + r.activityInfo.packageName);  
                    Slog.d(TAG, "r.activityInfo.name:::::hyhyhyhy:::: = " + r.activityInfo.name);  
                    if (r.match > bestMatch) bestMatch = r.match;    
                }    
                //设置默认launcher   
                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);    
                }     
            }    
        }    
    }
然后我们可以在最开始加入一个自己写的接口,来设置默认launcher:
Launcher
Launcher是android系统的桌面、是android系统的主要组件。android系统允许存在多个Launcher并设置默认主界面。
应用程序作为Home(主界面)需在Activity的intent-filter节点中添加如下内容
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />

当系统中存在多个Home app且没有设置默认,用户点击Home键会弹出如下图所示的界面(图一):



用户可以选择“只有一次”或者“总是”来启动选择的APP
一般情况下android ROM中只会存在一个Home APP,系统启动后会直接启动此APP为默认,不需要用户选择。但是当ROM存在多个Home APP时,系统第一次启动就会弹出上图所示界面,让用户选择其中一个APP作为主屏幕应用,如果用户通过 “ALWAYS” 确认会设置选择的APP为默认的Home,用户通过 “JUST ONCE” 则此次以选择的APP为Home,再次按home键还是会弹出选择窗口

android ROM默认Launcher设置:
1:无效方案
网上有很多博客介绍如何设置ROM的默认Home app思路,都是在 packages/apps/Provision/src/com/android/provision/DefaultActivity.java 中添加一段设置默认Home的代码,代码如下
//网上的看到的
 private void setupDefaultLauncher() {
        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();

        String examplePackageName = "******"; // package name
        String exampleActivityName = "******"; // launcher activity name

        ComponentName defaultLauncher = null;

        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);

        List<ResolveInfo> resolveInfoList =
                pm.queryIntentActivities(intent, 0);
        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];
            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.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);

            pm.clearPackagePreferredActivities(defaultLauncher.getPackageName());
            pm.addPreferredActivity(filter, defaultMatch, set, defaultLauncher);
        }
    }
    //系统设置中修改默认Launcher的代码,
    private void setupDefaultLauncher2() {
    String packageName = "******"; // package name
        PackageManager mPm = getPackageManager();
        ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
        ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
        IntentFilter mHomeFilter = new IntentFilter(Intent.ACTION_MAIN);
        mHomeFilter.addCategory(Intent.CATEGORY_HOME);
        mHomeFilter.addCategory(Intent.CATEGORY_DEFAULT);
        ComponentName[] mHomeComponentSet = new ComponentName[homeActivities.size()];
        int prefIndex = 0;
        for (int i = 0; i < homeActivities.size(); i++) {
            final ResolveInfo candidate = homeActivities.get(i);
            final ActivityInfo info = candidate.activityInfo;
            ComponentName activityName = new ComponentName(info.packageName, info.name);
            mHomeComponentSet[i] = activityName;
            if(info.packageName.equals(packageName)){
                currentDefaultHome = activityName;
            }
        }
        mPm.replacePreferredActivity(mHomeFilter, IntentFilter.MATCH_CATEGORY_EMPTY,
                mHomeComponentSet, currentDefaultHome);
    }
然后我们可以在最开始加入一个自己写的接口,来设置默认launcher:
将以上任何一段代码添加到 DefaultActivity onCreate 方法中,都无法实现修改启动后的默认Launcher,只能使设置的默认Launcher为选中状态如下图(图二):



2:有效方案
既然使用上述设置默认主界面的做法无效,而在上述界面选择却有效,那么就从上面弹出的选择默认主界面的界面入手,通过追踪发现上述界面是一个Activity,Activity代码路径 frameworks/base/core/java/com/android/internal/app/ResolverActivity.java
  • ResolverActivity分析
此Activity会获取系统中所有的Home app,并根据系统的设置情况显示如上界面。此类中有一个内部类ResolveListAdapter该类继承自BaseAdapter,该类是Home app选择界面的数据适配器。
ResolveListAdapter会在ResolverActivity的 onCreate 方法中被初始化并会传入一个 ResolveInfo 类型的List,ResolveListAdapter根据会传入的 List<ResolveInfo> 初始化一个 List<DisplayResolveInfo> mList ,用户的点击事件都会在ResolveListAdapter获取数据。
用户点击”ALWAYS”的事件发生在ResolverActivity的 onButtonClick 方法中,此方法会获取选中的Item的position、或者获取用户上一次启动的Home app的,mAlwaysUseOption代表用户选中的是否为历史选择(如2图中的Launcher3),并调用startSelected。
 public void onButtonClick(View v) {
        final int id = v.getId();
        startSelected(mAlwaysUseOption ?
                mListView.getCheckedItemPosition() : mAdapter.getFilteredPosition(),
                id == R.id.button_always,
                mAlwaysUseOption);
        dismiss();
    }
StartSeletced中通过ResolveListAdapter获取选择的item代表的Home app。并且finish此activity 
onIntentSelected会根据传入的ResolveInfo设置默认的Home,并根据Intent跳转到相应界面,onIntentSelected的代码在这里就不列出。
    /**
     * 设置默认Home app并跳转,结束此Activity
     * @param which 用户选择的Item的position
     * @param always 是否设置为总是
     * @param filtered 是否非历史选择
     */
    void startSelected(int which, boolean always, boolean filtered) {
        if (isFinishing()) {
            return;
        }
        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, filtered);
        Intent intent = mAdapter.intentForPosition(which, filtered);
        //设置默认Home,并启动
        onIntentSelected(ri, intent, always);
        finish();
    }
ResolveListAdapter的相关代码
        /**
        *
        * @param position 
        * @param filtered
        * @return 
        */
        public ResolveInfo resolveInfoForPosition(int position, boolean filtered) {
            //mList为List<DisplayResolveInfo>
            return (filtered ? getItem(position) : mList.get(position)).ri;
        }
        /**
        * 
        * @param position
        * @param filtered
        * @return 
        */
        public Intent intentForPosition(int position, boolean filtered) {
            //mList为List<DisplayResolveInfo>
            DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
            return intentForDisplayResolveInfo(dri);
        }

然后我们可以在最开始加入一个自己写的接口,来设置默认launcher:
解决办法
此Activity的onCreate方法中判断是否为第一次启动,如果是则调用startSelected方法设置默认Home app。
默认Home app的从ResolveListAdapter中获取,所以在ResolveListAdapter中添加getDefaultHomePosition(String packageName)方法,用于获取默认home app在List<DisplayResolveInfo>中的位置,代码如下:
        public int getDefaultHomePosition(String packageName){
            for (int i = 0; i < mList.size(); i++) {
                ResolveInfo info = mList.get(i).ri;
                if (DEBUG)
                Log.w(TAG,"getDefaultHomePosition " + info.activityInfo.packageName);
                if (info.activityInfo.packageName.equals(packageName)) {
                   return i;
                }
            }
            return -1;
        }
在ResolverActivity中添加设置默认app的方法setupDefaultLauncher(),代码如下:
    //用于记录默认home app是否设置过
    private static final String DEFAULT_HOME = "persist.sys.default.home";
    private void setupDefaultLauncher() {
        String first = "";
        try{
            first =  SystemProperties.get(DEFAULT_HOME);
        }catch(Exception e){
            Log.w(TAG,"exception error get DEFAULT_HOME");
        }
        //判断默认home 是否设置过,如果获取的字符串为空代表,未设置,否则return不在进行设置
        if (!TextUtils.isEmpty(first)) {
            return;
        }
        //使用包名获取所需设置的默认home app在ResolveListAdapter中的位置
        int position = mAdapter.getDefaultHomePosition("home app包名");
        //如果不存在则return
        if (position == -1) {
            if (DEBUG)
            Log.w(TAG,"not find default Home");
            return;
        }
        //设置默认home app后,则添加记录
        try{
            SystemProperties.set(DEFAULT_HOME,DEFAULT_HOME);
        }catch(Exception e){
            Log.w(TAG,"exception error set DEFAULT_HOME");
        }
        //设置默认home app,并跳转
        startSelected(position, true, true);
        //结束此activity
        dismiss();
    }
为了保证mAdapter被初始化 setupDefaultLauncher()的调用添加到ResolverActivity的onCreate函数中,代码如下:
    protected void onCreate(Bundle savedInstanceState, Intent intent,
            CharSequence title, int defaultTitleRes, Intent[] initialIntents,
            List<ResolveInfo> rList, boolean alwaysUseOption) {
        //其他初始化代码
        ............
        mIntent = new Intent(intent);
        mAdapter = new ResolveListAdapter(this, initialIntents, rList,
                mLaunchedFromUid, alwaysUseOption);
        //其它初始化代码
        ............

        if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) {
            // Gulp!
            finish();
            return;
        }

        int count = mAdapter.mList.size();
        //添加的代码
        setupDefaultLauncher();
        //原有逻辑
        //如果系统中home app大于1
        if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) {
            //初始化代码
            .........
        //如果home app等于1则设置唯一的home app为Home
        } else if (count == 1) {
            safelyStartActivity(mAdapter.intentForPosition(0, false));
            mPackageMonitor.unregister();
            mRegistered = false;
            finish();
            return;
        } else {
            setContentView(R.layout.resolver_list);

            final TextView empty = (TextView) findViewById(R.id.empty);
            empty.setVisibility(View.VISIBLE);

            mListView = (ListView) findViewById(R.id.resolver_list);
            mListView.setVisibility(View.GONE);
        }
        //其它初始化代码
        ..........
    }



然后我们可以在最开始加入一个自己写的接口,来设置默认launcher:
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值