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,因为基本上修改这个文件就够了。

  1. Android.mk 11-Jun-2018 5.2 KiB
  2. animation.h 11-Jun-2018 1.7 KiB
  3. AnimationParser.cpp 11-Jun-2018 4.8 KiB
  4. AnimationParser.h 11-Jun-2018 1 KiB
  5. BatteryMonitor.cpp 11-Jun-2018 24 KiB
  6. BatteryPropertiesRegistrar.cpp 11-Jun-2018 4 KiB
  7. BatteryPropertiesRegistrar.h 11-Jun-2018 1.8 KiB
  8. charger.cpp 11-Jun-2018 2.8 KiB
  9. healthd.cpp 11-Jun-2018 3.8 KiB
  10. healthd_common.cpp 11-Jun-2018 8.6 KiB
  11. healthd_draw.cpp 11-Jun-2018 5.7 KiB
  12. healthd_draw.h 11-Jun-2018 2.3 KiB
  13. healthd_mode_android.cpp 11-Jun-2018 2 KiB
  14. healthd_mode_charger.cpp 11-Jun-2018 20.6 KiB
  15. images/ 11-Jun-2018 4 KiB
  16. include/ 11-Jun-2018 4 KiB
  17. tests/ 11-Jun-2018 4 KiB

修改 healthd_mode_charger.cpp

  1. 在头文件animation.h的结构体animation添加成员变量user_animation_file,如下所示;
  1. struct animation {
  2. ...
  3. #define USER_IMAGE_NUM 5
  4. std::string user_animation_file[USER_IMAGE_NUM];
  5. ...
  6. }
  1. healthd_mode_charger.cpp添加frame数组user_animation_frames,目前添加了5帧画面,而且代码里直接固定为5帧的画面为一个充电循环来做,这里后面可能需要改动一下;
  1. static animation::frame user_animation_frames[] = {
  2. {
  3. .disp_time = 750,
  4. .min_level = 0,
  5. .max_level = 19,
  6. .surface = NULL,
  7. },
  8. {
  9. .disp_time = 750,
  10. .min_level = 0,
  11. .max_level = 39,
  12. .surface = NULL,
  13. },
  14. {
  15. .disp_time = 750,
  16. .min_level = 0,
  17. .max_level = 59,
  18. .surface = NULL,
  19. },
  20. {
  21. .disp_time = 750,
  22. .min_level = 0,
  23. .max_level = 79,
  24. .surface = NULL,
  25. },
  26. {
  27. .disp_time = 750,
  28. .min_level = 0,
  29. .max_level = 100,
  30. .surface = NULL,
  31. },
  32. };

init_animation

需要对init_animation函数进行部分的修改,这里简单说明一下;

  1. animation_desc_path = "/res/values/charger/animation.txt" ,这里程序中路径具体我也没有找到源码中对应的路径,最终debug的结果是parse_success = false 是一直成立的;所以程序中会直接制定路径下的图片,源码路径system/core/healthd/images/下的图片会在编译的过程中被拷贝到制定的路径下;
  2. 原程序的做法只去解析一张png图片,而且这张图片中包含了所有电池电量百分比的对应信息。
  3. 修改部分加入到条件编译的宏定义CHARGER_USER_ANIMATION中;
  1. animation* init_animation() {
  2. bool parse_success;
  3. std::string content;
  4. if (base::ReadFileToString(animation_desc_path, &content)) {
  5. parse_success = parse_animation_desc(content, &battery_animation);
  6. } else {
  7. LOGW("Could not open animation description at %s\n", animation_desc_path);
  8. parse_success = false;
  9. }
  10. if (!parse_success) {
  11. LOGW("Could not parse animation description. Using default animation.\n");
  12. battery_animation = BASE_ANIMATION;
  13. #ifdef CHARGER_USER_ANIMATION
  14. battery_animation.user_animation_file[0].assign("charger/battery_user_0");
  15. battery_animation.user_animation_file[1].assign("charger/battery_user_1");
  16. battery_animation.user_animation_file[2].assign("charger/battery_user_2");
  17. battery_animation.user_animation_file[3].assign("charger/battery_user_3");
  18. battery_animation.user_animation_file[4].assign("charger/battery_user_4");
  19. battery_animation.frames = user_animation_frames;
  20. battery_animation.num_frames = ARRAY_SIZE(user_animation_frames);
  21. #else
  22. battery_animation.animation_file.assign("charger/battery_scale");
  23. battery_animation.frames = default_animation_frames;
  24. battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
  25. #endif
  26. }
  27. if (battery_animation.fail_file.empty()) {
  28. #ifdef CHARGER_USER_ANIMATION
  29. battery_animation.fail_file.assign("charger/battery_user_fail");
  30. #else
  31. battery_animation.fail_file.assign("charger/battery_fail");
  32. #endif
  33. }
  34. if(battery_animation.text_percent.font_file.empty())
  35. battery_animation.text_percent.font_file.assign("charger/font");
  36. //battery_animation.text_clock.font_file.assign("charger/font");
  37. LOGW("Animation Description:\n");
  38. LOGW(" animation: %d %d '%s' (%d)\n", battery_animation.num_cycles,
  39. battery_animation.first_frame_repeats, battery_animation.animation_file.c_str(),
  40. battery_animation.num_frames);
  41. LOGW(" fail_file: '%s'\n", battery_animation.fail_file.c_str());
  42. LOGW(" clock: %d %d %d %d %d %d '%s'\n", battery_animation.text_clock.pos_x,
  43. battery_animation.text_clock.pos_y, battery_animation.text_clock.color_r,
  44. battery_animation.text_clock.color_g, battery_animation.text_clock.color_b,
  45. battery_animation.text_clock.color_a, battery_animation.text_clock.font_file.c_str());
  46. LOGW(" percent: %d %d %d %d %d %d '%s'\n", battery_animation.text_percent.pos_x,
  47. battery_animation.text_percent.pos_y, battery_animation.text_percent.color_r,
  48. battery_animation.text_percent.color_g, battery_animation.text_percent.color_b,
  49. battery_animation.text_percent.color_a, battery_animation.text_percent.font_file.c_str());
  50. for (int i = 0; i < battery_animation.num_frames; i++) {
  51. LOGW(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
  52. battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
  53. }
  54. return &battery_animation;
  55. }

healthd_mode_charger_init

Android底层的2D引擎库使用了skia,对应的每一帧需要分配GRSurface,通过函数res_create_display_surface分配内存,所以,对于需要定制加入的图片,都需要重新分配内存,然后保存到anim->frames[i].surface中,具体的修改如下所示;

  1. void healthd_mode_charger_init(struct healthd_config* config) {
  2. int ret;
  3. charger* charger = &charger_state;
  4. int i;
  5. int epollfd;
  6. dump_last_kmsg();
  7. LOGW("--------------- STARTING CHARGER MODE ---------------\n");
  8. ret = ev_init(std::bind(&input_callback, charger, std::placeholders::_1, std::placeholders::_2));
  9. if (!ret) {
  10. epollfd = ev_get_epollfd();
  11. healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
  12. }
  13. animation* anim = init_animation();
  14. charger->batt_anim = anim;
  15. ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
  16. if (ret < 0) {
  17. LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
  18. ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
  19. if (ret < 0) {
  20. LOGE("Cannot load built in battery_fail image\n");
  21. charger->surf_unknown = NULL;
  22. }
  23. }
  24. #ifdef CHARGER_USER_ANIMATION
  25. GRSurface* scale_frames[USER_IMAGE_NUM];
  26. for(int i = 0; i<USER_IMAGE_NUM; i++){
  27. ret = res_create_display_surface(anim->user_animation_file[i].c_str(), &scale_frames[i]);
  28. if (ret < 0) {
  29. LOGE("Cannot load custom %s image. Reverting to built in.\n",anim->user_animation_file[i].c_str());
  30. }else{
  31. anim->frames[i].surface = scale_frames[i];
  32. LOGW("file is:[%s],anim->frames[%d].surface = charger->surf_unknown;\n",
  33. anim->user_animation_file[i].c_str(),i);
  34. }
  35. }
  36. #else
  37. GRSurface** scale_frames
  38. int scale_count;
  39. int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
  40. // chunk). We are using hard-coded frame.disp_time instead.
  41. ret = res_create_multi_display_surface(anim->animation_file.c_str(), &scale_count, &scale_fps,
  42. &scale_frames);
  43. if (ret < 0) {
  44. LOGE("Cannot load battery_scale image\n");
  45. anim->num_frames = 0;
  46. anim->num_cycles = 1;
  47. } else if (scale_count != anim->num_frames) {
  48. LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n", scale_count,
  49. anim->num_frames);
  50. anim->num_frames = 0;
  51. anim->num_cycles = 1;
  52. } else {
  53. for (i = 0; i < anim->num_frames; i++) {
  54. anim->frames[i].surface = scale_frames[i];
  55. }
  56. }
  57. #endif
  58. ev_sync_key_state(
  59. std::bind(&set_key_callback, charger, std::placeholders::_1, std::placeholders::_2));
  60. charger->next_screen_transition = -1;
  61. charger->next_key_check = -1;
  62. charger->next_pwr_check = -1;
  63. healthd_config = config;
  64. charger->boot_min_cap = config->boot_min_cap;
  65. }

替换图片

把图片复制到/system/core/healthd/images/路径下,注意图片格式需要是png,而且图片保存的位深度为8位,文件名需要和程序中定义的路径变量保持一致即可;如下所示;我简单地切了五张图片,感觉切图的时间比debug的时间还要久。苦。



然后,在Android.mk中可找到,在编译的时候对图片进行了打包,相应的命令如下所示;

  1. ...
  2. _img_modules :=
  3. _images :=
  4. $(foreach _img, $(call find-subdir-subdir-files, "images", "*.png"), \
  5. $(eval $(call _add-charger-image,$(_img))))
  6. ...

完成以上这些步骤之后,重新编译Android系统,当然还需要进入关机充电的Android模式,可以发现充电动画已经修改完了。

总结

这里介绍的是比较简单对充电动画的单帧图片进行替换,如果有更加复杂的需求,还需要在healthd_draw.cpp进行修改,或者更高级可以自己画充电动画出来也未尝不可。

Android 8.1 关机充电动画(三)Android模式的更多相关文章

  1. Android 8.1 关机充电动画(二)Uboot模式

    system:Android 8.1 platform:RK3326/PX30 uboot kernel Android 8.1 关机充电动画(一)模式选择 Android 8.1 关机充电动画(二) ...

  2. Android 8.1 关机充电动画(一)模式选择

    system:Android 8.1 platform:RK3326/PX30 uboot kernel Android 8.1 关机充电动画(一)模式选择 Android 8.1 关机充电动画(二) ...

  3. Android MTK6580 客制化关机充电动画

    1.客制化关机充电图片 vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo/xxx 找到对应分辨率替换 2.调整显示图片位置.大小 ...

  4. Android9.0 MTK 平板横屏方案修改(强制app横屏 + 开机logo/动画+关机充电横屏 + RecoveryUI 横屏)

    文章较长建议先收藏再看 拆解步骤 1.app 强制横屏显示,无视 android:screenOrientation="portrait" 属性 2.屏幕触摸坐标修改为横屏 3.开 ...

  5. Android ListView item项 显示动画

    (1)使用LayoutAnimation 所谓的布局动画,其实就是为ViewGroup添加显示动画效果,主要用过LayoutAnimationController来控制实现.LayoutAnimati ...

  6. 【转】android 电池(二):android关机充电流程、充电画面显示

    关键词:android 电池关机充电 androidboot.mode charger关机充电 充电画面显示 平台信息:内核:linux2.6/linux3.0系统:android/android4. ...

  7. Android 电池关机充电

    android 电池(一):锂电池基本原理篇 android 电池(二):android关机充电流程.充电画面显示 android 电池(三):android电池系统 android电池(四):电池 ...

  8. android 电池(二):android关机充电流程、充电画面显示【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8498580 上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下a ...

  9. android 关机充电流程

    点击打开链接 0.主要流程 usb插入通过传递cmdline给init解析从而启动充电进程 1. LK lk\app\aboot\aboot.c update_cmdline ---------- i ...

随机推荐

  1. Application.Exit

    Application.Exit:通知winform消息循环退出.Environment.Exit:终止当前进程,返回exitcode给操作系统 Application.Exit会在所有前台线程退出后 ...

  2. Python的炫技操作:条件语句的七种写法

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Python极客社区 PS:如有需要Python学习资料的小伙伴可以 ...

  3. 抠脚大叔如何改变性别,Python实现变声器功能

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 乔柯 PS:如有需要Python学习资料的小伙伴可以加点击下方链接 ...

  4. mysql搭建亿级cmd5数据库,毫秒级查询 完全过程

    前言: 最近也在玩数据库,感觉普通机子搞数据库,还是差了点,全文查找,慢的要查一分钟更久. 但是搞cmd5库很不错,亿级数据库,毫秒级. qq 944520563好吧,下面开始,首先你得需要一个mys ...

  5. 《并发编程的艺术》阅读笔记之Lock与AQS

    Lock接口 在jdk1.5之后,并发包下新增了一个lock接口,lock接口定义了锁的获取,锁的释放,等方法,需要用户手动设置.与关键字不同的是,lock具有可操作性,比如,可以中断线程,设置超时时 ...

  6. 学习Python爬虫的4幅思维导图

    这次给大家带来的是4 幅思维导图,梳理了 Python 爬虫部分核心知识点:网络基础知识,Requests,BeautifulSoup,urllib 和 Scrapy 爬虫框架. 爬虫是一个非常有趣的 ...

  7. pytorch 中交叉熵损失实现方法

  8. [Abp vNext 入坑分享] - 前言

    一·背景 Abp vnext是 ABP 框架作者所发起一个完全基于 ASP .NET Core框架,截至2020年4月份已经升级到2.5.0版本,根据经验2.0版本以后可以放心的使用在生产环境.类似a ...

  9. GOLANG 匿名函数笔记

    在函数内部,没有名字的函数,就是匿名函数 实现方法1: func main(){ a := "我是无参无返回值的匿名函数" x := func(){ //可以获取到匿名函数外部的变 ...

  10. [Windows API] Listing the Files in a Directory,可用来数文件夹下有多少个子文件(夹)

    转载 #include <windows.h> #include <tchar.h> #include <stdio.h> #include <strsafe ...