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键还是会弹出选择窗口
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 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 homeActivities = new ArrayList();
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);
}
将以上任何一段代码添加到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
初始化一个List
,用户的点击事件都会在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
return (filtered ? getItem(position) : mList.get(position)).ri;
}
/**
*
* @param position
* @param filtered
* @return
*/
public Intent intentForPosition(int position, boolean filtered) {
//mList为List
DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position);
return intentForDisplayResolveInfo(dri);
}
此Activity的onCreate方法中判断是否为第一次启动,如果是则调用startSelected方法设置默认Home app。
默认Home app的从ResolveListAdapter中获取,所以在ResolveListAdapter中添加getDefaultHomePosition(String packageName)
方法,用于获取默认home app在List
中的位置,代码如下:
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 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);
}
//其它初始化代码
..........
}
通过以上方法即可实现设置默认home的功能