缅文字体CTS问题
背景
缅甸有官方字体和民间字体,为了通过CTS必须将系统缅文默认字体设置为官方字体,但用户习惯使用民间字体。
需求
将官方字体和民间字体都预制到固件中,通过监听系统是否处于CTS测试状态,来实现两者字体的动态切换,若没跑CTS系统缅文默认字体为民间样式,若跑CTS则系统缅文默认字体切换为官方样式。
实现方法
在开始实现这个需求时,可以先看下这篇文章,了解下Android字体加载原理安卓字体加载原理。
1. 内置缅甸民间字体
安卓原生默认支持缅文官方字体,所以只需再内置缅文民间字体即可。
1. 在frameworks/base/data/fonts/fonts.xml中添加字体节点
<family lang="my">
<font weight="400" style="normal">ZawgyiOne.ttf</font>
</family>
注要将该节点添加在官方字体之前,原因在上述文章中有描述,如下
<family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
</family>
<family lang="my">
<font weight="400" style="normal">ZawgyiOne.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
<font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="compact">
<font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
</family>
2. 在frameworks/base/data/fonts/fonts.mk的最后加入新加的字体文件
PRODUCT_PACKAGES := \
DroidSansFallback.ttf \
DroidSansMono.ttf \
AndroidClock.ttf \
DINPro-Black.otf \
DINPro-Bold.otf \
DINPro-Light.otf \
DINPro-Medium.otf \
DINPro-Regular.otf \
ZawgyiOne.ttf
3. 在frameworks/base/data/fonts/Android.mk的font_src_files最后加入新加的字体文件
font_src_files := \
AndroidClock.ttf \
ZawgyiOne.ttf
4. 将下载的字体放入frameworks/base/data/fonts下
通过以上步骤就可以内置缅文民间字体。
2. 配置更改
为了让缅文官方字体和民间字体能够进行切换,需准备好官方字体的配置文件和民间字体的配置文件,做如下工作。
1. 准备字体配置文件
Android默认的字体配置文件为fonts.xml,里面配置了各个字体的信息,在上面添加民间字体时,我们在缅文官方字体节点前添加了民间字体的描述节点。
复制一份fonts.xml,命名为fonts_cts.xml(名字可以自定义),并将民间字体节点移动到官方字体节点底下,如下。
<family lang="und-Beng" variant="compact">
<font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="elegant">
<font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
</family>
<family lang="und-Mymr" variant="compact">
<font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
<font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
</family>
<family lang="my">
<font weight="400" style="normal">ZawgyiOne.ttf</font>
</family>
2. 在frameworks/base/data/fonts/fonts.mk中添加配置文件代码
PRODUCT_COPY_FILES := \
frameworks/base/data/fonts/fonts.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/fonts.xml \
frameworks/base/data/fonts/fonts_cts.xml:$(TARGET_COPY_OUT_SYSTEM)/etc/fonts_cts.xml \
3. 添加CTS监听器
为了动态的切换字体的样式,需要监听系统的状态,判断系统是否处于CTS测试过程,然后根据系统的当前状态设置全局属性值。
这里可以在系统的全局属性中添加属性,并设置初始值。
SHELL.whether cts is running
PRODUCT_PROPERTY_OVERRIDES += \
persist.sys.cts_Myanmar_state=false //false表示默认民间字体,true表示默认官方字体
然后可以通过编写CTS状态监听广播,规定一旦有CTS开始跑时,就设置该属性为true,并不重置。
private BroadcastReceiver mPackageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
final String packageName = intent.getData().getSchemeSpecificPart();
final int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_OWNER);
final int tmpUidCount = sUidpackage.get(uid);
if (DEBUG) Slog.d(TAG, "onReceive packageName = " + packageName + ",uid=" + uid);
if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
if (isCtsPackages(packageName) && isCtsSignatures(getSignatures(packageName, uid))) {
sUidpackage.put(uid, tmpUidCount + 1);
if (!mIsCtsRunning) {
SystemProperties.set("persist.sys.cts_Myanmar_state", "true");
mIsCtsRunning = true;
}
}
} else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
if (isCtsPackages(packageName)) {
if (tmpUidCount == 1) {
sUidpackage.delete(uid);
} else if (tmpUidCount > 1) {
sUidpackage.put(uid, tmpUidCount - 1);
}
if (mIsCtsRunning && sUidpackage.size() == 0) {
mIsCtsRunning = false;
}
}
}
}
};
4. 修改字体加载逻辑
修改frameworks/base/graphics/java/android/graphics/Typeface.java文件下的代码,其中最主要的是init()函数。
static final String FONTS_CONFIG = "fonts.xml";
static final String FONTS_CTS_CONFIG = "fonts_cts.xml";
private static void init() {
// Load font config and initialize Minikin state
File systemFontConfigLocation = getSystemFontConfigLocation();
boolean isCTSRunning = SystemProperties.getBoolean("persist.sys.cts_Myanmar_state", false);
File configFilename;
if (isCTSRunning){
configFilename = new File(systemFontConfigLocation, FONTS_CTS_CONFIG);
}else {
configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
}
// @}
...
}
从代码上可以看出,当字体加载时,会去判断系统是否处于CTS状态,如果处于CTS状态,就会加载官方字体的逻辑(fonts_cts.xml),如果不处于CTS状态,就会加载民间字体的逻辑(fonts.xml)。
注,Android在说明init()函数时,备注该函数只会在系统启动时调用,也就是如果系统不重启,该函数是不会重新调用的。因此当系统检测到CTS测试,并值全局属性为CTS状态后,需要让系统重启,字体的加载逻辑才会走缅文的官方字体逻辑。而经过我们多轮测试验证,CTS过程中,在缅文用例测试之前,会有整机重启的时机,因此这套方案是可行的。