文章目录
1.简介
Google开机向导com.google.android.setupwizard是没有源码的,但Google提供了客制化接口,可以做OEM定制。开机向导应用以APK的形式存放在vendor/partner_gms/apps/SetupWizard
下面以Android10为例,讲述开机向导客制化的方法。
2.修改wizard_script.xml
vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml
--- a/idh.code/vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml
+++ b/idh.code/vendor/partner_gms/apps/GmsSampleIntegration/res/raw/wizard_script.xml
@@ -190,11 +190,13 @@
<WizardAction id="qr_provision_flow"
wizard:script="android.resource://com.google.android.gmsintegration/raw/wizard_script_qr_provision_flow" />
<!-- OEM completion [CUSTOMIZABLE] -->
<WizardAction id="oem_post_setup"
wizard:uri="intent:#Intent;action=com.android.setupwizard.OEM_POST_SETUP;end" />
+ <!-- MY completion [CUSTOMIZABLE] -->
+ <WizardAction id="my_post_setup"
+ wizard:uri="intent:#Intent;action=com.android.setupwizard.MY_POST_SETUP;end" />
参考oem_post_setup,在后面添加自定义的action。注意,如果客制化应用有多个Activity,就要在这里定义多个action,不能在自己的应用内跳转!
3.编写自己的Activity
怎样编写Activity属于应用开发,不是本篇讨论的范畴。我们主要看下如何才能将自己的Activity添加到开机向导。
3.1 AndroidManifest.xml声明action
<activity android:name="com.my.mysetupwizard.Activity" >
<intent-filter>
<action android:name="com.android.setupwizard.MY_POST_SETUP" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
测试一下,看看能否通过命令调起这个Activity:
am start -a com.android.setupwizard.MY_POST_SETUP
到这一步,客制化Activity已经成功地添加到开机向导了,它将在Google开机向导的最后一步被调起。但还有一个问题,此时无法退出开机向导。
3.2 如何退出Activity
(1) 首先想到的是finish()。然而事与愿违,finish()是不行的。测试结果:finish()结束了当前Activity之后,会跳回上一个页面,而不是结束开机向导;此时点击下一步,又进入了客制化Activity;这样就进入了死循环,开机向导永远无法结束。
(2) 参考博客 https://blog.csdn.net/u012824529/article/details/90109914 添加下面这段跳转代码,实测点击跳转,开机向导会crash。看来Android10已经不能这么干了(其他Android版本没有验证)。
// Google Wizard Manager Constants
private static final String ACTION_NEXT = "com.android.wizard.NEXT";
private static final String EXTRA_ACTION_ID = "actionId";
private static final String EXTRA_SCRIPT_URI = "scriptUri";
private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
public static void onNext(Intent intent, Activity activity, int resultCode) {
Intent nextIntent = new Intent(ACTION_NEXT);
nextIntent.putExtra(EXTRA_SCRIPT_URI, intent.getStringExtra(EXTRA_SCRIPT_URI));
nextIntent.putExtra(EXTRA_ACTION_ID, intent.getStringExtra(EXTRA_ACTION_ID));
nextIntent.putExtra(EXTRA_THEME, intent.getStringExtra(EXTRA_THEME));
nextIntent.putExtra(EXTRA_RESULT_CODE, resultCode);
activity.startActivityForResult(nextIntent, 10000);
}
(3) 正确的跳转办法,使用Android原生方法WizardManagerHelper.getNextIntent
class MyClickListner implements OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button:
try {
onNext(Activity.RESULT_OK);
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
public void onNext(int requestCode) {
int resultCode = Activity.RESULT_OK;
Intent intent = WizardManagerHelper.getNextIntent(getIntent(), resultCode);
try {
startActivityForResult(intent, requestCode);
} catch (ActivityNotFoundException e) {
Log.e(TAG, e.getMessage());
}
}
关键代码是这句:
Intent intent = WizardManagerHelper.getNextIntent(getIntent(), resultCode);
代码位于frameworks/opt/setupwizard/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
遗憾的是,这个方法没有开放到sdk里面供普通应用使用。既然没法直接调用,我们就把它抽出来,做成一个工具类吧。
自定义WizardManagerHelper.java
package com.my.mysetupwizard;
import java.util.Arrays;
import android.content.Intent;
public class WizardManagerHelper {
private static final String ACTION_NEXT = "com.android.wizard.NEXT";
static final String EXTRA_SCRIPT_URI = "scriptUri";
static final String EXTRA_ACTION_ID = "actionId";
private static final String EXTRA_RESULT_CODE = "com.android.setupwizard.ResultCode";
public static final String EXTRA_THEME = "theme";
static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
static final String EXTRA_IS_FIRST_RUN = "firstRun";
static final String EXTRA_IS_DEFERRED_SETUP = "deferredSetup";
static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
public static Intent getNextIntent(Intent originalIntent, int resultCode) {
return getNextIntent(originalIntent, resultCode, null);
}
public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) {
Intent intent = new Intent(ACTION_NEXT);
copyWizardManagerExtras(originalIntent, intent);
intent.putExtra(EXTRA_RESULT_CODE, resultCode);
if (data != null && data.getExtras() != null) {
intent.putExtras(data.getExtras());
}
intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME));
return intent;
}
public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
for (String key :
Arrays.asList(
EXTRA_IS_FIRST_RUN,
EXTRA_IS_DEFERRED_SETUP,
EXTRA_IS_PRE_DEFERRED_SETUP,
EXTRA_IS_SETUP_FLOW)) {
dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
}
for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
dstIntent.putExtra(key, srcIntent.getStringExtra(key));
}
}
}
4 开机向导调试方法
开机向导只在首次开机时启动,想要再次启动开机向导,只能重刷固件或者恢复出厂设置,这个太麻烦了,不适合调试。
Google开机向导SetupWizard没有源码,不好分析。我们从Android原生开机向导Provision的代码里面得到了一些信息。
packages/apps/Provision/src/com/android/provision/DefaultActivity.java
private void setProvision() {
// Add a persistent setting to allow other apps to know the device has been provisioned.
Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);
// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
// terminate the activity.
finish();
}
可见,完成开机向导的时候,做了3件事,
(1) Settings.Global.DEVICE_PROVISIONED 设置为1;
(2) Settings.Secure.USER_SETUP_COMPLETE 设置为1;
(3) DISABLE自己。
根据以上分析,想要使能开机向导,我们做一下这3件事的反向操作,就可以了。
1.通过命令使能开机向导
adb shell
settings put global device_provisioned 0
settings put secure user_setup_complete 0
pm enable com.google.android.setupwizard/com.google.android.setupwizard.SetupWizardActivity
sync
reboot
2.查询settings的值
settings get global device_provisioned
settings get secure user_setup_complete
3.通过代码实现
+Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
+Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 0);
+ComponentName name = new ComponentName("com.google.android.setupwizard", "com.google.android.setupwizard.SetupWizardActivity");
+mContext.getPackageManager().setComponentEnabledSetting(name,PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);