之前的博客Android ROM开发之预制GMS 给自己挖了一个坑——针对定制GMS会另写一篇,本篇补坑。定制GMS本身是一个很广泛的问题,简单的如根据需求取舍GMS包中的某些应用,复杂点的如定制Chrome的书签,主页,设置项等。本篇拿如何定制Google SetupWizard(SUW)开刀,通过本篇也可看出Google是如何在不公开GMS代码的前提下给众多合作伙伴预留下可定制的方法的。
定制 SUW 需要了解下面的知识点
SUW 部分页面是可以在源码中实现的
SUW只在第一次开机才运行,通过它可以设置系统语言、时区、Wifi 开关、Google 帐号等,它是一个隶属与GMS包里的独立应用,但这个应用的某些页面是可以在源码中实现的。
比如上面这个界面黑色框起来的部分就是在Settings下面实现的,这点是我在做Settins下的锁屏功能时发现的,Settings的锁屏一改动,上图的界面也跟着改变了,然后停在该界面用命令
adb shell dumpsys activity activities|grep mFocusedActivity
查看当前页面在哪个包中,输出信息如下:
mFocusedActivity: ActivityRecord{2a1f2a3b u0 com.android.settings/.SetupChooseLockGeneric t2}
原来发现它的实现类在Settings模块下的SetupChooseLockGeneric类中。
wizard_script配置文件
wizard_script 文件定义了 SUW 的页面跳转行为,要追加页面就需要更改 wizard_script 文件内容。这个信息Google在发布GMS包时会有专门文档做说明,由于牵涉保密性,这里无法共享出来了。wizard_script文件的内容形式如下:
<WizardAction wizard:uri="intent:#Intent;action=com.google.android.setupwizard.START_VPA;end" id="start_vpa">
<result wizard:action="date_time" />
</WizardAction>
<WizardAction wizard:uri="intent:#Intent;action=com.android.setupwizard.DATE_TIME;end" id="date_time">
<result wizard:action="user_name" />
</WizardAction>
WizardAction节点对应的就是SUW里面的一个页面,其中uri代表当前action可以处理的intent, id表示当前action,result代表点当前action跳转的目的action。
Runntime Resource Overlay(RRO)机制
这个知识点在Android 运行时资源替换—-Runtime Resource Overlay 这篇博文里介绍过,在此不重复介绍。
了解了上面的三个知识点,就可以通过RRO机制,替换掉原始SUW里面的wizard_script,从而使wizard_script能调用到我们在源码里添加的的intent。从而实现SUW的定制需求。下面开始上干货。
现在需求是在SUW WiFi 连接界面之后追加一个 SIM 卡数据设置界面。根据之前的三点知识。首先在Settings目录下编写新增的页面,java代码部分不是本篇重点,不贴出具体代码了,贴下在AndroidManifest.xml文件中注册intent的代码片段:
<activity
android:name="com.android.settings.SetupSetMobileData"
android:taskAffinity="com.android.wizard"
android:theme="@android:style/Theme.Material.Light.NoActionBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.settings.SET_MOBILEDATA" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
记住这里的android:name="android.settings.SET_MOBILEDATA"
然后准备修改wizard_script,按理说该文件Google会release给合作伙伴,但我在实际开发过程中邮件沟通了半天,也没有拿到,因此直接采取破解SUW获取wizard_script的暴力手段。破解SUW后直接grep关键字wizard_script,获取到如下信息: <string name="wizard_script_uri">android.resource://com.google.android.setupwizard/xml/wizard_script</string>
打开xml/wizard_script文件进行修改,需求是在wifi页面后追加新页面,因此搜索关键字wifi,直觉定位到这个WizardAction
<WizardAction wizard:uri="intent:#Intent;action=com.android.setupwizard.WIFI_SETTINGS;end" id="wifi_settings">
<result wizard:action="connection_check" />
</WizardAction>
<WizardAction wizard:uri="intent:#Intent;action=com.android.setupwizard.CONNECTION_CHECK;end" id="connection_check">
<result wizard:resultCode="1" wizard:action="no_account_date_time" wizard:name="skip" />
<result wizard:action="walled_garden" />
</WizardAction>
它原来的逻辑是wifi_settings界面之后,会跳转到connection_check。现在准备让wifi_settings这个action跳转到新增的SIM卡设置界面,SIM卡设置界面之后在跳转至connection_check,也就是在原来的页面里嵌入一个新增页面。根据wizard_script的规则,修改wizard_script文件。上面让记住的android:name="android.settings.SET_MOBILEDATA"
这个新加的intent该用上了,修后的片段如下:
<WizardAction wizard:uri="intent:#Intent;action=com.android.setupwizard.WIFI_SETTINGS;end" id="wifi_settings">
<result wizard:action="mobile_data_set" />
</WizardAction>
<WizardAction wizard:uri="intent:#Intent;action=android.settings.SET_MOBILEDATA;end" id="mobile_data_set">
<result wizard:action="connection_check" />
</WizardAction>
<WizardAction wizard:uri="intent:#Intent;action=com.android.setupwizard.CONNECTION_CHECK;end" id="connection_check">
<result wizard:resultCode="1" wizard:action="no_account_date_time" wizard:name="skip" />
<result wizard:action="walled_garden" />
</WizardAction>
只修改需要的地方,其他内容原样保留。将这份修改后的wizard_script文件保存为wizard_script_user.xml。
最后该制作SetupWizardOverlay.apk了。overlay目录结构
其中的AndroidManifest.xml文件内容
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.setupwizard.overlay">
<overlay android:targetPackage="com.google.android.setupwizard" android:priority="1"/>
</manifest>
config.xml文件内容
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="wizard_script_uri" translatable="false">
"android.resource://com.google.android.setupwizard.overlay/raw/wizard_script_user.xml"
</string>
</resources>
这里用overlay apk下的raw/wizard_script_user.xml替换掉了原生的wizard_script文件。
最终的效果图如下: