关机充电是用minui开发的,代码路径如下
vendor/sprd/proprietories-source/charge

代码中的图片资源路径
vendor/sprd/proprietories-source/charge/images

手机上的图片资源路径
vendor/etc/res/images

项目的屏幕分辨率是540X1200,根据 vendor/sprd/proprietories-source/charge/ui.c代码中的判断,是 SIZE_720X1280

static int res_pixel_identify(void) {
	int x = gr_fb_width();
	int y = gr_fb_height();
	int size = 0;
	if((x<= 360) && (y <= 640)){
		size = SIZE_360X640;
	}else if((x<=480) && (y<=800)){
		size = SIZE_480X800;
	}else if((x<=480) && (y<=854)) {
		size = SIZE_480X800;
		LOGD("480 * 854 Use res of 480 * 800\n");
	}else if((x<=720) && (y<=1280)){
		size = SIZE_720X1280;
	}else if((x<=1080) && (y<=1920)){
		size = SIZE_1080X1920;
	}else if((x<=1440) && (y<=2560)){
		size = SIZE_1440X2560;
	}else{
		LOGE("Picture size is not standard size\n");
	}
	return size;
}

从如下代码中得知,对应电池电量的图片是根据 PROGRESSBAR_INDETERMINATE_STATES 这个值来进行设置的,默认为0%-100%每16.6%为一档7个状态,但要求是20%为一档,我们只需要修改 0-5 档的6个状态的图片资源,并把 PROGRESSBAR_INDETERMINATE_STATES 改为 6 即可

#define PROGRESSBAR_INDETERMINATE_STATES 7
frame = level * (PROGRESSBAR_INDETERMINATE_STATES - 1) / 100;

然后修改电池电量 level 的字体,默认情况下字体是显示在图片的外面的上面,客户要求显示在图片中间,而且默认是用图片显示的,默认代码如下:

#ifdef PICTURE_SHOW_PERCENT_SUPPORT
	draw_text_picture(level);
#else
	draw_text_xy((dy + height),  (gr_fb_width()/2 - 20),  bat);
#endif

首先去掉文件上面定义的宏 PICTURE_SHOW_PERCENT_SUPPORT,让它走 draw_text_xy 去画字体
然后修改 x,y 的坐标居中显示,修改后的代码如下:

#ifdef PICTURE_SHOW_PERCENT_SUPPORT
    draw_text_picture(level);
#else
    draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 20),  bat);
#endif

修改之后发现字体不见了,找了一下原因原来是先画字体然后画图片导致字体被覆盖了,只需要修改一下图片和字体的顺序即可,修改之后代码如下:

if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
        gr_blit(gProgressBarIndeterminate[frame],  0,  0,  width,  height,  dx,  dy);   //画电量的图片
        frame = (frame + 1);
        if (frame >= PROGRESSBAR_INDETERMINATE_STATES) {
            frame = level * (PROGRESSBAR_INDETERMINATE_STATES - 1) / 100;
        }
    }
#ifdef PICTURE_SHOW_PERCENT_SUPPORT
    draw_text_picture(level);
#else
    draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 20),  bat);
#endif

然后修改字体的颜色,修改之后的代码如下

gr_color(0xa4,0xc6,0x39,255);   //RGBA,0xa4c639是客户要求的颜色
draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 20),  bat);

然后测试之后发现这个字体的坐标是固定坐标,从左边开始画字体,对于0-9是居中的,但是10-100就不是居中的了,需要添加判断level,动态修改字体x的坐标,修改后的代码如下:

gr_color(0xa4,0xc6,0x39,255);
if (level < 10) {
    draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 20),  bat);
} else if (level < 100) {
    draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 30),  bat);
} else {
    draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 40),  bat);
}

然后发现客户对字体也有要求,他们也提供了96x2的图片资源,查看代码发现本身已经支持了这个功能,代码如下:
vendor/sprd/proprietories-source/charge/minui/graphics.c

static void gr_init_font(void) {
    gr_font = calloc(sizeof(*gr_font), 1);

    int res = res_create_alpha_surface("font", &(gr_font->texture));    //系统本身会先去找 font.png 这个资源文件,如果没有就用默认的
    if (res == 0) {
        // The font image should be a 96x2 array of character images.  The
        // columns are the printable ASCII characters 0x20 - 0x7f.  The
        // top row is regular text; the bottom row is bold.
        gr_font->cwidth = gr_font->texture->width / 96;
        gr_font->cheight = gr_font->texture->height / 2;
    } else {
        printf("failed to read font: res=%d\n", res);

        // fall back to the compiled-in font.
        gr_font->texture = malloc(sizeof(*gr_font->texture));
        gr_font->texture->width = font.width;
        gr_font->texture->height = font.height;
        gr_font->texture->row_bytes = font.width;
        gr_font->texture->pixel_bytes = 1;

        unsigned char* bits = malloc(font.width * font.height);
        gr_font->texture->data = (void*) bits;

        unsigned char data;
        unsigned char* in = font.rundata;
        while ((data = *in++)) {
            memset(bits, (data & 0x80) ? 255 : 0, data & 0x7f);
            bits += (data & 0x7f);
        }

        gr_font->cwidth = font.cwidth;
        gr_font->cheight = font.cheight;
    }
}

系统本身会先去找 font.png 这个资源文件,如果没有就用默认的,所以我们只需要把客户提供的图片改为 font.png 让后放入资源文件夹中即可,测试之后有效果

然后客户对充电动画也有要求,默认的充电动画在没充满的情况下是从0-5每一张图片都会播放,客户要求是 0%-19% 在0-1之间变化,修改后的代码如下

if (gProgressBarType == PROGRESSBAR_TYPE_INDETERMINATE) {
        gr_blit(gProgressBarIndeterminate[frame],  0,  0,  width,  height,  dx,  dy);
        if (frame > level * (PROGRESSBAR_INDETERMINATE_STATES - 1) / 100) {
            frame = level * (PROGRESSBAR_INDETERMINATE_STATES - 1) / 100;
        } else {
            frame = (frame + 1);
        }
        if (level >= 100) {
            frame = PROGRESSBAR_INDETERMINATE_STATES - 1;
        }
    }
#ifdef PICTURE_SHOW_PERCENT_SUPPORT
    draw_text_picture(level);
#else
    gr_color(0xa4, 0xc6, 0x39, 255);
    if (level < 10) {
        draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 20),  bat);
    } else if (level < 100) {
        draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 30),  bat);
    } else {
        draw_text_xy((dy + height / 2),  (gr_fb_width()/2 - 40),  bat);
    }
#endif
    gr_flip();      //从下面的方法介绍中得知这个方法的作用是“刷新显示内容”,调用这个方法之后就会重新调用 charge_thread() 这个方法

修改每次刷新的时间为750ms

void *charge_thread(void *cookie) {
    int bat_level = 0;
    for (; !is_exit; ) {
	    if(thread_ext_ctrl == CHARGE_THREAD_CTRL){
		    thread_count++;
		    if(thread_count > 5){
			    is_exit = 1;
			    thread_count = 0;
		    }
	    }
        usleep(1000000/ PROGRESSBAR_INDETERMINATE_FPS);
        bat_level = battery_capacity();
	    if(bat_level < 0){
		    thread_st = CHARGE_THREAD_EXIT_ERROR;
	    }else{
		    thread_st = CHARGE_THREAD_OK;
	    }
	    pthread_mutex_lock(&gchargeMutex);
	    led_control(bat_level);
	    status_index = charge_health_check();
	    if (screen_on_flag == 1) {
	       draw_progress_locked(bat_level);
	    }
	    pthread_mutex_unlock(&gchargeMutex);
	    //modified begin
        usleep(750000); //这里原来是500000微秒,现在改为750000微秒
        //modified end
    }
    usleep(200);
    return NULL;
}

调试方法

./prebuilts/build-tools/linux-x86/bin/ninja -f out/combined-sp7731e_1h10_native.ninja charge 2>&1 | tee charge.log;adb wait-for-device;adb root;adb remount;adb sync vendor;adb shell reboot -p;

如果只修改了图片,只需要把对应的图片 push 到 vendor/etc/res/images文件夹下关机查看关机充电动画即可

快速模拟测试查看从 0%-100% 的充电效果变化的方法

void *charge_thread(void *cookie) {
    int bat_level = 0;
    for (; !is_exit; ) {
	    if(thread_ext_ctrl == CHARGE_THREAD_CTRL){
		    thread_count++;
		    if(thread_count > 5){
			    is_exit = 1;
			    thread_count = 0;
		    }
	    }
        usleep(1000000/ PROGRESSBAR_INDETERMINATE_FPS);
        //modified begin
        //bat_level = battery_capacity();
        bat_level = bat_level > 100 ? 0 : bat_level++;
        //modified end
	    if(bat_level < 0){
		    thread_st = CHARGE_THREAD_EXIT_ERROR;
	    }else{
		    thread_st = CHARGE_THREAD_OK;
	    }
	    pthread_mutex_lock(&gchargeMutex);
	    led_control(bat_level);
	    status_index = charge_health_check();
	    if (screen_on_flag == 1) {
	       draw_progress_locked(bat_level);
	    }
	    pthread_mutex_unlock(&gchargeMutex);
        //Redmine46812 zhangqi modified for power off charge anim 2020/10/27:begin
        usleep(750000);
        //Redmine46812 zhangqi modified for power off charge anim 2020/10/27:end
    }

    usleep(200);
    return NULL;
}

minui简介
方法简介

gr_init()  /*初始化图形设备,分配Pixelflinger库渲染的内存*/
gr_font_size()  /*将字体对应的surface长宽赋值给char_width和char_height*/
int gr_init(void);             /* 初始化图形显示,主要是打开设备、分配内存、初始化一些参数 */  
void gr_exit(void);            /* 注销图形显示,关闭设备并释放内存 */  
int gr_fb_width(void);         /* 获取屏幕的宽度 */  
int gr_fb_height(void);        /* 获取屏幕的高度 */  
gr_pixel *gr_fb_data(void);    /* 获取显示数据缓存的地址 */  
void gr_flip(void);            /* 刷新显示内容 */  
void gr_fb_blank(bool blank);  /* 清屏 */  
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);  /* 设置字体颜色 */  
void gr_fill(int x, int y, int w, int h);  /* 填充矩形区域,参数分别代表起始坐标、矩形区域大小 */  
int gr_text(int x, int y, const char *s);  /* 显示字符串 */  
int gr_measure(const char *s);             /* 获取字符串在默认字库中占用的像素长度 */  
void gr_font_size(int *x, int *y);         /* 获取当前字库一个字符所占的长宽 */  
void gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);  /* 填充由source指定的图片 */  
unsigned int gr_get_width(gr_surface surface);   /* 获取图片宽度 */  
unsigned int gr_get_height(gr_surface surface);  /* 获取图片高度 */  
int res_create_surface(const char* name, gr_surface* pSurface);  /* 根据图片创建显示资源数据,name为图片在mk文件指定的相对路径 */
void res_free_surface(gr_surface surface);       /* 释放资源数据 */