system:Android 8.1
platform:RK3326/PX30
uboot
kernel
system/core/healthd
Android 8.1 关机充电动画(一)模式选择
Android 8.1 关机充电动画(二)Uboot模式
Android 8.1 关机充电动画(三)Android模式
Android
模式下的关机充电动画修改相对来说需要修改Linux
应用层的东西了,可以定位到源码路径/system/core/healthd
下,和uboot
模式类似,这里只需要修改轮播的充电图片,然后将每张图片和电量百分比对应到代码中即可,思路还是比较简单的,下面我会慢慢分析具体实现的细节。
在system/core/healthd下可以看到以下文件,只有一部分文件需要修改,我们需要重点关注一下healthd_mode_charger.cpp
,因为基本上修改这个文件就够了。
Android.mk 11-Jun-2018 5.2 KiB
animation.h 11-Jun-2018 1.7 KiB
AnimationParser.cpp 11-Jun-2018 4.8 KiB
AnimationParser.h 11-Jun-2018 1 KiB
BatteryMonitor.cpp 11-Jun-2018 24 KiB
BatteryPropertiesRegistrar.cpp 11-Jun-2018 4 KiB
BatteryPropertiesRegistrar.h 11-Jun-2018 1.8 KiB
charger.cpp 11-Jun-2018 2.8 KiB
healthd.cpp 11-Jun-2018 3.8 KiB
healthd_common.cpp 11-Jun-2018 8.6 KiB
healthd_draw.cpp 11-Jun-2018 5.7 KiB
healthd_draw.h 11-Jun-2018 2.3 KiB
healthd_mode_android.cpp 11-Jun-2018 2 KiB
healthd_mode_charger.cpp 11-Jun-2018 20.6 KiB
images/ 11-Jun-2018 4 KiB
include/ 11-Jun-2018 4 KiB
tests/ 11-Jun-2018 4 KiB
animation.h
的结构体animation
添加成员变量user_animation_file
,如下所示;struct animation {
...
#define USER_IMAGE_NUM 5
std::string user_animation_file[USER_IMAGE_NUM];
...
}
healthd_mode_charger.cpp
添加frame
数组user_animation_frames
,目前添加了5帧画面,而且代码里直接固定为5帧的画面为一个充电循环来做,这里后面可能需要改动一下;static animation::frame user_animation_frames[] = {
{
.disp_time = 750,
.min_level = 0,
.max_level = 19,
.surface = NULL,
},
{
.disp_time = 750,
.min_level = 0,
.max_level = 39,
.surface = NULL,
},
{
.disp_time = 750,
.min_level = 0,
.max_level = 59,
.surface = NULL,
},
{
.disp_time = 750,
.min_level = 0,
.max_level = 79,
.surface = NULL,
},
{
.disp_time = 750,
.min_level = 0,
.max_level = 100,
.surface = NULL,
},
};
需要对init_animation
函数进行部分的修改,这里简单说明一下;
animation_desc_path = "/res/values/charger/animation.txt"
,这里程序中路径具体我也没有找到源码中对应的路径,最终debug的结果是parse_success = false
是一直成立的;所以程序中会直接制定路径下的图片,源码路径system/core/healthd/images/
下的图片会在编译的过程中被拷贝到制定的路径下;png
图片,而且这张图片中包含了所有电池电量百分比的对应信息。CHARGER_USER_ANIMATION
中;animation* init_animation() {
bool parse_success;
std::string content;
if (base::ReadFileToString(animation_desc_path, &content)) {
parse_success = parse_animation_desc(content, &battery_animation);
} else {
LOGW("Could not open animation description at %s\n", animation_desc_path);
parse_success = false;
}
if (!parse_success) {
LOGW("Could not parse animation description. Using default animation.\n");
battery_animation = BASE_ANIMATION;
#ifdef CHARGER_USER_ANIMATION
battery_animation.user_animation_file[0].assign("charger/battery_user_0");
battery_animation.user_animation_file[1].assign("charger/battery_user_1");
battery_animation.user_animation_file[2].assign("charger/battery_user_2");
battery_animation.user_animation_file[3].assign("charger/battery_user_3");
battery_animation.user_animation_file[4].assign("charger/battery_user_4");
battery_animation.frames = user_animation_frames;
battery_animation.num_frames = ARRAY_SIZE(user_animation_frames);
#else
battery_animation.animation_file.assign("charger/battery_scale");
battery_animation.frames = default_animation_frames;
battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
#endif
}
if (battery_animation.fail_file.empty()) {
#ifdef CHARGER_USER_ANIMATION
battery_animation.fail_file.assign("charger/battery_user_fail");
#else
battery_animation.fail_file.assign("charger/battery_fail");
#endif
}
if(battery_animation.text_percent.font_file.empty())
battery_animation.text_percent.font_file.assign("charger/font");
//battery_animation.text_clock.font_file.assign("charger/font");
LOGW("Animation Description:\n");
LOGW(" animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
battery_animation.num_frames);
LOGW(" fail_file: '%s'\n", battery_animation.fail_file.c_str());
LOGW(" clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
LOGW(" percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
for (int i = 0; i < battery_animation.num_frames; i++) {
LOGW(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
}
return &battery_animation;
}
Android
底层的2D引擎库使用了skia
,对应的每一帧需要分配GRSurface
,通过函数res_create_display_surface
分配内存,所以,对于需要定制加入的图片,都需要重新分配内存,然后保存到anim->frames[i].surface
中,具体的修改如下所示;
void healthd_mode_charger_init(struct healthd_config* config) {
int ret;
charger* charger = &charger_state;
int i;
int epollfd;
dump_last_kmsg();
LOGW("--------------- STARTING CHARGER MODE ---------------\n");
ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
}
animation* anim = init_animation();
charger->batt_anim = anim;
ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
if (ret < 0) {
LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
if (ret < 0) {
LOGE("Cannot load built in battery_fail image\n");
charger->surf_unknown = NULL;
}
}
#ifdef CHARGER_USER_ANIMATION
GRSurface* scale_frames[USER_IMAGE_NUM];
for(int i = 0; i<USER_IMAGE_NUM; i++){
ret = res_create_display_surface(anim->user_animation_file[i].c_str(), &scale_frames[i]);
if (ret < 0) {
LOGE("Cannot load custom %s image. Reverting to built in.\n",anim->user_animation_file[i].c_str());
}else{
anim->frames[i].surface = scale_frames[i];
LOGW("file is:[%s],anim->frames[%d].surface = charger->surf_unknown;\n",
anim->user_animation_file[i].c_str(),i);
}
}
#else
GRSurface** scale_frames
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
&scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
anim->num_frames = 0;
anim->num_cycles = 1;
} else if (scale_count != anim->num_frames) {
LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
anim->num_frames);
anim->num_frames = 0;
anim->num_cycles = 1;
} else {
for (i = 0; i < anim->num_frames; i++) {
anim->frames[i].surface = scale_frames[i];
}
}
#endif
ev_sync_key_state(
std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
charger->next_screen_transition = -1;
charger->next_key_check = -1;
charger->next_pwr_check = -1;
healthd_config = config;
charger->boot_min_cap = config->boot_min_cap;
}
把图片复制到/system/core/healthd/images/
路径下,注意图片格式需要是png
,而且图片保存的位深度为8位,文件名需要和程序中定义的路径变量保持一致即可;如下所示;我简单地切了五张图片,感觉切图的时间比debug的时间还要久。苦。
然后,在Android.mk
中可找到,在编译的时候对图片进行了打包,相应的命令如下所示;
...
_img_modules :=
_images :=
$(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
$(eval $(call _add-charger-image,$(_img))))
...
完成以上这些步骤之后,重新编译Android
系统,当然还需要进入关机充电的Android
模式,可以发现充电动画已经修改完了。
这里介绍的是比较简单对充电动画的单帧图片进行替换,如果有更加复杂的需求,还需要在healthd_draw.cpp
进行修改,或者更高级可以自己画充电动画出来也未尝不可。