方案一

根据操作流程分析code flow,两者互相糅合,理清问题的关键。

从上面弹出的选择默认主界面的界面入手,通过追踪发现上述界面是一个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 mList ,用户的点击事件都会在ResolveListAdapter获取数据。
用户点击”ALWAYS”的事件发生在ResolverActivity的onButtonClick 方法中,此方法会获取选中的Item的position、或者获取用户上一次启动的Home app的,mAlwaysUseOption代表用户选中的是否为历史选择(如2图中的Launcher3),并调用startSelected。

 

1

2

3

4

5

6

7

8

9

 

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的代码在这里就不列出。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

 

/**

* 设置默认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的相关代码

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

 

/**

*

* @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 中的位置,代码如下:

 

1

2

3

4

5

6

7

8

9

10

11

 

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(),代码如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

 

//用于记录默认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函数中,代码如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

 

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的功能

方案二【优势,可以通过长按home按键进行切换】

ActivityManagerService.java在每次启动时执行,每次都默认启动设定的launcher,当然,如果设定的launcher存在的话,设置其他的launcher为默认会无效,因为重新启动时setDefaultLauncher()会将当前默认launcher清除。只有在卸载了设定默认启动的launcher后才能设置其他launcher为默认启动.
修改AMS:

 

1

 

frameworks\base\services\java\com\android\server\am\ActivityManagerService.java

新增客制化的method

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

 

private void setDefaultLauncher() {

// get default component

String packageName = "你指定的包名";//默认launcher包名

String className = "你指定的类名";////默认launcher入口

IPackageManager pm = ActivityThread.getPackageManager();

//判断指定的launcher是否存在

if (hasApkInstalled(packageName)) {

Slog.i(TAG, "defautl packageName = " + packageName + ", default className = " + className);

//清除当前默认launcher

ArrayList intentList = new ArrayList();

ArrayList cnList = new ArrayList();

mContext.getPackageManager().getPreferredActivities(intentList, cnList, null);

IntentFilter dhIF = null;

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 list = new ArrayList();

try {

list = pm.queryIntentActivities(intent,

intent.resolveTypeIfNeeded(mContext.getContentResolver()),

PackageManager.MATCH_DEFAULT_ONLY, getCurrentUserIdLocked());

} 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();

//设置默认launcher

ComponentName launcher = new ComponentName(packageName, className);

ComponentName[] set = new ComponentName[N];

int defaultMatch = 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======= " + r.activityInfo.packageName);

Slog.d(TAG, "r.activityInfo.name========= " + r.activityInfo.name);

if (launcher.getClassName().equals(r.activityInfo.name)) {

defaultMatch = r.match;

}

}

try {

pm.addPreferredActivity(filter, defaultMatch, set, launcher, getCurrentUserIdLocked());

} catch (RemoteException e) {

throw new RuntimeException("Package manager has died", e);

}

}

}

private boolean hasApkInstalled(String packageName) {

if (packageName == null || "".equals(packageName))

return false;

android.content.pm.ApplicationInfo info = null;

try {

info = mContext.getPackageManager().getApplicationInfo(packageName, 0);

return info != null;

} catch (NameNotFoundException e) {

return false;

}

}

然后在ActivityManagerService类中的
boolean startHomeActivityLocked()
方法第一行调用上面添加的
setDefaultLauncher()

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

 

boolean startHomeActivityLocked() {

if (mFactoryTest == SystemServer.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;

}

`

//-------新增方法的执行位置---------------

setDefaultLauncher();

Intent intent = new Intent(

mTopAction,

mTopData != null ? Uri.parse(mTopData) : null);

intent.setComponent(mTopComponent);

if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {

intent.addCategory(Intent.CATEGORY_HOME);

}

ActivityInfo aInfo =

intent.resolveActivityInfo(mContext.getPackageManager(),

STOCK_PM_FLAGS);

if (aInfo != null) {

intent.setComponent(new ComponentName(

aInfo.applicationInfo.packageName, aInfo.name));

// Don't do this if the home app is currently being

// instrumented.

ProcessRecord app = getProcessRecordLocked(aInfo.processName,

aInfo.applicationInfo.uid);

if (app == null || app.instrumentationClass == null) {

intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);

mMainStack.startActivityLocked(null, intent, null, null, 0, aInfo,

null, null, 0, 0, 0, false, false, null);

}

}

return true;

}

添加后的方法全部内容如上,重新编译android,烧录,开机就能够自动进入自定义的launcher
可以通过系统设置取消该launcher的默认设置,取消之后按home键会弹出launcher选择提示框
frameworks\base\core\java\com\android\internal\app\ResolverActivity.java
ResolverActivity类就是选择打开方式的弹出框

顺便查看老外有没有类似的需求,在stackoverflow上面一查询还真发现有一些精彩的答案(O(∩_∩)O哈哈~,看来奸商全球通用):
How to set default app launcher programmatically?
How to reset default launcher/home screen replacement?

更多相关文章

  1. android sdk 编译--如何将源代码加入android.jar,以及make原理
  2. [RK3288]串口开发之JNI环境搭建(基于android8.1使用android studi
  3. Android(安卓)Studio使用技巧系列教程(三)
  4. android学习轨迹之三:图片浏览+拨号程序总结
  5. android studio快捷键记录
  6. java获取系统语言(区分简体中文和繁体中文) .
  7. Android应用之Hybird混合开发,集成web页面的方法尝试
  8. Android自定义Dialog实现通用圆角对话框
  9. Android(安卓)使用Intent 在Activity 间跳转

随机推荐

  1. 解决scrollview中内容改变后自动滑动到底
  2. android实现两个页面跳转
  3. Android(安卓)Tthread 建立线程使用方法
  4. Android开发指南(38) —— Status Bar No
  5. Android定时刷新UI界面----Handler
  6. 百度贴吧客户端(Android)网络通信行为分
  7. Android(安卓)build/envsetup.sh分析
  8. Android(安卓)webkit对于网络传入的数据
  9. 最新基于adt-bundle-windows-x86的androi
  10. android ListView的滑动效率问题