zoukankan      html  css  js  c++  java
  • Android recovery UI实现分析

    Android recovery模式为何物?

    关于这个问题, baidu上已经有无数的答案。不理解的朋友先补习一下。

    从纯技术角度来讲, recovery和android本质上是两个独立的rootfs,  仅仅是recovery这个rootfs存在的意义就是为android这个rootfs服务,因此被解释为Android系统的一部分。 

     recovery作为一个简单的rootfs, 提供了很有限的几个功能,仅仅包括了几个简单的库,UI的显示採用的是直接刷framebuffer的形式,作为android framework及app层的码农,对这样的形式相对陌生,特抽点时间梳理了一番。
     
    首先,浏览一下reocvery的main函数代码中UI相关的语句
    main(int argc, char **argv) {
    
        ......
    
        Device* device = make_device();        
        ui = device->GetUI();
        gCurrentUI = ui;
    
        ui->Init();
        ui->SetLocale(locale);
        ui->SetBackground(RecoveryUI::NONE);
        if (show_text) ui->ShowText(true);
    
        ......
    
        if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
            prompt_and_wait(device, status);
        }
    
        ......
    }
    
    1、首先新建了一个Device类的对象, Device类封装了一些操作,包含UI的操作
    2、调用Device类的GetUI()返回一个DefaultUI对象,recovery中涉及到三个UI类,三个类之间为继承关系,分别为DefaultUI、
       ScreenRecoveryUI、RecoveryUI
    3、调用DefaultUI类的Init(), DefaultUI类没有Init()方法,因此将调用它的父类ScreenRecoveryUI的Init()
    4、同理。调用ScreenRecoveryUI类的SetLocale()来标识几个比較特别的区域
    5、同理。调用ScreenRecoveryUI类的SetBackground()设置初始状态的背景图
    6、显示recovery的主界面,即一个选择菜单

     

    void ScreenRecoveryUI::Init()
    {
        gr_init();
    
        gr_font_size(&char_width, &char_height);
    
        text_col = text_row = 0;
        text_rows = gr_fb_height() / char_height;
        if (text_rows > kMaxRows) text_rows = kMaxRows;
        text_top = 1;
    
        text_cols = gr_fb_width() / char_width;
        if (text_cols > kMaxCols - 1) text_cols = kMaxCols - 1;
    
        LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
        backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
        LoadBitmap("icon_error", &backgroundIcon[ERROR]);
        backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
    
        LoadBitmap("progress_empty", &progressBarEmpty);
        LoadBitmap("progress_fill", &progressBarFill);
    
        LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
        LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
        LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
        LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
    
        int i;
    
        progressBarIndeterminate = (gr_surface*)malloc(indeterminate_frames *
                                                        sizeof(gr_surface));
        for (i = 0; i < indeterminate_frames; ++i) {
            char filename[40];
            // "indeterminate01.png", "indeterminate02.png", ...
            sprintf(filename, "indeterminate%02d", i+1);
            LoadBitmap(filename, progressBarIndeterminate+i);
        }
    
        if (installing_frames > 0) {
            installationOverlay = (gr_surface*)malloc(installing_frames *
                                                       sizeof(gr_surface));
            for (i = 0; i < installing_frames; ++i) {
                char filename[40];
                // "icon_installing_overlay01.png",
                // "icon_installing_overlay02.png", ...
                sprintf(filename, "icon_installing_overlay%02d", i+1);
                LoadBitmap(filename, installationOverlay+i);
            }
        } else {
            installationOverlay = NULL;
        }
    
        pthread_create(&progress_t, NULL, progress_thread, NULL);
    
        RecoveryUI::Init();
    }
    
    


    1、gr_init()  初始化图形设备,分配Pixelflinger库渲染的内存

    2、gr_font_size()  将字体相应的surface长宽赋值给char_width和char_height

    3、LoadBitmap()  将png生成surface, 每一个png图片相应一个surface, 全部surface存放在一个数组中
    4、LoadLocalizedBitmap()  将区域文字所在的图片中的text信息依据当前的locale提取出来,生成相应的surface, 所以
       surface也存放在一个数组中
    6、pthread_create(&progress_t, NULL, progress_thread, NULL) 创建一个线程,该线程的任务是一个死循环,在该循环中不停
       地检測currentIcon以及progressBarType来决定是不是要更新进度条。
    7、调用RecoveryUI的Init()。初始化输入事件处理。

     


    void ScreenRecoveryUI::SetLocale(const char* locale) {
        if (locale) {
            char* lang = strdup(locale);
            for (char* p = lang; *p; ++p) {
                if (*p == '_') {
                    *p = '';
                    break;
                }
            }
    
            // A bit cheesy: keep an explicit list of supported languages
            // that are RTL.
            if (strcmp(lang, "ar") == 0 ||   // Arabic
                strcmp(lang, "fa") == 0 ||   // Persian (Farsi)
                strcmp(lang, "he") == 0 ||   // Hebrew (new language code)
                strcmp(lang, "iw") == 0 ||   // Hebrew (old language code)
                strcmp(lang, "ur") == 0) {   // Urdu
                rtl_locale = true;
            }
            free(lang);
        }
    }
    
    

     
    ScreenRecoveryUI类的SetLocale, 该函数依据locale推断所用的字体是否属于阿拉伯语系。阿拉伯语的书写习惯是从右到左,假设是阿拉伯语系的话,就设置一个标志,后面依据这个标志决定从右到左显示文字或进度条。

    SetLocale的參数locale赋值逻辑是这种,先从command文件里读取, command文件里设置locale的命令如"--locale=zh_CN“。假设没有传入locale,初始化过程中会尝试从/cache/recovery/last_locale中读取locale, 假设该文件也没有,则locale不会被赋值。就默认用English.
     
     
    void ScreenRecoveryUI::SetBackground(Icon icon)
    {
        pthread_mutex_lock(&updateMutex);
    
        // Adjust the offset to account for the positioning of the
        // base image on the screen.
        if (backgroundIcon[icon] != NULL) {
            gr_surface bg = backgroundIcon[icon];
            gr_surface text = backgroundText[icon];
            overlay_offset_x = install_overlay_offset_x + (gr_fb_width() - gr_get_width(bg)) / 2;
            overlay_offset_y = install_overlay_offset_y +
                (gr_fb_height() - (gr_get_height(bg) + gr_get_height(text) + 40)) / 2;
        }
    
        currentIcon = icon;
        update_screen_locked();
    
        pthread_mutex_unlock(&updateMutex);
    }
    
    
     
    SetBackground函数比較简洁,关键部分在update_screen_locked,以下我们重点分析一下。
     
    update_screen_locked和update_progress_locked是recovery的UI部分的关键函数。update_screen_locked用来更新背景, update_progress_locked用来更新进度条,由于显示的画面会一直在更新。所以这两个函数会在不同的地方被重复调用

    void ScreenRecoveryUI::update_screen_locked()
    {
        draw_screen_locked();
        gr_flip();
    }
    
     
    update_screen_locked包括两个操作,一是更新screen, 二是切换前后buffer。
     
    void ScreenRecoveryUI::draw_screen_locked()
    {
        draw_background_locked(currentIcon);
        draw_progress_locked();
    
        if (show_text) {
            SetColor(TEXT_FILL);
            gr_fill(0, 0, gr_fb_width(), gr_fb_height());
    
            int y = 0;
            int i = 0;
            if (show_menu) {
                SetColor(HEADER);
    
                for (; i < menu_top + menu_items; ++i) {
                    if (i == menu_top) SetColor(MENU);
    
                    if (i == menu_top + menu_sel) {
                        // draw the highlight bar
                        SetColor(MENU_SEL_BG);
                        gr_fill(0, y-2, gr_fb_width(), y+char_height+2);
                        // white text of selected item
                        SetColor(MENU_SEL_FG);
                        if (menu[i][0]) gr_text(4, y, menu[i], 1);
                        SetColor(MENU);
                    } else {
                        if (menu[i][0]) gr_text(4, y, menu[i], i < menu_top);
                    }
                    y += char_height+4;
                }
                SetColor(MENU);
                y += 4;
                gr_fill(0, y, gr_fb_width(), y+2);
                y += 4;
                ++i;
            }
    
            SetColor(LOG);
    
            // display from the bottom up, until we hit the top of the
            // screen, the bottom of the menu, or we've displayed the
            // entire text buffer.
            int ty;
            int row = (text_top+text_rows-1) % text_rows;
            for (int ty = gr_fb_height() - char_height, count = 0;
                 ty > y+2 && count < text_rows;
                 ty -= char_height, ++count) {
                gr_text(4, ty, text[row], 0);
                --row;
                if (row < 0) row = text_rows-1;
            }
        }
    }
    

     
    draw_background_locked函数的实现代码中又出现了几个以gr_开头的函数,以gr_开头的函数来自minui库,minui库的代码在recovery源代码下的minui文件夹下,minui提供的接口实现了图形的描绘以及固定大小的文字显示。

    gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);  /* 设置字体颜色 */  
    gr_fill(int x, int y, int w, int h);  /* 填充矩形区域,參数分别代表起始坐标、矩形区域大小 */  
    gr_blit(gr_surface source, int sx, int sy, int w, int h, int dx, int dy);  /* 填充由source指定的图片 */  

    draw_background_locked函数先将整个渲染buffer填充为黑色,然后计算背景surface的长宽,文字surface的长宽,再结合fb的长宽计算出背景surface以及文字surface显示的坐标。有长宽和坐标就能够调用Pixelflinger的接口在渲染buffer上进行渲染。
     
    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);
    
            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;
                }
            }
        }
    }
    
    

     
    draw_progress_locked函数的原理与 update_screen_locked函数类似。 终于是将进度条的surface输出到渲染buffer, 
    recovery中各个场景的画面,就是由背景、文字、进度条的重叠,不同的是所用的surface 以及surface的坐标。
     
    recovery main函数中的UI代码基本上已经分析过了。最后一点主菜单的显示。就是通过上面介绍的这些接口将文字图片显示出来,因此就不再多讲。总的来说。recovery的UI显示部分难度不大,应用层调用minui库实现了图形的描绘以及固定大小的文字显示。minui库调用了Pixelflinger库来进行渲染。

     
    附上minui部分接口的说明,供參考
    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);  /* 获取图片高度 */  
    /* 依据图片创建显示资源数据,name为图片在mk文件指定的相对路径 */  
    int res_create_surface(const char* name, gr_surface* pSurface);  
    void res_free_surface(gr_surface surface);       /* 释放资源数据 */  
    

  • 相关阅读:
    POJ 1141 括号匹配 DP
    881. Boats to Save People
    870. Advantage Shuffle
    874. Walking Robot Simulation
    文件操作
    861. Score After Flipping Matrix
    860. Lemonade Change
    842. Split Array into Fibonacci Sequence
    765. Couples Holding Hands
    763. Partition Labels
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/6973335.html
Copyright © 2011-2022 走看看