关机充电图标修改

关机充电图标修改1. 关机充电main函数
2. 函数set_draw_anim_mode()
3. 函数pthread_mutex_init();
4. 函数bootlogo_init()
5. 函数alarm_control();
6. 函数charging_control();
6.1 draw_thread_routine()
6.2 draw_with_interval()
6.3 bootlogo_show_charging()
6.4 show_battery_capacity()
6.5 fill_animation_battery_by_ver()
6.6 fill_animation_battery_new()与fill_animation_battery_old
7. 函数 key_control(pwrkeys, ARRAY_SIZE(pwrkeys));
8. solution
8.1 图片替换
8.2 修改vendor/mediatek/proprietary/external/charger/bootlogo.cpp文件
8.3 修改vendor/jrdcom/build/common/update_logo.sh文件
8.4 修改vendor/mediatek/proprietary/external/libshowlogo/cust_display.h文件
8.5 修改vendor/mediatek/proprietary/external/libshowlogo/show_animation_common.h文件
8.6 修改vendor/mediatek/proprietary/external/libshowlogo/show_animation_common.c文件
9. 总结

这几天解一个defect,主要需求是修改关机充电的图标,把原来的样式进行修该成如图1所示样子:

图 1


修改后充电各个阶段具体要求如图2所示:


android关机充电的log不全 关机充电状态图标_bc


图 2


原本的样式为蓝色电池,充电动画为电量波动状,期望修改后变成5格滚动状。(其实个人感觉还是原本的好看,而新样式设计是一个UE做,反正觉得她这个设计很脑残而且估计是抄袭把别的手机的样式吧,而且这个UE很坑壁,给的图片根本不符合要求,后面还要和她各种斯比各种gloomy).

分析如下:

  • 首先,需要找到原来版本充电图片所在位置,然后用新的图片替换掉原版本的充电图片。
    在原版本中,充电logo图片实际上是由多个位图组合起来的。如图中充电电量95%则是由3张图片组合而成,而蓝色的电池部分是由一个白的电池形状图片和蓝色条状图片组合起来的。修改logo图标即使用大小尺寸一样的图片替换掉原来图片,在替换图片时,最好是按原来图片的方式来命令新的图片名字,这样就可以不用修改具体代码的逻辑了。(后面发现还是要该部分代码)
  • 然后,和原版图标相比新样式的logo图标,各个位图的相对位置有所更改。需要找到控制位图坐标的代码并进行修改调整。
  • 最后,在连接充电器时,首先需要显示连接充电器的logo图片一秒,然后才显示充电电量的logo图标,所以需要在显示充电电量图片前加入显示该图片的逻辑。(其实原本的关机充电中,连接充电器会显示一张logo,应该是改此处)

1. 关机充电main函数

关机充电其实是一个单独的应用,主要是由init进程开启,充电部分相关源码主要在vendor/mediatek/proprietary/external/charger/目录下。应用的入口在文件vendor/mediatek/proprietary/external/charger/main.cpp中,首先看看main函数。

int main(int argc, char *argv[]) 
{

    set_draw_anim_mode(1);
    pthread_mutex_init(&lights_mutex, NULL);
    setpriority(PRIO_PROCESS, 0, -20);
    FILE *oom_adj = fopen("/proc/self/oom_adj", "w");
    if (oom_adj) {
        fputs("-17", oom_adj);
        fclose(oom_adj);
    }
    //stop_backlight();
    KPOC_LOGI("stop_backlight 1\n");
    bootlogo_init();
    alarm_control();
    charging_control();
    int i;
    for (i=0; i< ARRAY_SIZE(pwrkeys); i++)
    KPOC_LOGI("pwrkeys[%d]:%d\n",i,pwrkeys[i]);
    key_control(pwrkeys, ARRAY_SIZE(pwrkeys)); //will loop inside
    return 0;
}

2. 函数set_draw_anim_mode()

为设置充电动画的绘制模式,其中有2个模式:

  • DRAW_ANIM_MODE_SURFACE
  • DRAW_ANIM_MODE_SURFACE

3. 函数pthread_mutex_init();

这个函数作用没看懂,略过。

4. 函数bootlogo_init()

这个函数从名字上就可以看出主要是做一些初始化工作,主要包括充电动画版本(mtk有2个充电动画的版本)、动画绘制方式以及其他参数的初始化,下面看看这个函数的具体实现。

void bootlogo_init()
{
    KPOC_LOGI("[ChargingAnimation: %s %d]\n",__FUNCTION__,__LINE__);
    sync_anim_version();
    anim_init();
}

在bootlogo_init();中有2个函数

  • sync_anim_version();

主要是设定关机充电充电动画的版本,
version 0: show 4 recatangle growing animation without battery number
version 1: show wave animation with battery number
所以在这里控制了到底是用哪种充电动画

  • anim_init();

充电动画logo初始化,应该就是获得充电图片的在文件系统中的位置吧,方便后面调用指定图片。

5. 函数alarm_control();

这个函数作用没看懂,略过。

6. 函数charging_control();

这个函数比较重要,主要控制充电时led灯和动画效果的,具体来看看它的代码实现。

void charging_control()
{
    int ret = 0;
    pthread_attr_t attr, attrd, attrl;
    pthread_t uevent_thread, draw_thread, light_thread;

    //charging led control
    if (!is_charging_source_available()) {
        lights_exit();
    }
    pthread_mutex_init(&mutexlstate, NULL);
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);
    pthread_attr_init(&attr);
    pthread_attr_init(&attrd);
    pthread_attr_init(&attrl);
    inDraw = 0;
    ret = pthread_create(&uevent_thread, &attr, uevent_thread_routine, NULL);
    if (ret != 0) 
    {
        KPOC_LOGI("create uevt pthread failed.\n");
        exit_charger(EXIT_ERROR_SHUTDOWN);
    }
    firstTime = 1;
    ret = pthread_create(&draw_thread, &attrd, draw_thread_routine, NULL);
    if (ret != 0) 
    {
        KPOC_LOGI("create draw pthread failed.\n");
        exit_charger(EXIT_ERROR_SHUTDOWN);
    }
}

在该函数中,最开始对led进行检测,然后是对线程资源的初始化,后面分别开了2个线程,第一个线程主要是操作led灯的,第二个线程是操作充电动画效果的。

6.1 draw_thread_routine()

接下来看看函数draw_thread_routine()做了些什么。

  • 首先,是一些变量值的初始化,
  • 然后,进入while循环,这个while循环应该至始至终都在运行,先是等了charging_source_waiting_interval_ms长时间,保证charging_source_available准备好(实际上并没有真的等charging_source_waiting_interval_ms长时间,只要usb_online、ac_online、wireless_online、vchr中有一个满足就会跳出等待状态),同时if(firstTime)语句只会跑一次,后面都不会执行了。之后跑充电动画,在动画跑完后执行stop_backlight();看名字该是熄灭背光灯,这时候背光灯熄灭后,还执行了 bootlogo_show_charging(bc, 1);显示图片,因为背光灯熄灭了,这个图片是看不到的。那它这么做事为了干什么呢,可能是为了等下次背光灯亮起来而当前图片还没显示出来的情况下,先用bootlogo_show_charging(bc, 1);显示出图片做个缓冲吧。
  • 最后,应该是通过锁阻塞线程,让这个while循环停止在某个状态,当阻塞条件解除后继续while循环(或者说继续播放充电动画,当然这只是猜测,其实没看懂while循环后面部分那些线程锁的作用)。
static void* draw_thread_routine(void *arg)
{
    int i, bc, bc_offset = 0;
    int fd_fb = -1, err =0;
    char buf[PROPERTY_VALUE_MAX];
    char filename[32] = {0};
    // protect the following critical section for the 1st time
    pthread_mutex_lock(&mutex);
    do {
        KPOC_LOGI("draw thread working...\n");
        // move here to avoid suspend when syncing with surfaceflinger
        if(firstTime){
            // make sure charging source online when in KPOC mode
            // add 2s tolerance
            if(wait_until(is_charging_source_available, 
                        charging_source_waiting_duration_ms,
                        charging_source_waiting_interval_ms))
            {
                KPOC_LOGI("wait until charging source available\n");
            }else{
                KPOC_LOGI("charging source not available for %d ms at KPOC starup\n",
                        charging_source_waiting_duration_ms);
            }
            firstTime = 0;
        }
        inDraw = 1;
        // check the bc offest value
        bc = get_capacity();
        //动画播放的函数
        draw_with_interval(bootlogo_show_charging, bc, nChgAnimDuration_msec,    nCbInterval_msec);
        stop_backlight();
        // @@@ draw fb again to refresh ddp
        bootlogo_show_charging(bc, 1);
        /* make fb blank */
        snprintf(filename, sizeof(filename), "/dev/graphics/fb0");
        fd_fb = open(filename, O_RDWR);
        if (fd_fb < 0) {
            KPOC_LOGI("Failed to open fb0 device: %s", strerror(errno));
        }
        err = ioctl(fd_fb, FBIOBLANK, FB_BLANK_POWERDOWN);
        if (err < 0) {
            KPOC_LOGI("Failed to blank fb0 device: %s", strerror(errno));
        }
        if (fd_fb >= 0)
            close(fd_fb);
        request_suspend(true);
        inDraw = 0;
        pthread_mutex_unlock(&mutex);
        KPOC_LOGI("draw thread waiting...\n");
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&cond, &mutex);
    } while(1);
    pthread_exit(NULL);
    return NULL;
}

6.2 draw_with_interval()

这个函数功能主要是设置播放充电动画时长和动画图片间时间间隔的

  • 参数1:void (*func)(int, int) 这个就是进行图片显示的函数,通过它循环调用它实现动画效果
  • 参数2:int bc 这个参数没搞懂是做什么用的
  • 参数3:int total_time_msec 看名字就知道是播放动画的总时长
  • 参数4:int interval_msec 上一个图片和下一个图片之间的时间间隔
// total_time : ms
// interval : ms
static void draw_with_interval(void (*func)(int, int), int bc, int total_time_msec, int interval_msec)
{
    struct timeval start;
    int resume_started = 0, backlight_started = 0, cnt = 0;
    int fd_fb = -1, err = 0;
    char filename[32] = {0};
    gettimeofday(&start, NULL);
 /*这里的while循环就是动画效果的实现,不断的调用显示不同图片的函数来达到动态效果,当播放时长达到total_time_msec设定值或者有power键按下,跳出while循环停止动画效果*/
    while(!time_exceed(start, total_time_msec) && !key_trigger_suspend)
    {
        // check if need to draw animation before performing drawing
        if (!is_charging_source_available())
            return;
        if (!resume_started) {
            resume_started = 1;
            request_suspend(false);
            /* make fb unblank */
            snprintf(filename, sizeof(filename), "/dev/graphics/fb0");
            fd_fb = open(filename, O_RDWR);
            if (fd_fb < 0) {
                KPOC_LOGI("Failed to open fb0 device: %s", strerror(errno));
            }
            err = ioctl(fd_fb, FBIOBLANK, FB_BLANK_UNBLANK);
            if (err < 0) {
                KPOC_LOGI("Failed to unblank fb0 device: %s", strerror(errno));
            }
            if (fd_fb >= 0)
                close(fd_fb);
        }

        func(bc, ++cnt);
        if (!backlight_started) {
            backlight_started = 1;
            usleep(200*1000);
            start_backlight();
        }
        KPOC_LOGI("draw_with_interval... key_trigger_suspend = %d\n",key_trigger_suspend);
        /*这里就上一个图片和下一个图片之间的时间间隔,通过usleep();实现,它是以微秒为单位的。如果设置的时间长,播放的速度就比较慢,如果设置的时间短,播放的速度就比较快*/
        usleep(interval_msec*1000);
    }
}

6.3 bootlogo_show_charging()

然后是函数bootlogo_show_charging,就是通过它来显示充电动画的图片和显示充电电量的。

/*
* Show charging animation with battery capacity
 *
 */
void bootlogo_show_charging(int capacity, int cnt)
{
   KPOC_LOGI("[ChargingAnimation: %s %d]%d, %d",__FUNCTION__,__LINE__, capacity, cnt);

  if (get_battnotify_status())
   {
       KPOC_LOGI("[ChargingAnimation: %s %d] show_charger_error_logo, get_battnotify_status()= %d \n",__FUNCTION__,__LINE__, get_battnotify_status());
     show_charger_ov_logo();
       return;
   }
   if (showLowBattLogo)
    {
       KPOC_LOGI("[ChargingAnimation: %s %d] show_low_battery , showLowBattLogo = %d \n",__FUNCTION__,__LINE__,showLowBattLogo);
       show_low_battery();
      return;
    }
   show_battery_capacity(capacity);
}

show_charger_ov_logo();和 show_low_battery();分别显示电压过高的图片和低电量图片,如果出现这2个情况中一个,就return;,程序就不在向下走了,否则根据当前的电量来显示不的动画效果。

6.4 show_battery_capacity()

show_battery_capacity同样是在根据不同的动画版本(version 0 or version 1)来决定如何在屏幕上显示动画,

/*
 * Show charging animation with battery capacity
 *
 */
void show_battery_capacity(unsigned int capacity)
{
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[libshowlogo: %s %d]capacity =%d\n",__FUNCTION__,__LINE__, capacity);
    }

    if (draw_anim_mode == (DRAW_ANIM_MODE_FB)) {
        anim_fb_addr_switch();
#if defined(MTK_PUMP_EXPRESS_SUPPORT) || defined(MTK_PUMP_EXPRESS_PLUS_SUPPORT) || defined(MTK_PUMP_EXPRESS_PLUS_20_SUPPORT)
        if (2 != show_animationm_ver && get_fast_charging_state()) {
            fill_animation_battery_fast_charging(capacity, (void *)fb_addr, dec_logo_addr, logo_addr, phical_screen, draw_anim_mode);
        } else {
            fill_animation_battery_by_ver(capacity, (void *)fb_addr, dec_logo_addr, logo_addr, phical_screen, show_animationm_ver);
        }
#else
        fill_animation_battery_by_ver(capacity, (void *)fb_addr, dec_logo_addr, logo_addr, phical_screen, show_animationm_ver);
#endif
        anim_fb_disp_update();
    } else {
        ARect tmpRect;
        tmpRect.left = 0;
        tmpRect.top = 0;
        tmpRect.right = phical_screen.width;
        tmpRect.bottom = phical_screen.height;

        status_t  lockResult = surface->lock(&outBuffer, &tmpRect);
        if (MTK_LOG_ENABLE == 1) {
            SLOGD("[libshowlogo: %s %d]outBuffer.bits = %d, surface->lock return =  0x%08x,\n",__FUNCTION__,__LINE__, (int)outBuffer.bits,lockResult);
        }

        if (0 == lockResult)
        {
#if defined(MTK_PUMP_EXPRESS_SUPPORT) || defined(MTK_PUMP_EXPRESS_PLUS_SUPPORT) || defined(MTK_PUMP_EXPRESS_PLUS_20_SUPPORT)
            if (2 != show_animationm_ver && get_fast_charging_state()) {
                fill_animation_battery_fast_charging(capacity, (void *)outBuffer.bits, dec_logo_addr, logo_addr, phical_screen, draw_anim_mode);
            } else {
                fill_animation_battery_by_ver(capacity, (void *)outBuffer.bits, dec_logo_addr, logo_addr, phical_screen, show_animationm_ver);
            }
#else
            fill_animation_battery_by_ver(capacity, (void *)outBuffer.bits, dec_logo_addr, logo_addr, phical_screen, show_animationm_ver);
#endif
            surface->unlockAndPost();
        }
    }
}

6.5 fill_animation_battery_by_ver()

在vendor/mediatek/proprietary/external/libshowlogo/show_animation_common.c文件中的fill_animation_battery_by_ver方法控制关机充电的样式,根本不同的version值使用不同充电动画,方法具体实现如下。

/*
 * Show charging animation by version
 *
 */
void fill_animation_battery_by_ver(int capacity, void *fill_addr, void * dec_logo_addr, void * logo_addr,
                        LCM_SCREEN_T phical_screen, int version)
{
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[show_animation_common: %s %d]version : %d\n",__FUNCTION__,__LINE__, version);
    }
    switch (version)
    {
        case VERION_OLD_ANIMATION:
            fill_animation_battery_old(capacity, fill_addr, dec_logo_addr, logo_addr, phical_screen);

            break;
        case VERION_NEW_ANIMATION:
            fill_animation_battery_new(capacity, fill_addr, dec_logo_addr, logo_addr, phical_screen);

            break;
        case VERION_WIRELESS_CHARGING_ANIMATION:
            fill_animation_battery_wireless_charging(capacity, fill_addr, dec_logo_addr, logo_addr, phical_screen);

            break;
        default:
            fill_animation_battery_old(capacity, fill_addr, dec_logo_addr, logo_addr, phical_screen);

            break;
    }
}

然后继续查找show_animationm_ver赋值情况,可以看到文件开始位置有如下赋值语句:

/*
 * Get the defined charging animation version
 *
 */
void sync_anim_version()
{
    dprintf(INFO, "[lk logo: %s %d]\n",__FUNCTION__,__LINE__);

#ifdef ANIMATION_NEW
    show_animationm_ver = VERION_NEW_ANIMATION;
#else
    show_animationm_ver = VERION_OLD_ANIMATION;
    dprintf(INFO, "[lk logo %s %d]not define ANIMATION_NEW:show old animation \n",__FUNCTION__,__LINE__);
#endif

}

而宏定义ANIMATION_NEW是在文件vendor/mediatek/proprietary/external/libshowlogo/cust_display.h中定义的。也就是说如果定义了字段ANIMATION_NEW,则使用fill_animation_battery_new动画,否则使用fill_animation_battery_old。不管使用哪个充电动画效果,2个方法都需要使用图片组合来达到动画的效果,而图片的存放位置在一个名为logo的文件加中。名为logo的文件夹可能会比较多,具体使用哪个文件夹中的图片需要根据具体编译的项目决定,同时在device/jrdcsh[project]/ProjectConfig.mk文件中,看到BOOT_LOGO = fhd;LCM_HEIGHT =1920;LCM_WIDTH=1080;这3个字段,分别表示存放logo图片的文件加名以及手机所用屏幕分辨率。如果需要客制化这些图片,需要用新logo图片替换掉原图片。(最开始做的时候没找对路径所以替换图片后还是没有效果,主要是许多公司都会对基线代码添加定制值,项目最终使用的是定制化代码中fhd文件中的logo图标,所以一定要找对logo图标存储的文件夹再替换)。fill_animation_battery_new方法和fill_animation_battery_old方法使用到的图片共40张。

  • 低电量充电显示的动画是由[fhd_bat_10_01.bmp~ fhd_bat_10_01.bmp]这10张静态图片组成,针对verion 1.
  • 充电背景图片[fhd_bat_bg.bmp] 共1张,针对verion 1.
  • 正常充电填充图片[fhd_bat_img.bmp] 共1张,针对verion 1.
  • 充电时动画显示是由[fhd_bat_animation_01.bmp~ fhd_bat_animation_10.bmp] 这10张静态图片组成,针对verion 1.
  • 数字图片[fhd_num_0.bmp~ fhd_num_9.bmp]共10张,,针对verion 1.
  • 百分比图片[fhd_mun_percent.bmp] 共1张 ,针对verion 1.
  • 100%电量图[fhd_bat_100.bmp] 共1张 ,针对verion 1.
  • 低电量图片[fhd_low_battery.bmp] 共1张 ,未见过此情况 。
  • 过电压图片[fhd_change_ov.bmp] 共1张,未见过此情况 。
  • 电压过低的图片[fhd_low_voltage.bmp] 共1张,未见过此情况 。
  • Kernel图片[fhd_kernal.bmp] 共1张 ,
  • 关机插入充电器显示第一张图片[fhd_uboot.bmp] 共1张 ,mt_disp_show_boot_logo();方法调用,入口函数为platform_init();
  • 图片fhd_battery[fhd_battery.bmp]共1张,由方法fill_animation_battery_old调用,针对verion 0

而这些图片都由脚本vendor/jrdcom/build/common/update_logo.sh打包为一个文件,脚本具体如下。

#!/bin/bash
if [ "$1" = "" ]; then
    echo "Please input resolution,"
    echo "Such as: qvga, wqvga, wvga, hvga"
    exit
fi
p=${1}
product_path=${2}
logo_path=${3}
output_path=${4}
boot_logo=${5}
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp0.raw ${logo_path}/$p/"${boot_logo}_uboot".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp1.raw ${logo_path}/$p/"${boot_logo}_battery".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp2.raw ${logo_path}/$p/"${boot_logo}_low_battery".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp3.raw ${logo_path}/$p/"${boot_logo}_charger_ov".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp4.raw ${logo_path}/$p/"${boot_logo}_num_0".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp5.raw ${logo_path}/$p/"${boot_logo}_num_1".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp6.raw ${logo_path}/$p/"${boot_logo}_num_2".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp7.raw ${logo_path}/$p/"${boot_logo}_num_3".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp8.raw ${logo_path}/$p/"${boot_logo}_num_4".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp9.raw ${logo_path}/$p/"${boot_logo}_num_5".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp10.raw ${logo_path}/$p/"${boot_logo}_num_6".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp11.raw ${logo_path}/$p/"${boot_logo}_num_7".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp12.raw ${logo_path}/$p/"${boot_logo}_num_8".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp13.raw ${logo_path}/$p/"${boot_logo}_num_9".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp14.raw ${logo_path}/$p/"${boot_logo}_num_percent".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp15.raw ${logo_path}/$p/"${boot_logo}_bat_animation_01".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp16.raw ${logo_path}/$p/"${boot_logo}_bat_animation_02".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp17.raw ${logo_path}/$p/"${boot_logo}_bat_animation_03".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp18.raw ${logo_path}/$p/"${boot_logo}_bat_animation_04".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp19.raw ${logo_path}/$p/"${boot_logo}_bat_animation_05".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp20.raw ${logo_path}/$p/"${boot_logo}_bat_animation_06".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp21.raw ${logo_path}/$p/"${boot_logo}_bat_animation_07".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp22.raw ${logo_path}/$p/"${boot_logo}_bat_animation_08".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp23.raw ${logo_path}/$p/"${boot_logo}_bat_animation_09".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp24.raw ${logo_path}/$p/"${boot_logo}_bat_animation_10".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp25.raw ${logo_path}/$p/"${boot_logo}_bat_10_01".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp26.raw ${logo_path}/$p/"${boot_logo}_bat_10_02".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp27.raw ${logo_path}/$p/"${boot_logo}_bat_10_03".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp28.raw ${logo_path}/$p/"${boot_logo}_bat_10_04".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp29.raw ${logo_path}/$p/"${boot_logo}_bat_10_05".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp30.raw ${logo_path}/$p/"${boot_logo}_bat_10_06".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp31.raw ${logo_path}/$p/"${boot_logo}_bat_10_07".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp32.raw ${logo_path}/$p/"${boot_logo}_bat_10_08".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp33.raw ${logo_path}/$p/"${boot_logo}_bat_10_09".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp34.raw ${logo_path}/$p/"${boot_logo}_bat_10_10".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp35.raw ${logo_path}/$p/"${boot_logo}_bat_bg".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp36.raw ${logo_path}/$p/"${boot_logo}_bat_img".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp37.raw ${logo_path}/$p/"${boot_logo}_bat_100_".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp38.raw ${logo_path}/$p/"${boot_logo}_kernel".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp39.raw ${logo_path}/$p/"${boot_logo}_low_voltage".bmp

${product_path}/vendor/jrdcom/build/jrdtools/zpipe -l 9 ${output_path}/"${p}".raw ${output_path}/temp0.raw ${output_path}/temp1.raw ${output_path}/temp2.raw ${output_path}/temp3.raw ${output_path}/temp4.raw ${output_path}/temp5.raw ${output_path}/temp6.raw ${output_path}/temp7.raw ${output_path}/temp8.raw ${output_path}/temp9.raw ${output_path}/temp10.raw ${output_path}/temp11.raw ${output_path}/temp12.raw ${output_path}/temp13.raw ${output_path}/temp14.raw ${output_path}/temp15.raw ${output_path}/temp16.raw ${output_path}/temp17.raw ${output_path}/temp18.raw ${output_path}/temp19.raw ${output_path}/temp20.raw ${output_path}/temp21.raw ${output_path}/temp22.raw ${output_path}/temp23.raw ${output_path}/temp24.raw ${output_path}/temp25.raw ${output_path}/temp26.raw ${output_path}/temp27.raw ${output_path}/temp28.raw ${output_path}/temp29.raw ${output_path}/temp30.raw ${output_path}/temp31.raw ${output_path}/temp32.raw ${output_path}/temp33.raw ${output_path}/temp34.raw ${output_path}/temp35.raw ${output_path}/temp36.raw ${output_path}/temp37.raw ${output_path}/temp38.raw ${output_path}/temp39.raw
rm -rf ${output_path}/temp0.raw ${output_path}/temp1.raw ${output_path}/temp2.raw ${output_path}/temp3.raw ${output_path}/temp4.raw ${output_path}/temp5.raw ${output_path}/temp6.raw ${output_path}/temp7.raw ${output_path}/temp8.raw ${output_path}/temp9.raw ${output_path}/temp10.raw ${output_path}/temp11.raw ${output_path}/temp12.raw ${output_path}/temp13.raw ${output_path}/temp14.raw ${output_path}/temp15.raw ${output_path}/temp16.raw ${output_path}/temp17.raw ${output_path}/temp18.raw ${output_path}/temp19.raw ${output_path}/temp20.raw ${output_path}/temp21.raw ${output_path}/temp22.raw ${output_path}/temp23.raw ${output_path}/temp24.raw ${output_path}/temp25.raw ${output_path}/temp26.raw ${output_path}/temp27.raw ${output_path}/temp28.raw ${output_path}/temp29.raw ${output_path}/temp30.raw ${output_path}/temp31.raw ${output_path}/temp32.raw ${output_path}/temp33.raw ${output_path}/temp34.raw ${output_path}/temp35.raw ${output_path}/temp36.raw ${output_path}/temp37.raw ${output_path}/temp38.raw ${output_path}/temp39.raw
echo "conversion finished"

而这些图片被打包后如何使用呢?在文件vendor/mediatek/proprietary/external/libshowlogo/cust_display.h中,会把图片序号定义为宏,在代码中通过这些宏来调用指定图片。

#ifndef __CUST_DISPLAY_H__
#define __CUST_DISPLAY_H__
/*  change*/
// color                       #00FF00  #ffffff
#define BAR_OCCUPIED_COLOR  (0xFF7ED321)    // Green
#define BAR_EMPTY_COLOR     (0xFF000000)    // White
#define BAR_BG_COLOR        (0xFF000000)    // Black
/*  change*/
// LOGO number
#define ANIM_V0_LOGO_NUM   5            // version 0: show 4 recatangle growing animation without battery number
#define ANIM_V1_LOGO_NUM   39           // version 1: show wave animation with  battery number 
#define ANIM_V2_LOGO_NUM   86           // version 2: show wireless charging animation      

// Common LOGO index
#define BOOT_LOGO_INDEX   0 
#define KERNEL_LOGO_INDEX   38 

#define ANIM_V0_BACKGROUND_INDEX   1 
/*  change*/
#define SHOW_BO_POWER   34
/*  change*/
#define ANIM_V1_BACKGROUND_INDEX   35


#define LOW_BATTERY_INDEX   2 
#define CHARGER_OV_INDEX   3 
#define FULL_BATTERY_INDEX   37 

// version 1: show wave animation with  battery number 

// NUMBER LOGO INDEX
#define NUMBER_PIC_START_0   4 
#define NUMBER_PIC_PERCENT   14 

// DYNAMIC ANIMATION LOGO INDEX
#define BAT_ANIM_START_0   15 

// LOW BATTERY(0~10%) ANIMATION LOGO
#define LOW_BAT_ANIM_START_0    25 

#define ANIM_LINE_INDEX   36 

// version 2: show wireless charging animation logo index

#define V2_NUM_START_0_INDEX  57  
#define V2_NUM_PERCENT_INDEX  67 

#define V2_BAT_0_10_START_INDEX     68  
#define V2_BAT_10_40_START_INDEX    72 
#define V2_BAT_40_80_START_INDEX    76 
#define V2_BAT_80_100_START_NDEX   80

#define V2_BAT_0_INDEX   84
#define V2_BAT_100_INDEX   85

// show fast charging animation logo index

#define FAST_CHARGING_BAT_100_INDEX   39
#define FAST_CHARGING_BAT_START_0_INDEX   40

#define FAST_CHARGING_NUM_START_0_INDEX   46
#define FAST_CHARGING_NUM_PERCENT_INDEX   56

#if defined(FHD) || defined(CU_FHD) || defined(CMCC_FHD) || defined(CT_FHD) || defined(CMCC_LTE_FHD) || defined(CT_LTE_FHD) || defined(CU_LTE_FHD)
    // fhd 1080*1920
    // battery capacity rectangle
    #define CAPACITY_LEFT                (387) // battery capacity center
    #define CAPACITY_TOP                 (802)
    #define CAPACITY_RIGHT               (691)
    #define CAPACITY_BOTTOM              (1292)
    /*  change*/
    // first number rectangle
    #define NUMBER_LEFT                  (512) // number
    #define NUMBER_TOP                   (1097)
    #define NUMBER_RIGHT                 (540)
    #define NUMBER_BOTTOM                (1137)
    /*  change*/
    // %  rectangle
    #define PERCENT_LEFT                 (568) // percent number_left + 2*number_width
    #define PERCENT_TOP                  (1110)
    #define PERCENT_RIGHT                (592)
    #define PERCENT_BOTTOM               (1137)
    /*  change*/
    // top animation part
    #define TOP_ANIMATION_LEFT           (387) // top animation
    #define TOP_ANIMATION_TOP            (100)
    #define TOP_ANIMATION_RIGHT          (691)
    #define TOP_ANIMATION_BOTTOM         (152)
    /*  change*/
    // for old animation
    #define BAR_LEFT            (463)
    #define BAR_TOP             (760)
    #define BAR_RIGHT           (619)
    #define BAR_BOTTOM          (1049)
    /*  change*/
#elif defined(WQHD) || defined(CU_WQHD) || defined(CMCC_WQHD) || defined(CT_WQHD) || defined(CMCC_LTE_WQHD) || defined(CT_LTE_WQHD) || defined(CU_LTE_WQHD)
    // wqhd 1440*2560

    // battery capacity rectangle
    #define CAPACITY_LEFT                (556) // battery capacity center
    #define CAPACITY_TOP                 (1112)
    #define CAPACITY_RIGHT               (882)
    #define CAPACITY_BOTTOM              (1630)

    // first number rectangle
    #define NUMBER_LEFT                  (570) // number
    #define NUMBER_TOP                   (770)
    #define NUMBER_RIGHT                 (660)
    #define NUMBER_BOTTOM                (898)

    // %  rectangle
    #define PERCENT_LEFT                 (750) // percent number_left + 2*number_width
    #define PERCENT_TOP                  (770)
    #define PERCENT_RIGHT                (864)
    #define PERCENT_BOTTOM               (898)

    // top animation part
    #define TOP_ANIMATION_LEFT           (556) // top animation
    #define TOP_ANIMATION_TOP            (100)
    #define TOP_ANIMATION_RIGHT          (882)
    #define TOP_ANIMATION_BOTTOM         (158)

    // for old animation
    #define BAR_LEFT            (443)
    #define BAR_TOP             (337)
    #define BAR_RIGHT           (574)
    #define BAR_BOTTOM          (641)

#elif defined(HD720) || defined(CU_HD720) || defined(CMCC_HD720) || defined(CT_HD720) || defined(CMCC_LTE_HD720) || defined(CT_LTE_HD720) || defined(CU_LTE_HD720)
    // hd720 720*1280

    // battery capacity rectangle
    #define CAPACITY_LEFT                (278) // battery capacity center
    #define CAPACITY_TOP                 (556)
    #define CAPACITY_RIGHT               (441)
    #define CAPACITY_BOTTOM              (817)

    // first number rectangle
    #define NUMBER_LEFT                  (290) // number
    #define NUMBER_TOP                   (386)
    #define NUMBER_RIGHT                 (335)
    #define NUMBER_BOTTOM                (450)

    // %  rectangle
    #define PERCENT_LEFT                 (380) // percent number_left + 2*number_width
    #define PERCENT_TOP                  (386)
    #define PERCENT_RIGHT                (437)
    #define PERCENT_BOTTOM               (450)

    // top animation part
    #define TOP_ANIMATION_LEFT           (278) // top animation
    #define TOP_ANIMATION_TOP            (100)
    #define TOP_ANIMATION_RIGHT          (441)
    #define TOP_ANIMATION_BOTTOM         (129)

    // for old animation
    #define BAR_LEFT            (313)
    #define BAR_TOP             (238)
    #define BAR_RIGHT           (406)
    #define BAR_BOTTOM          (453)

同时,在这个文件中还定义了各个图片在屏幕上的坐标位置,以fhd 1080*1920这个屏幕为例,手机屏幕左上角的坐标为(0,0)(是以一个像素为一个单位来计的)右下角的坐标为(1080,1920)。

  • battery capacity rectangle –表示电池电量图像的坐标,针对verion 1.
  • first number rectangle – 表示电池电量值十位数上数字的坐标,针对verion 1.
  • % rectangle – 表示电池电量值百分号的坐标,针对verion 1.
  • top animation part – 表示电池充电时波浪状动画的坐标,针对verion 1.
  • for old animation – 表示电池电量图像的坐标,针对verion 0.
    如果想修改各个图片在屏幕上的位置,只需要调整对应坐标即可。
#if defined(FHD) || defined(CU_FHD) || defined(CMCC_FHD) || defined(CT_FHD) || defined(CMCC_LTE_FHD) || defined(CT_LTE_FHD) || defined(CU_LTE_FHD)
    // fhd 1080*1920

    // battery capacity rectangle
    #define CAPACITY_LEFT                (387) // battery capacity center
    #define CAPACITY_TOP                 (802)
    #define CAPACITY_RIGHT               (691)
    #define CAPACITY_BOTTOM              (1292)
    // first number rectangle
    #define NUMBER_LEFT                  (512) // number
    #define NUMBER_TOP                   (1097)
    #define NUMBER_RIGHT                 (540)
    #define NUMBER_BOTTOM                (1137)
    // %  rectangle
    #define PERCENT_LEFT                 (568) // percent number_left + 2*number_width
    #define PERCENT_TOP                  (1110)
    #define PERCENT_RIGHT                (592)
    #define PERCENT_BOTTOM               (1137)
    // top animation part
    #define TOP_ANIMATION_LEFT           (387) // top animation
    #define TOP_ANIMATION_TOP            (100)
    #define TOP_ANIMATION_RIGHT          (691)
    #define TOP_ANIMATION_BOTTOM         (152)
    // for old animation
    #define BAR_LEFT            (463)
    #define BAR_TOP             (760)
    #define BAR_RIGHT           (619)
    #define BAR_BOTTOM          (1049)

6.6 fill_animation_battery_new()与fill_animation_battery_old

接下来分别查看fill_animation_battery_new()与fill_animation_battery_old这2个函数的具体逻辑。

  • 首先,是fill_animation_battery_new函数,正如前面所说,该函数的动画效果是显示波浪状充电效果和电池电量白百分比(version 1: show wave animation with battery number)。
    在该方法中分别设定0~10%、10~90%、90~100%、100%等4个电量显示效果。
    0~10%为低电量效果:显示的大概是图片[fhd_bat_10_01.bmp~ fhd_bat_10_01.bmp]共10张轮流播放效果。
    10~90%为正常充电效果:显示由图片[fhd_bat_bg.bmp] 为背景,图片[fhd_bat_img.bmp]填充实际电量,图片[fhd_bat_animation_01.bmp~ fhd_bat_animation_10.bmp] 共10张显示动画效果3部分组成。
    90~100%正常充电效果:显示由图片[fhd_bat_bg.bmp] 为背景,图片[fhd_bat_img.bmp]填充实际电量,无动画效果,由前面2部分组成。
    100%电量效果:由图[fhd_bat_100.bmp] 共1张表示。
/*change*/
/*
 * Show new charging animation
 *
 */
void fill_animation_battery_new(int capacity, void *fill_addr, void * dec_logo_addr, void * logo_addr, LCM_SCREEN_T phical_screen)
{
    if (MTK_LOG_ENABLE == 1) {
        SLOGD("[show_animation_common: %s %d]capacity : %d\n",__FUNCTION__,__LINE__, capacity);
    }

    if (capacity >= 100) {
        //show_logo(37); // battery 100
        fill_animation_logo(FULL_BATTERY_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);

    } else if (capacity < 10) {
        if (MTK_LOG_ENABLE == 1) {
            SLOGD("[show_animation_common: %s %d]charging_low_index = %d\n",__FUNCTION__,__LINE__, charging_low_index);
        }
        //通过charging_low_index++来循环显示低电量的10张图片
        charging_low_index ++ ;
        fill_animation_logo(LOW_BAT_ANIM_START_0 + charging_low_index, fill_addr, dec_logo_addr, logo_addr,phical_screen);
        //显示电量值上个位数的值
        fill_animation_number(NUMBER_PIC_START_0 + capacity, 1, fill_addr, logo_addr, phical_screen);
        fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);

        if (charging_low_index >= 9) charging_low_index = 0;

    } else {

        unsigned int capacity_grids = 0;
        //static RECT_REGION_T battery_rect = {CAPACITY_LEFT,CAPACITY_TOP,CAPACITY_RIGHT,CAPACITY_BOTTOM};
        /*把电池分成100等分,然后计算当前电量所占比重(因为背景图片已近画了10%电量,所以这里有capacity - 10这个操作)*/
        capacity_grids = CAPACITY_BOTTOM - (CAPACITY_BOTTOM - CAPACITY_TOP) * (capacity - 10) / 90;

        if (MTK_LOG_ENABLE == 1) {
            SLOGD("[show_animation_common: %s %d]capacity_grids : %d,charging_animation_index = %d\n"
                     ,__FUNCTION__,__LINE__, capacity_grids,charging_animation_index);
        }
        //background,即充电时的背景图片fhd_bat_bg.bmp,在该图中已经画出了10%电量
        fill_animation_logo(ANIM_V1_BACKGROUND_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);
         //画出当前电池对应的电量
        fill_animation_line(ANIM_LINE_INDEX, capacity_grids, fill_addr,  logo_addr, phical_screen);
        fill_animation_number(NUMBER_PIC_START_0 + (capacity/10), 0, fill_addr, logo_addr, phical_screen);
        fill_animation_number(NUMBER_PIC_START_0 + (capacity%10), 1, fill_addr, logo_addr, phical_screen);
        fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
        //如果电量小于90%,加上充电动画效果
         if (capacity <= 90)
         {
            RECT_REGION_T top_animation_rect = {TOP_ANIMATION_LEFT, capacity_grids - (TOP_ANIMATION_BOTTOM - TOP_ANIMATION_TOP), TOP_ANIMATION_RIGHT, capacity_grids};
            //top_animation_rect.bottom = capacity_grids;
            //top_animation_rect.top = capacity_grids - top_animation_height;
            charging_animation_index++;
            //show_animation_dynamic(15 + charging_animation_index, top_animation_rect, top_animation_addr);
            fill_animation_dynamic(BAT_ANIM_START_0 + charging_animation_index, top_animation_rect, fill_addr,
                            top_animation_addr, logo_addr, phical_screen);
            if (charging_animation_index >= 9) charging_animation_index = 0;
         }
    }
}
  • 然后,是fill_animation_battery_old函数,该函数的动画是4格滚动的动画效果(version 0: show 4 recatangle growing animation without battery number),在该动画中使用图片fhd_battery.bmp充当背景图片,然后其他的4格电量都是通过函数fill_animation_prog_bar动态画出来的,fill_animation_battery_old函数具体实现如下。
/*
 *
 * Show old charging animation
 *
 */
void fill_animation_battery_old(int capacity,  void *fill_addr, void * dec_logo_addr, void * logo_addr,
                       LCM_SCREEN_T phical_screen)
{
    int capacity_grids = 0;
    if (capacity > 100) capacity = 100;
    //这里表示计算当前电量占的比重,字段ANIM_V0_REGIONS表示把电池划分成几个格的电量
    capacity_grids = (capacity * (ANIM_V0_REGIONS)) / 100;

    if (version0_charging_index < capacity_grids * 2)
        version0_charging_index = capacity_grids * 2;
    if (capacity < 100){
        version0_charging_index > 7? version0_charging_index = capacity_grids * 2 : version0_charging_index++;
    } else {
        version0_charging_index = ANIM_V0_REGIONS * 2;
    }
    //此处为显示背景图片fhd_battery.bmp的函数
    fill_animation_logo(ANIM_V0_BACKGROUND_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);
    RECT_REGION_T rect_bar = {bar_rect.left + 1, bar_rect.top + 1, bar_rect.right, bar_rect.bottom};
    //此处动态画出第0格到version0_charging_index/2格的电量
    fill_animation_prog_bar(rect_bar,
                       (unsigned int)(BAR_OCCUPIED_COLOR),
                       0,  version0_charging_index/2,
                       fill_addr, phical_screen);
     /*此处动态画出第version0_charging_index/2格到ANIM_V0_REGIONS - version0_charging_index/2格的电量,与前面的函数交替使用就会出现4格电量滚动的效果(当然还需要在draw_thread_routine中不断while循环)*/
    fill_animation_prog_bar(rect_bar,
                      (unsigned int)(BAR_EMPTY_COLOR),
                      version0_charging_index/2, ANIM_V0_REGIONS - version0_charging_index/2,
                      fill_addr, phical_screen);
}

接下老看看函数fill_animation_prog_bar的具体实现。

/*
 * Fill a rectangle size address with special color
 *
 */
void fill_animation_prog_bar(RECT_REGION_T rect_bar,
                       unsigned int fgColor,
                       unsigned int start_div, unsigned int occupied_div,
                       void *fill_addr, LCM_SCREEN_T phical_screen)
{    //计算每一个格电量占多少像素
    unsigned int div_size  = (rect_bar.bottom - rect_bar.top) / (ANIM_V0_REGIONS);
    /*这是计算每格电量实际需要画多少像素的图像,字段ANIM_V0_SPACE_AFTER_REGION为各个电量格之间的空隙,如果为0则表示电量格之间无空隙*/
    unsigned int draw_size = div_size - (ANIM_V0_SPACE_AFTER_REGION);
    unsigned int i;
    for (i = start_div; i < start_div + occupied_div; ++ i)
    {
        unsigned int draw_bottom = rect_bar.bottom - div_size * i - (ANIM_V0_SPACE_AFTER_REGION);
        unsigned int draw_top    = draw_bottom - draw_size;
        RECT_REGION_T rect = {rect_bar.left, draw_top, rect_bar.right, draw_bottom};
         //这是函数才是真正进行电量格绘画的
        fill_rect_with_color(fill_addr, rect, fgColor, phical_screen);
    }
}

7. 函数 key_control(pwrkeys, ARRAY_SIZE(pwrkeys));

这个函数从名字上看该是对按键进行控制的函数,推测该是检测power键是否被按下的,字段longPressDuration = 500;该是设定需要长按power的时间,函数long_press_control();会开启一个线程进行检测,当长按power达到指定时间,就会退出关机充电的线程然后设定bootingUp = true;估计会通过设定的bootingUp值来进行手机的重启操作,下面是key_control函数的具体实现。

void key_control(int * pwrkeys, int pwrkeys_num)
{
    int res,i,j,k;
    int pollres;
    const char *device = NULL;
    struct input_event event;
    longPressDuration = 500;
    long_press_control();
    num_fds = 1;
    ufds = (struct pollfd*)calloc(1, sizeof(ufds[0]));
    ufds[0].fd = inotify_init();
    ufds[0].events = POLLIN;
    wd = inotify_add_watch(ufds[0].fd, INPUT_DEVICE_PATH, IN_DELETE | IN_CREATE);
    if(wd < 0) {
        KPOC_LOGI("could not add watch for %s, %s\n", INPUT_DEVICE_PATH, strerror(errno));
        exit_charger(EXIT_ERROR_SHUTDOWN);
    }
    res = scan_dir(INPUT_DEVICE_PATH);
    if(res < 0) {   
        KPOC_LOGI("scan dir failed for %s\n", INPUT_DEVICE_PATH);
        exit_charger(EXIT_ERROR_SHUTDOWN);
    }
    while(1) {
        pollres = poll(ufds, num_fds, -1);
        if(ufds[0].revents & POLLIN) {
            read_notify(INPUT_DEVICE_PATH, ufds[0].fd);
        }
        for(i = 1; i < num_fds; i++) {
            if(ufds[i].revents) {
                if(ufds[i].revents & POLLIN) {
                    res = read(ufds[i].fd, &event, sizeof(event));
                    if(res < (int)sizeof(event)) {
                        KPOC_LOGI("could not get event\n");
                        exit_charger(EXIT_ERROR_SHUTDOWN);
                    }
#ifdef VERBOSE_OUTPUT
                    KPOC_LOGI("%s: event.type:%d,%d:%d\n", __FUNCTION__, event.type, event.code, event.value);
#endif
                    if (EV_KEY == event.type) {
                        for (k=0; k<pwrkeys_num; k++) {
                            if(event.code == pwrkeys[k]) {
                                if (1 == event.value) {
                                    powerKeyPressed = true;
                                    pthread_cond_signal(&keycond);
                                } else
                                    powerKeyPressed = false;
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}

然后看看函数long_press_control(),在该函数中先对开启线程所需各种资源进行初始化,然后开启线程,线程开启后并不表示就进行重启操作了,它还要等待其他条件也满足才会重启,long_press_control函数的具体实现如下所示。

void long_press_control()
{
    int ret = 0;
    pthread_attr_t attr;
    pthread_t pwrkey_thread;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&keycond, NULL);
    pthread_attr_init(&attr);
   ret = pthread_create(&pwrkey_thread, &attr, key_thread_routine, NULL);
    if (ret != 0)
    {
       KPOC_LOGI("create key pthread failed.\n");
       exit_charger(EXIT_ERROR_SHUTDOWN);
    }
}

线程中的具体操作如下,先要判断powerKey是否被按下,而对powerKey是否被按下操作的检测是在函数key_control中实现的,当powerKey被按下则powerKeyPressed = true,在线程的实现函数中可以看到有2个while循环,最外层的while应该是循环等待powerKey被按下,达到要求会进入内层while进行计时等待的循环。

static void* key_thread_routine(void *arg)
{
    static bool bootingUp = false;
    static bool long_press = false;
    struct timeval start;
    while(1)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&keycond, &mutex);
        /* longPressDetecting = true; */
        gettimeofday(&start, NULL);
        KPOC_LOGI("pwr key long press check start\n");
        while(powerKeyPressed)
        {
            usleep(1000*100); //200ms
            long_press = time_exceed(start, longPressDuration);
            if(long_press)
            {
                KPOC_LOGI("pwr key reaches boot condition\n");
                if(get_voltage() > VBAT_POWER_ON)
                {
                    showLowBattLogo = 0;
                    // ready to boot up.
                    inotify_rm_watch(ufds[0].fd, wd);
                    if (keyDownPressed || keyUpPressed)
                        exit_charger(EXIT_REBOOT_UBOOT);
                    else
                        exit_charger(EXIT_POWER_UP);
                    bootingUp = true;
                }
                else {
                    showLowBattLogo = 1;
                    KPOC_LOGI("VBAT <= %d\n", VBAT_POWER_ON);
                    break;
                }
            }
        }
        if (!long_press)
            showLowBattLogo = 0;
        if(!bootingUp)
            start_charging_anim(TRIGGER_ANIM_KEY);
        KPOC_LOGI("pwr key long press check end\n");
        pthread_mutex_unlock(&mutex);
    }
    return NULL;
}

8. solution

进过上面的分析(当然很多都是猜测,分析也比较水),就开始动手改代码,希望能够实现期望的效果。

8.1 图片替换

总共修改/增加共16张图片:

  • 数字图片0~9 需要替换成新风格的数字图片 共10张,直接替换
  • 百分号图片,需要替换成新风格的百分号图片 共1张,直接替换
  • 充电背景图片 需要替换成新风格的图片 共1张,直接替换
  • 低电量图片 需要替换成新风格的图片 共1张,直接替换
  • 低电压、高电压图片 需要替换成新风格的图片 共2张,直接替换
  • 无电量图片(这是新加的,要求电量小于等于0时显示) 1张,添加
    被替换图片的位置一定要找对,不然就坑壁了,到时候效果出不来

8.2 修改vendor/mediatek/proprietary/external/charger/bootlogo.cpp文件

因为在显示充电动画之前,还需要先显示一张低电量图片,时长1秒钟。本来在插入充电器后,在启动lk/kernel会显示一个logo,本来想直接该这个logo的。但是发现从这个logo显示到开始播放充电动画之间间隔5~6秒钟,这当然不行,所以在开始播放充电动画前面加了个显示低电量图片时长1秒钟的逻辑,也不知道这样搞会不会被叼。先不管,改了看看效果再说,具体修改如下。

/*
 * Show charging animation with battery capacity
 *
 */
void bootlogo_show_charging(int capacity, int cnt)
{
    KPOC_LOGI("[ChargingAnimation: %s %d]%d, %d",__FUNCTION__,__LINE__, capacity, cnt);

    if (get_battnotify_status())
    {
        KPOC_LOGI("[ChargingAnimation: %s %d] show_charger_error_logo, get_battnotify_status()= %d \n",__FUNCTION__,__LINE__, get_battnotify_status());
        show_charger_ov_logo();
        return;
    }
    if (showLowBattLogo)
    {
        KPOC_LOGI("[ChargingAnimation: %s %d] show_low_battery , showLowBattLogo = %d \n",__FUNCTION__,__LINE__,showLowBattLogo);
        show_low_battery();
        return;
    }
/*change*/
//字段show_low_battery_one_time在前面还需要定义下,不然编译报错
    if(show_low_battery_one_time)
    {    //delay 1000ms
    show_low_battery();
    usleep(1000*1000);
    show_low_battery_one_time=0;
    }
/*change*/
    show_battery_capacity(capacity);
}

8.3 修改vendor/jrdcom/build/common/update_logo.sh文件

这个文件就是对图片进行打包成raw文件的脚本,因为前面添加一张无电量图片,所以也要在这个文件中添加,不然只在logo目录中添加是没用的,修改如下。

${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp29.raw ${logo_path}/$p/"${boot_logo}_bat_10_05".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp30.raw ${logo_path}/$p/"${boot_logo}_bat_10_06".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp31.raw ${logo_path}/$p/"${boot_logo}_bat_10_07".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp32.raw ${logo_path}/$p/"${boot_logo}_bat_10_08".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp33.raw ${logo_path}/$p/"${boot_logo}_bat_10_09".bmp
#change 直接在原来低电量图片上改的,反正低电量效果不需要了
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp34.raw ${logo_path}/$p/"${boot_logo}_no_power".bmp
#change
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp35.raw ${logo_path}/$p/"${boot_logo}_bat_bg".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp36.raw ${logo_path}/$p/"${boot_logo}_bat_img".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp37.raw ${logo_path}/$p/"${boot_logo}_bat_100".bmp
${product_path}/vendor/jrdcom/build/jrdtools/bmp_to_raw ${output_path}/temp38.raw ${logo_path}/$p/"${boot_logo}_kernel".bmp

8.4 修改vendor/mediatek/proprietary/external/libshowlogo/cust_display.h文件

修改这个文件主要是调整新充电样式中电池电量、电量值、百分号的位置(坑爹的UE不给具体坐标,就说按设计文档上搞,卧槽真是被狗咬了一口的感觉,文档上根本不是写的很清楚,坐标也是用GD表示的,没办法只能慢慢调了),电池电量的颜色还有就是添加无电量图片的宏定义,具体修改如下。

/*change*/
// color                       #00FF00  #ffffff
#define BAR_OCCUPIED_COLOR  (0xFF7ED321)    // Green
#define BAR_EMPTY_COLOR     (0xFF000000)    // Black
#define BAR_BG_COLOR        (0xFF000000)    // Black
#define SHOW_NO_POWER   34   //这个是无电量图片对应的序号
// first number rectangle
    #define NUMBER_LEFT                  (512) // number
    #define NUMBER_TOP                   (1097)
    #define NUMBER_RIGHT                 (540)
    #define NUMBER_BOTTOM                (1137)

    // %  rectangle
    #define PERCENT_LEFT                 (568) // percent number_left + 2*number_width
    #define PERCENT_TOP                  (1110)
    #define PERCENT_RIGHT                (592)
    #define PERCENT_BOTTOM               (1137)

    // top animation part
    #define TOP_ANIMATION_LEFT           (387) // top animation
    #define TOP_ANIMATION_TOP            (100)
    #define TOP_ANIMATION_RIGHT          (691)
    #define TOP_ANIMATION_BOTTOM         (152)

    // for old animation
    #define BAR_LEFT            (465)
    #define BAR_TOP             (773)
    #define BAR_RIGHT           (616)
    #define BAR_BOTTOM          (1048)
/*change*/

8.5 修改vendor/mediatek/proprietary/external/libshowlogo/show_animation_common.h文件

修改这个文件主要是调整电池电量的格数,原来是4个格,而且每个格之间有间隙,需要弄成5个格,每个格之间无间隙,只需要修改2个宏,具体修改如下。

/*change*/
// version_0 animation
#define ANIM_V0_REGIONS  5
#define ANIM_V0_SPACE_AFTER_REGION  0
/*change*/

8.6 修改vendor/mediatek/proprietary/external/libshowlogo/show_animation_common.c文件

这个文件主要修改函数void fill_animation_battery_old,因为这个函数的功能就是实现4格滚动效果,所以直接在它上面改。

  • 原本没有显示电池电量值,所以需要添加
  • 需要设置90%后无动画
  • 需要设置电量小于等于0显示SHOW_NO_POWER图片

具体的修改如下所示。

/*
 * Show old charging animation
 *
 */
void fill_animation_battery_old(int capacity,  void *fill_addr, void * dec_logo_addr, void * logo_addr,
                       LCM_SCREEN_T phical_screen)
{
    int capacity_grids = 0;
    if (capacity > 100) capacity = 100;
    capacity_grids = (capacity * (ANIM_V0_REGIONS)) / 100;
/*change*/
    if (version0_charging_index < capacity_grids * 2)
        version0_charging_index = capacity_grids * 2;
    if (capacity < 100){
    if(capacity < 90){
        version0_charging_index > 9? version0_charging_index = capacity_grids * 2 : version0_charging_index++;
    }else{
    version0_charging_index > 7? version0_charging_index = capacity_grids * 2 : version0_charging_index++;
    }
    } else {
        version0_charging_index = ANIM_V0_REGIONS * 2;
    }
    if(capacity>0)
    {
    fill_animation_logo(ANIM_V0_BACKGROUND_INDEX, fill_addr, dec_logo_addr, logo_addr,phical_screen);
    if (capacity < 10) {
    fill_animation_number(NUMBER_PIC_START_0 + capacity, 1, fill_addr, logo_addr, phical_screen);
        fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
    }else{
        if(capacity >=100){
        fill_animation_number(NUMBER_PIC_START_0 + ((capacity%100)/10), 0, fill_addr, logo_addr, phical_screen);
        fill_animation_number(NUMBER_PIC_START_0 + (capacity%10), 1, fill_addr, logo_addr, phical_screen);
        fill_animation_number(NUMBER_PIC_START_0 + (capacity/100), -1, fill_addr, logo_addr, phical_screen);
        fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
        }else{
        fill_animation_number(NUMBER_PIC_START_0 + (capacity/10), 0, fill_addr, logo_addr, phical_screen);
        fill_animation_number(NUMBER_PIC_START_0 + (capacity%10), 1, fill_addr, logo_addr, phical_screen);
        fill_animation_dynamic(NUMBER_PIC_PERCENT, percent_location_rect, fill_addr, percent_pic_addr, logo_addr, phical_screen);
            }
    }
    RECT_REGION_T rect_bar = {bar_rect.left + 1, bar_rect.top + 1, bar_rect.right, bar_rect.bottom};
    fill_animation_prog_bar(rect_bar,
                       (unsigned int)(BAR_OCCUPIED_COLOR),
                       0,  version0_charging_index/2,
                       fill_addr, phical_screen);
    fill_animation_prog_bar(rect_bar,
                      (unsigned int)(BAR_EMPTY_COLOR),
                      version0_charging_index/2, ANIM_V0_REGIONS - version0_charging_index/2,
                      fill_addr, phical_screen);
    }else{
    fill_animation_logo(SHOW_NO_POWER, fill_addr, dec_logo_addr, logo_addr,phical_screen);
    }
}
/*change*/

改完后编译发现是可以达到需求上面的要求(当然不是一下就编成功了的,都编了n次,而且每次编译都要费很长的时间,看来做事要细心,不然错了或者漏改了都会报错),这样也算马马虎虎完成任务吧,最后就看设计的人是否觉得达到她期望效果,不然估计还要坑壁的继续改。

9. 总结

最开始做这个还真的不知道怎么入手,然后是各种找资料,当然网上也会有一些参考的例子,不过看了后也还是不是非常懂,主要是不理解关机充电在什么阶段开始的。并且对lk、kernel这些流程不是非常熟悉,只能慢慢尝试在图片上做标记,改不同的坐标来看编译出来是什么效果。最开始找到的就是fill_animation_battery_new这个函数,只知道是它实现的充电动画效果,还想就在这个函数上面改成5格滚动的动画效果。后面看到关机充电动画实现四格滚动动画问题分析才知道原来原版系统本来就有这个滚动的动画效果实现(要是对相关代码有个全局观估计就不会这么狼狈),只要在它上修改其实就方便多了。同时,只要知道关机充电这个功能就是一个应用而已,它是由第一个进程init开启的,然后从这应用的入口函数vendor/mediatek/proprietary/external/charger/main.cpp中的main开始追,整个充电逻辑就会比较清楚了。