需求
默认的升级界面如下图所示,我们要修改的是中间的“正在安装系统更新”这一文字提示。
实现分析
在Recovery中,这一段文字并不是文本,而是从图片资源文件中截取的。资源文件在bootable\recovery目录下的各种res-hdpi/res-mdpi……文件夹中。打开installing_text.png文件,可以看出这个文件是包含了各种语言的文字提示。
在Recovery中,将根据传进来的本地化信息,从这个图片中截取正确的行,进行显示。那么,Recovery具体是怎么解析的呢?看下面的代码:
void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) {
int result = res_create_localized_alpha_surface(filename, locale_.c_str(), surface);
if (result < 0) {
LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")";
}
}
int res_create_localized_alpha_surface(const char* name,
const char* locale,
GRSurface** pSurface) {
GRSurface* surface = NULL;
int result = 0;
png_structp png_ptr = NULL;
png_infop info_ptr = NULL;
png_uint_32 width, height;
png_byte channels;
png_uint_32 y;
std::vector<unsigned char> row;
*pSurface = NULL;
if (locale == NULL) {
return result;
}
result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
if (result < 0) return result;
if (channels != 1) {
result = -7;
goto exit;
}
row.resize(width);
for (y = 0; y < height; ++y) {
png_read_row(png_ptr, row.data(), NULL);
int w = (row[1] << 8) | row[0]; // 解析宽度
int h = (row[3] << 8) | row[2]; // 解析高度
__unused int len = row[4];
char* loc = reinterpret_cast<char*>(&row[5]); // 解析语言
if (y+1+h >= height || matches_locale(loc, locale)) {
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
surface = malloc_surface(w*h);
if (surface == NULL) {
result = -8;
goto exit;
}
surface->width = w;
surface->height = h;
surface->row_bytes = w;
surface->pixel_bytes = 1;
int i;
for (i = 0; i < h; ++i, ++y) {
png_read_row(png_ptr, row.data(), NULL);
memcpy(surface->data + i*w, row.data(), w);
}
*pSurface = surface;
break;
} else {
int i;
for (i = 0; i < h; ++i, ++y) {
png_read_row(png_ptr, row.data(), NULL);
}
}
}
exit:
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
if (result < 0 && surface != NULL) free(surface);
return result;
}
在res_create_localized_alpha_surface()函数中,首先调用open_png()函数打开资源文件,然后遍历每一行文字,解析出宽度、高度和语言。如果语言和传进来的本地化信息匹配,就命中行。命中之后会打印日志:
printf(" %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);
从升级后的/cache/recovery/last_log中我们可以看到如下打印:
[ 0.340314] erasing_text: zh-CN (55 x 19 @ 2500)
[ 0.350683] no_command_text: zh-CN (42 x 19 @ 2500)
[ 0.359514] error_text: zh-CN (46 x 19 @ 2500)
[ 0.631050] installing_text: zh-CN (266 x 19 @ 2500)
所以传入的本地化信息字符串是zh-CN,命中的行所在位置是2500像素处。用画图工具打开原始图片,果然,简体中文的起始位置是2500像素处。
所以,要修改这个文字,只要重新制作这个素材文件即可。
制作图片素材
正常的PNG图片肯定解析不出来每一行文字的长、宽和语言信息,那么,这个素材文件是怎么生成的呢?答案是Recovery_l10n。这是一个apk,在设备上运行之后,能生成符合设备配置的多语言的素材文件。这个工具的源码的位置,因Android版本的不同而不同。在比较老的版本中,例如Android6.0,是在development/tools下面,在Android8.0以及之后的9.0中,已经移到了bootable/recovery/tools下面。
在recovery_l10n/res/values-xx的xml文件中修改文字,然后使用mmm指令进行局部编译,将生成RecoveryLocalizer.apk,将这个app安装到设备中,启动之后界面如下图所示:
在下拉菜单中选择要制作的资源文件,点击go,等待一会,就会在/data/data/com.android.recovery_l10n/files下生成text-out.png。
转换文件格式
在上一步中生成的文件并不能直接使用。打开生成的文件和原始的文件的属性,可见两处不同:
因为Recovery只能解析特定格式的png,所以就需要用到linux下的一个工具pngcrush。这个工具的使用命令如下:
pngcrush -c0 input.png output.png
将生成的文件替换掉原来的文件,你会有惊奇的发现。
(未完待续)