android 9.0關機充電流程,充電圖標和電量顯示百分比修改

android 9.0關機充電流程,充電圖標和電量顯示百分比修改

版權聲明:本文爲博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明

原文鏈接:https://blog.csdn.net/yy782101688/article/details/102852180

android 9.0關機充電圖標和字體修改

 

相關源文件

 system/core/healthd/healthd_draw.cpp
 system/core/healthd/images/battery_fail.png
 system/core/healthd/images/battery_scale.png
 /system/core/healthd/ charger.cpp
 hardware/interfaces/health/2.0/default/healthd_common.cpp
 system/core/healthd/healthd_mode_charger.cpp
 bootable/recovery/minui/graphics.cpp
 bootable/recovery/minui/font_10x18.h

電量顯示百分比字體替換

android默認文字和充電圖標分開。
android 9.0的充電圖標和電量百分比顯示主要在函數 healthd_mode_charger_heartbeat中實現。

void healthd_mode_charger_heartbeat() {
    charger* charger = &charger_state;
    int64_t now = curr_time_ms();

    handle_input_state(charger, now);
    handle_power_supply_state(charger, now);

    /* do screen update last in case any of the above want to start
     * screen transitions (animations, etc)
     */
    update_screen_state(charger, now);
}
static void update_screen_state(charger* charger, int64_t now) {
    animation* batt_anim = charger->batt_anim;
    int disp_time;

    if (!batt_anim->run || now < charger->next_screen_transition) return;

    if (healthd_draw == nullptr) {
        if (healthd_config && healthd_config->screen_on) {
            if (!healthd_config->screen_on(batt_prop)) {
                LOGV("[%" PRId64 "] leave screen off\n", now);
                batt_anim->run = false;
                charger->next_screen_transition = -1;
                if (charger->charger_connected) request_suspend(true);
                return;
            }
        }

        healthd_draw.reset(new HealthdDraw(batt_anim));

#ifndef CHARGER_DISABLE_INIT_BLANK
        set_backlight(false);
        healthd_draw->blank_screen(true);
#endif
    }

    /*如果當前顯示的循環次數已經爲設置的最大cycle,就做滅屏處理,等待下次按下*/
    /*power鍵或者拔掉usb*/
    /* animation is over, blank screen and leave */
    if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
        reset_animation(batt_anim);
        charger->next_screen_transition = -1;
        set_backlight(false);
        healthd_draw->blank_screen(true);
        LOGV("[%" PRId64 "] animation done\n", now);
        if (charger->charger_connected) request_suspend(true);
        return;
    }
    /*設置一幀,即一個png圖片的顯示時間*/
    disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time;

    /*充電休眠過程中按power鍵或者插第一次剛開始充電時候亮屏*/
    /* unblank the screen on first cycle and first frame */
    if (batt_anim->cur_cycle == 0 && batt_anim->cur_frame == 0) {
        healthd_draw->blank_screen(false);
        set_backlight(true);
    }
    /*當前幀如果是第一幀(按下power鍵喚醒或者第一次剛開始充電)*/
    /*根據實際電量,確認起始幀(即從第幾張png圖片開始顯示),起始*/
    /*幀顯示時間加first_frame_repeats*/
    /* animation starting, set up the animation */
    if (batt_anim->cur_frame == 0) {
        LOGV("[%" PRId64 "] animation starting\n", now);
        if (batt_prop) {
            batt_anim->cur_level = batt_prop->batteryLevel;
            batt_anim->cur_status = batt_prop->batteryStatus;
            if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
                /* find first frame given current battery level */
                for (int i = 0; i < batt_anim->num_frames; i++) {
                    if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
                        batt_anim->cur_level <= batt_anim->frames[i].max_level) {
                        batt_anim->cur_frame = i;
                        break;
                    }
                }

                // repeat the first frame first_frame_repeats times
                disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
                            batt_anim->first_frame_repeats;
            }
        }
    }
    /*設定起始幀之後,開始調用redraw_screen顯示充電畫面,包括文字*/
    /* draw the new frame (@ cur_frame) */
    healthd_draw->redraw_screen(charger->batt_anim, charger->surf_unknown);

    /* if we don't have anim frames, we only have one image, so just bump
     * the cycle counter and exit
     */
    if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
        LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
        charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
        batt_anim->cur_cycle++;
        return;
    }
    /*顯示一幀需要等待的耗時*/
    /* schedule next screen transition */
    charger->next_screen_transition = now + disp_time;

    /* advance frame cntr to the next valid frame only if we are charging
     * if necessary, advance cycle cntr, and reset frame cntr
     */
    if (charger->charger_connected) {
        batt_anim->cur_frame++;

        while (batt_anim->cur_frame < batt_anim->num_frames &&
               (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
                batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
            batt_anim->cur_frame++;
        }
        /*第N輪顯示結束,進入第N+1輪顯示,直到cycle爲設置的最大的值,進入滅屏*/
        if (batt_anim->cur_frame >= batt_anim->num_frames) {
            batt_anim->cur_cycle++;
            batt_anim->cur_frame = 0;

            /* don't reset the cycle counter, since we use that as a signal
             * in a test above to check if animation is over
             */
        }
    } else {
        /* Stop animating if we're not charging.
         * If we stop it immediately instead of going through this loop, then
         * the animation would stop somewhere in the middle.
         */
        batt_anim->cur_frame = 0;
        batt_anim->cur_cycle++;
    }
}
void HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {
  clear_screen();

  /* try to display *something* */
  if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
    draw_unknown(surf_unknown);
  else
    draw_battery(batt_anim);
  gr_flip();
}

顯示充電圖標,電量百分比和時間。

void HealthdDraw::draw_battery(const animation* anim) {
  const animation::frame& frame = anim->frames[anim->cur_frame];

  if (anim->num_frames != 0) {
    draw_surface_centered(frame.surface);
    LOGV("drawing frame #%d min_cap=%d time=%d\n", anim->cur_frame,
         frame.min_level, frame.disp_time);
  }
  draw_clock(anim);
  draw_percent(anim);
}

顯示充電圖標的實際爲draw_surface_centereddraw_percent爲顯示文字點亮百分比,默認代碼未初始化field.font,即字體font。使用minui默認字體進行顯示。

void HealthdDraw::draw_percent(const animation* anim) {
  int cur_level = anim->cur_level;
  if (anim->cur_status == BATTERY_STATUS_FULL) {
    cur_level = 100;
  }

  if (cur_level < 0) return;

  const animation::text_field& field = anim->text_percent;
  if (field.font == nullptr || field.font->char_width == 0 ||
      field.font->char_height == 0) {
    return;
  }

  std::string str = base::StringPrintf("%d%%", cur_level);

  int x, y;
  determine_xy(field, str.size(), &x, &y);

  LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
  /*設置顏色,根據field.font指定的字體顯示百分比,x,y爲顯示的座標,顯示大小*/
  由font指定*/
  gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
  draw_text(field.font, x, y, str.c_str());
}

由於默認代碼field.font爲空,不會顯示電量百分比,添加以下代碼,用字體gr_font顯示百分比。gr_font的初始化在gr_init_font中。

/* bootable/recovery/minui/graphics.cpp*/

const GRFont* gr_sys_font() {
  return gr_font;
}

gr_init_font() 初始化gr_font主要是兩個邏輯。
邏輯一:調用 int res = gr_init_font(“font”, &gr_font);最終會根據name “font”,找到res/images/font.png,依據font.png初始化gr_font,然後return,不會進入邏輯二。font.png的位置在out目錄recovery/root/res/images/font.pngrecovery模式下,就是走的這個流程。

/*build/make/core/Makefile默認使用bootable/recovery/fonts下的18x32.png初始化
 *recovery模式下字體。*/

ifneq (,$(filter xxxhdpi 560dpi xxhdpi 400dpi xhdpi,$(recovery_density)))
recovery_font := $(call include-path-for, recovery)/fonts/18x32.png
else
recovery_font := $(call include-path-for, recovery)/fonts/12x22.png
endif

邏輯二:如果res/images/font.png不存在,就會根據"font_10x18.h"這個頭文件定義的font初始化,這個默認字體文件在源碼路徑bootable/recovery/minui/font_10x18.h。關機充電電量百分比顯示的字體格式根據此頭文件初始化,替換驗證。

/*bootable/recovery/minui/font_10x18.h*/

struct {
  unsigned width;
  unsigned height;
  unsigned char_width;
  unsigned char_height;
  unsigned char rundata[2973];
} font = {
  .width = 960,
  .height = 18,
  .char_width = 10,
  .char_height = 18,
  .rundata = {
      ......
      }
  •  
static void update_screen_state(charger* charger, int64_t now) {
    animation* batt_anim = charger->batt_anim;
    int disp_time;

    if (!batt_anim->run || now < charger->next_screen_transition) return;

    if (healthd_draw == nullptr) {
        if (healthd_config && healthd_config->screen_on) {
            if (!healthd_config->screen_on(batt_prop)) {
                LOGV("[%" PRId64 "] leave screen off\n", now);
                batt_anim->run = false;
                charger->next_screen_transition = -1;
                if (charger->charger_connected) request_suspend(true);
                return;
            }
        }

        healthd_draw.reset(new HealthdDraw(batt_anim));

#ifndef CHARGER_DISABLE_INIT_BLANK
        set_backlight(false);
        healthd_draw->blank_screen(true);
#endif
    }
	......
}

HealthdDraw::HealthdDraw(animation* anim)
  : kSplitScreen(HEALTHD_DRAW_SPLIT_SCREEN),
    kSplitOffset(HEALTHD_DRAW_SPLIT_OFFSET) {
  gr_init();
  ......
}

int gr_init() {
  gr_init_font();
  ......
}

static void gr_init_font(void) {
  /*邏輯一*/
  int res = gr_init_font("font", &gr_font);
  if (res == 0) {
    return;
  }

  /*邏輯二*/
  printf("failed to read font: res=%d\n", res);

  // fall back to the compiled-in font.
  gr_font = static_cast<GRFont*>(calloc(1, sizeof(*gr_font)));
  gr_font->texture = static_cast<GRSurface*>(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 = static_cast<unsigned char*>(malloc(font.width * font.height));
  gr_font->texture->data = 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->char_width = font.char_width;
  gr_font->char_height = font.char_height;
}

/*邏輯二recovery 模式下字體初始化,最終找到res/images/font.png*/
int gr_init_font(const char* name, GRFont** dest) {
  ......
  int res = res_create_alpha_surface(name, &(font->texture));
  if (res < 0) {
    free(font);
    return res;
  }
  ......
}

PngHandler::PngHandler(const std::string& name) : error_code_(0), png_fp_(nullptr, fclose) {
  std::string res_path = android::base::StringPrintf("/res/images/%s.png", name.c_str());
  png_fp_.reset(fopen(res_path.c_str(), "rbe"));
  if (!png_fp_) {
    error_code_ = -1;
    return;
  }
  ......
}

充電圖標替換

不同於android6.0android7.0及以上版本默認關機充電圖標已經變更一張battery_scale.png。修改默認關機充電圖標,實際上要替換battery_scale.png

  • battery_scale.png文件製作

利用系統源碼中的工具bootable/recovery/interlace-frames.py,即能將battery_scale.png拆分成幾張png圖片,也能將若干張png合成一張battery_scale.png

  • 拆分指令
python interlace-frames.py -d battery_scale.png -o battery.png
  •  

結果生成battery00.png~battery05.png共6個PNG文件,對應c源碼配置:

static animation::frame default_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 = 80,
        .max_level = 95,
        .surface = NULL,
    },
    {
        .disp_time = 750,
        .min_level = 0,
        .max_level = 100,
        .surface = NULL,
    },
};

在這裏插入圖片描述
將生成的battery00.png~battery05.png替換成定製的充電圖片,並使用合併指令,重新生成新的battery_scale.png

  • 合併指令
python interlace-frames.py -o battery_scale.png oem/battery00.png oem/battery01.png oem/battery02.png oem/battery03.png oem/battery04.png oem/battery05.png
  • 驗證方法
adb root
adb remount
setenforce 0
adb push battery_scale.png res/images/charger
adb reboot
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章