修改线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: