android系统恢复出厂设置和升级界面的修改方法

       最近需要定制恢复出厂设置和升级的画面,将原生的绿色机器人改成其他的图片。
基于的android版本是4.4.4,改起来挺简单的,但是遇到了几个坑,特地记录下。
android 4.4.4的升级机器人图片是由两部分构成的:静态部分和动态部分。


icon_installing.png

android系统恢复出厂设置和升级界面的修改方法_第1张图片

icon_installing_overlay01.png


    静态部分提供了一个大的背景图icon_install.png,动态部分是在背景图上进行覆盖的7张小图片installationOverlay01-07.png,实现机器人肚子上旋转的动画。
整体这部分的代码路径是在platform/bootable/recovery下,主要涉及的类是screen_ui.cpp
修改的思路是替换installationOverlay01-07.png图片为自己需要的图片,然后修改installationOverlay01-07.png显示的坐标位置,同时调整一下下方progressbar的高度和文字的高度,实现整体协调的效果。

修改installationOverlay01-07.png的具体位置:
void ScreenRecoveryUI::draw_install_overlay_locked(int frame) {
    if (installationOverlay == NULL || overlay_offset_x < 0) return;
    gr_surface surface = installationOverlay[frame];
    int iconWidth = gr_get_width(surface);
    int iconHeight = gr_get_height(surface);
    // 改变iconX和iconY的位置,修改overlay图片的描画坐标,数值自己定
    int iconX = (gr_fb_width()-127) / 2;
    int iconY = (gr_fb_height()-192) / 2;
    gr_blit(surface, 0, 0, iconWidth, iconHeight,
            iconX, iconY);
}



修改progressbar的具体高度:
void ScreenRecoveryUI::draw_progress_locked()
{
    if (currentIcon == ERROR) return;

    if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
        draw_install_overlay_locked(installingFrame);
    }

    if (progressBarType != EMPTY) {
        int iconHeight = gr_get_height(backgroundIcon[INSTALLING_UPDATE]);
        int width = gr_get_width(progressBarEmpty);
        int height = gr_get_height(progressBarEmpty);

        //修改dx dy的数值,改变progressbar的位置显示
        int dx = (gr_fb_width() - width)/2;
        int dy = (3*gr_fb_height() + iconHeight - 2*height)/4;

        // Erase behind the progress bar (in case this was a progress-only update)
        gr_color(0, 0, 0, 255);
        gr_fill(dx, dy, width, height);

        if (progressBarType == DETERMINATE) {
            float p = progressScopeStart + progress * progressScopeSize;
            int pos = (int) (p * width);

            if (rtl_locale) {
                // Fill the progress bar from right to left.
                if (pos > 0) {
                    gr_blit(progressBarFill, width-pos, 0, pos, height, dx+width-pos, dy);
                }
                if (pos < width-1) {
                    gr_blit(progressBarEmpty, 0, 0, width-pos, height, dx, dy);
                }
            } else {
                // Fill the progress bar from left to right.
                if (pos > 0) {
                    gr_blit(progressBarFill, 0, 0, pos, height, dx, dy);
                }
                if (pos < width-1) {
                    gr_blit(progressBarEmpty, pos, 0, width-pos, height, dx+pos, dy);
                }
            }
        }

        if (progressBarType == INDETERMINATE) {
            static int frame = 0;
            gr_blit(progressBarIndeterminate[frame], 0, 0, width, height, dx, dy);
            // in RTL locales, we run the animation backwards, which
            // makes the spinner spin the other way.
            if (rtl_locale) {
                frame = (frame + indeterminate_frames - 1) % indeterminate_frames;
            } else {
                frame = (frame + 1) % indeterminate_frames;
            }
        }
    }
}



修改升级和恢复出厂界面下文字的位置:
void ScreenRecoveryUI::draw_background_locked(Icon icon)
{
    pagesIdentical = false;
    gr_color(0, 0, 0, 255);
    gr_fill(0, 0, gr_fb_width(), gr_fb_height());

    if (icon) {
        gr_surface surface = backgroundIcon[icon];
        gr_surface text_surface = backgroundText[icon];

        int iconWidth = gr_get_width(surface);
        int iconHeight = gr_get_height(surface);
        int textWidth = gr_get_width(text_surface);
        int textHeight = gr_get_height(text_surface);

        int iconX = (gr_fb_width() - iconWidth) / 2;
        int iconY = (gr_fb_height() - (iconHeight+textHeight+40)) / 2;

        //这里改变文字的位置坐标,会同时影响恢复出厂设置和升级界面显示下的文字位置
        int textX = (gr_fb_width() - textWidth) / 2;
        int textY = ((gr_fb_height() - (iconHeight+textHeight+40)) / 2) + iconHeight + 40;

        gr_blit(surface, 0, 0, iconWidth, iconHeight, iconX, iconY);
        if (icon == INSTALLING_UPDATE || icon == ERASING) {
           draw_install_overlay_locked(installingFrame);
        }

        gr_color(255, 255, 255, 255);
        gr_texticon(textX, textY, text_surface);
    }
}



动画播放的频率默认是1秒钟20帧。如果修改可以在这里改:
void ScreenRecoveryUI::progress_loop() {
    double interval = 1.0 / animation_fps;
    ......
    }
}



    遇到的坑主要有两个。
    第一个就是在替换图片的时候,最开始替换的图片的位深度跟原始图片的位深度不一致,导致图片进去后花屏,后来查清楚了原来是图片位深度引起的,所以在替换图片的时候如果图片被不正常的拉伸了,可以看下是否是位深度不一样导致的。
    第二个就是遇到了一个奇怪的现象,每次烧写之后如果直接升级的话,升级的界面显示总是跟自己改的不一样,但是如果烧写后恢复出厂的话,然后再次升级,则界面显示是完全ok的,这个问题也查了很久最终定位在recovery模式下的语言locale上,因为升级调用启动程序是自己写的,在往/cache/recovery/command写入升级命令的时候只写了--update_package命令,并没有给指定--locale=en_US或是--locale=zh_CN,所以recovery模式并未识别到语言环境,导致影响了screen_ui.cpp中关于textHeight的计算,而textHeight的数值又影响了图片以及文字的高度设置,所以看起来不会按照自己设置的方式来改变。
    升级时的语言识别的逻辑是这样的:
    首先进入recovery模式后,在执行Recovery.cpp的main函数时,会对/cache/recovery/command写入的命令进行逐行解析,当解析到locale的时候,就会将当前升级操作的locale设置为读到的内容,当没有在command中读到locale的时候,系统会走load_locale_from_cache()函数,来获取上一次我们是否在/cache/recovery/last_locale中写入了之前的语言环境,如果还是没有的话,locale就真的找不到了,text的高度是根据locale来获取的,这时候就会影响text的高度的值,从而导致升级界面出现问题。
    那为什么先恢复出厂设置后再升级就没问题了呢?原因是恢复出厂设置的最后一步会在/cache/recovery/last_locale中写入语言环境,这样在下次升级的时候就可以找到一个语言的依据,也就不会导致text高度出现问题。

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

    ......
    int arg;
    while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
        switch (arg) {
        case 'p': previous_runs = atoi(optarg); break;
        case 's': send_intent = optarg; break;
        case 'u': update_package = optarg; break;
        case 'w': wipe_data = wipe_cache = 1; break;
        case 'c': wipe_cache = 1; break;
        case 't': show_text = 1; break;
        case 'x': just_exit = true; break;
        case 'l': locale = optarg; break;
        case 'g': {
            if (stage == NULL || *stage == '\0') {
                char buffer[20] = "1/";
                strncat(buffer, optarg, sizeof(buffer)-3);
                stage = strdup(buffer);
            }
            break;
        }
        case '?':
            LOGE("Invalid command argument\n");
            continue;
        }
    }

    if (locale == NULL) {
        load_locale_from_cache();
    }
    ......
}



static void
load_locale_from_cache() {
    // LOCALE_FILE就是/cache/recovery/last_locale
    FILE* fp = fopen_path(LOCALE_FILE, "r");
    char buffer[80];
    if (fp != NULL) {
        fgets(buffer, sizeof(buffer), fp);
        int j = 0;
        unsigned int i;
        for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
            if (!isspace(buffer[i])) {
                buffer[j++] = buffer[i];
            }
        }
        buffer[j] = 0;
        locale = strdup(buffer);
        check_and_fclose(fp, LOCALE_FILE);
    }
}


    刚开始接触底层,有些可能说的不对,希望自己对底层一点点的熟悉起来。对于这部分的代码可以通过LOGI来进行log输出,然后在恢复出厂后在/cache/recovery/last_log中查看到自己打印的log信息。

另外看了下android 7.1的这部分的代码,发现貌似已经可以利用max_stage是否为-1来控制不显示图片,只显示文字了。

    最后附上两个博客,对了解recovery模式和recovery模式的界面描画很有帮助。
    http://blog.csdn.net/u010223349/article/details/40392789
    http://blog.csdn.net/fengying765/article/details/38301895







你可能感兴趣的:(android系统恢复出厂设置和升级界面的修改方法)