diff options
Diffstat (limited to 'screen_ui.cpp')
-rw-r--r-- | screen_ui.cpp | 496 |
1 files changed, 424 insertions, 72 deletions
diff --git a/screen_ui.cpp b/screen_ui.cpp index 2f8b07b9..41d0594a 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -37,6 +37,8 @@ #include <android-base/strings.h> #include <android-base/stringprintf.h> +#include <healthd/BatteryMonitor.h> + #include "common.h" #include "device.h" #include "minui/minui.h" @@ -50,6 +52,69 @@ static double now() { return tv.tv_sec + tv.tv_usec / 1000000.0; } +static void get_battery_status(bool& charged, int& capacity) { + struct healthd_config healthd_config = { + .batteryStatusPath = android::String8(android::String8::kEmptyString), + .batteryHealthPath = android::String8(android::String8::kEmptyString), + .batteryPresentPath = android::String8(android::String8::kEmptyString), + .batteryCapacityPath = android::String8(android::String8::kEmptyString), + .batteryVoltagePath = android::String8(android::String8::kEmptyString), + .batteryTemperaturePath = android::String8(android::String8::kEmptyString), + .batteryTechnologyPath = android::String8(android::String8::kEmptyString), + .batteryCurrentNowPath = android::String8(android::String8::kEmptyString), + .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString), + .batteryChargeCounterPath = android::String8(android::String8::kEmptyString), + .batteryFullChargePath = android::String8(android::String8::kEmptyString), + .batteryCycleCountPath = android::String8(android::String8::kEmptyString), + .energyCounter = NULL, + .boot_min_cap = 0, + .screen_on = NULL + }; + healthd_board_init(&healthd_config); + + android::BatteryMonitor monitor; + monitor.init(&healthd_config); + + int charge_status = monitor.getChargeStatus(); + // Treat unknown status as charged. + charged = (charge_status != android::BATTERY_STATUS_DISCHARGING && + charge_status != android::BATTERY_STATUS_NOT_CHARGING); + android::BatteryProperty prop; + android::status_t status = monitor.getProperty(android::BATTERY_PROP_CAPACITY, &prop); + // If we can't read battery percentage, it may be a device without battery. In this + // situation, use 100 as a fake battery percentage. + if (status != 0) { + prop.valueInt64 = 100; + } + capacity = (int)prop.valueInt64; +} + +ScreenMenuItem::~ScreenMenuItem() { + if (icon_) { + res_free_surface(icon_); + } + if (icon_sel_) { + res_free_surface(icon_sel_); + } +} + +GRSurface* ScreenMenuItem::icon() { + if (!icon_) { + res_create_display_surface(icon_name_.c_str(), &icon_); + } + return icon_; +} + +GRSurface* ScreenMenuItem::icon_sel() { + if (icon_name_sel_.empty()) { + return icon(); + } + if (!icon_sel_) { + res_create_display_surface(icon_name_sel_.c_str(), &icon_sel_); + } + return icon_sel_; +} + ScreenRecoveryUI::ScreenRecoveryUI() : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), @@ -69,9 +134,13 @@ ScreenRecoveryUI::ScreenRecoveryUI() text_top_(0), show_text(false), show_text_ever(false), - menu_(nullptr), + menu_is_main_(true), + menu_type_(MT_NONE), + menu_headers_(nullptr), + menu_start_y_(0), show_menu(false), - menu_items(0), + menu_show_start(0), + menu_show_count(0), menu_sel(0), file_viewer_text_(nullptr), intro_frames(0), @@ -180,7 +249,7 @@ void ScreenRecoveryUI::draw_background_locked() { // Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be // called with updateMutex locked. void ScreenRecoveryUI::draw_foreground_locked() { - if (currentIcon != NONE) { + if (currentIcon != NONE && currentIcon != NO_COMMAND) { gr_color(0, 0, 0, 255); gr_clear(); GRSurface* frame = GetCurrentFrame(); @@ -228,8 +297,12 @@ void ScreenRecoveryUI::draw_foreground_locked() { } } +/* Lineage teal: #167c80 */ void ScreenRecoveryUI::SetColor(UIElement e) const { switch (e) { + case STATUSBAR: + gr_color(255, 255, 255, 255); + break; case INFO: gr_color(249, 194, 0, 255); break; @@ -238,13 +311,13 @@ void ScreenRecoveryUI::SetColor(UIElement e) const { break; case MENU: case MENU_SEL_BG: - gr_color(106, 103, 102, 255); + gr_color(0xd8, 0xd8, 0xd8, 255); break; case MENU_SEL_BG_ACTIVE: gr_color(138, 135, 134, 255); break; case MENU_SEL_FG: - gr_color(0, 177, 229, 255); + gr_color(0x16, 0x7c, 0x80, 255); break; case LOG: gr_color(196, 196, 196, 255); @@ -302,7 +375,8 @@ int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* line next_start += last_space + 1; } } - offset += DrawTextLine(x, y + offset, sub.c_str(), false); + gr_text(gr_menu_font(), x, y + offset, sub.c_str(), false); + offset += menu_char_height_ + 4; } } return offset; @@ -319,6 +393,215 @@ static const char* LONG_PRESS_HELP[] = { NULL }; +void ScreenRecoveryUI::draw_statusbar_locked() { + int y = kMarginHeight; + int x; + + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + + GRSurface* icon; + int icon_x, icon_y, icon_h, icon_w; + + // Local time + char localtm_str[] = "--:--"; + time_t now; + struct tm localtm; + time(&now); + if (now > TV_MIN) { + localtime_r(&now, &localtm); + snprintf(localtm_str, sizeof(localtm_str), "%02d:%02d", + localtm.tm_hour, localtm.tm_min); + } + + // Battery status + bool batt_charged; + int batt_capacity; + get_battery_status(batt_charged, batt_capacity); + char batt_capacity_str[3+1+1]; + snprintf(batt_capacity_str, sizeof(batt_capacity_str), "%d%%", batt_capacity); + + // Draw status bar from right to left + + // Time + SetColor(STATUSBAR); + x = gr_fb_width(); + x -= 5 * char_width_; + gr_text(gr_sys_font(), x, y, localtm_str, false); + + x -= char_width_; // Separator + + // Battery icon + x -= 1 * char_width_; + SetColor((batt_capacity < 20) ? HEADER : STATUSBAR); + + // Top + icon_x = x + char_width_ / 3; + icon_y = y; + icon_w = char_width_ / 3; + icon_h = char_height_ / 12; + gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h); + + // Main rect + icon_x = x; + icon_y = y + icon_h; + icon_w = char_width_; + icon_h = char_height_ - (char_height_ / 12); + gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h); + + // Capacity + icon_x = x + char_width_ / 6; + icon_y = y + char_height_ / 12; + icon_w = char_width_ - (2 * char_width_ / 6); + icon_h = char_height_ - (3 * char_height_ / 12); + int cap_h = icon_h * batt_capacity / 100; + gr_fill(icon_x, icon_y + icon_h - cap_h, icon_x + icon_w, icon_y + icon_h); + gr_color(0, 0, 0, 255); + gr_fill(icon_x, icon_y, icon_x + icon_w, icon_y + icon_h - cap_h); + SetColor(STATUSBAR); + + x -= char_width_; // Separator + + // Battery text + x -= strlen(batt_capacity_str) * char_width_; + gr_text(gr_sys_font(), x, y, batt_capacity_str, false); +} + +/* + * Header layout: + * * 1/32: Status bar + * * Header image + * * 1/32: Margin + */ +void ScreenRecoveryUI::draw_header_locked(int& y) { + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + + GRSurface* icon; + int icon_x, icon_y, icon_h, icon_w; + + y += v_unit / 2; // Margin + + // Draw back icon if not in main menu + if (!menu_is_main_) { + icon = (menu_sel == Device::kGoBack ? ic_back_sel : ic_back); + icon_w = gr_get_width(icon); + icon_h = gr_get_height(icon); + icon_x = (h_unit / 2) + ((h_unit * 1) - icon_w) / 2; + icon_y = y + ((v_unit * 1) - icon_h) / 2; + gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y); + } + y += v_unit; + + // Draw logo + icon = logo_image; + icon_w = gr_get_width(icon); + icon_h = gr_get_height(icon); + icon_x = (gr_fb_width() - icon_w) / 2; + icon_y = y + ((v_unit * 4) - icon_h) / 2; + gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y); + y += v_unit * 4; + + y += v_unit * 1; // Margin +} + +void ScreenRecoveryUI::draw_text_menu_locked(int& y) { + static constexpr int kMenuIndent = 4; + int x = kMarginWidth + kMenuIndent; + + draw_statusbar_locked(); + draw_header_locked(y); + + if (menu_headers_) { + SetColor(HEADER); + // Ignore kMenuIndent, which is not taken into account by text_cols_. + y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_); + + SetColor(MENU); + y += DrawHorizontalRule(y) + 4; + } + + menu_start_y_ = y; + int i; + for (i = menu_show_start; i < (int)menu_items_.size() && y < gr_fb_height(); ++i) { + const ScreenMenuItem& item = menu_items_.at(i); + if (i == menu_sel) { + SetColor(MENU_SEL_FG); + y += menu_char_height_; + gr_text(gr_menu_font(), x, y, item.text().c_str(), true); + y += menu_char_height_; + y += menu_char_height_; + } else { + SetColor(MENU); + y += menu_char_height_; + gr_text(gr_menu_font(), x, y, item.text().c_str(), false); + y += menu_char_height_; + y += menu_char_height_; + } + } + menu_show_count = i - menu_show_start; +} + +/* + * Grid layout. + * + * Grid item: + * Horizontal: + * * 3/9 of screen per item. + * * 1/9 of screen margin around/between items. + * Vertical: + * * 3/16 of screen per item. + * * No margin between items. + * + * Within a grid item: + * Asher's icons 1/5 of grid both dimensions. + * Current icons 2/5 of grid both dimensions. + * Horizontal: + * * All items centered. + * Vertical: + * * Icon lower aligned in top 2/3. + * * Text upper aligned in low 1/3 plus half line margin. + */ +void ScreenRecoveryUI::draw_grid_menu_locked(int& y) { + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + + int grid_w = h_unit * 3; + int grid_h = v_unit * 3; + + draw_statusbar_locked(); + draw_header_locked(y); + + menu_start_y_ = y; + int i; + for (i = menu_show_start; i < (int)menu_items_.size() && y + grid_h < gr_fb_height(); ++i) { + ScreenMenuItem& item = menu_items_.at(i); + int grid_x = (i % 2) ? h_unit * 5 : h_unit * 1; + int grid_y = y; + if (item.icon()) { + GRSurface* icon = (i == menu_sel) ? item.icon_sel() : item.icon(); + int icon_w = gr_get_width(icon); + int icon_h = gr_get_height(icon); + int icon_x = grid_x + (grid_w - icon_w) / 2; + int icon_y = grid_y + ((grid_h * 2 / 3) - icon_h) / 2; + gr_blit(icon, 0, 0, icon_w, icon_h, icon_x, icon_y); + } + if (!item.text().empty()) { + int text_w = item.text().size() * char_width_; + int text_h = char_height_; + int text_x = grid_x + (grid_w - text_w) / 2; + int text_y = grid_y + (grid_h * 2 / 3) + (char_height_ / 2); + SetColor(i == menu_sel ? MENU_SEL_FG : MENU); + gr_text(gr_sys_font(), text_x, text_y, item.text().c_str(), false); + } + if (i % 2) { + y += grid_h; + grid_y = y; + } + } + menu_show_count = i - menu_show_start; +} + // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex // locked. void ScreenRecoveryUI::draw_screen_locked() { @@ -333,50 +616,40 @@ void ScreenRecoveryUI::draw_screen_locked() { int y = kMarginHeight; if (show_menu) { - static constexpr int kMenuIndent = 4; - int x = kMarginWidth + kMenuIndent; - - SetColor(INFO); - y += DrawTextLine(x, y, "Android Recovery", true); - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - y += DrawTextLine(x, y, chunk.c_str(), false); + switch (menu_type_) { + case MT_LIST: + draw_text_menu_locked(y); + break; + case MT_GRID: + draw_grid_menu_locked(y); + break; + default: + break; } - y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); - - SetColor(HEADER); - // Ignore kMenuIndent, which is not taken into account by text_cols_. - y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_); - SetColor(MENU); - y += DrawHorizontalRule(y) + 4; - for (int i = 0; i < menu_items; ++i) { - if (i == menu_sel) { - // Draw the highlight bar. - SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); - // Bold white text for the selected item. - SetColor(MENU_SEL_FG); - y += DrawTextLine(x, y, menu_[i], true); - SetColor(MENU); - } else { - y += DrawTextLine(x, y, menu_[i], false); - } + // Draw version info + if (menu_is_main_) { + int text_x, text_y; + text_x = (gr_fb_width() - (android_version_.size() * char_width_)) / 2; + text_y = gr_fb_height() - 2 * (char_height_ + 4); + DrawTextLine(text_x, text_y, android_version_.c_str(), false); + text_x = (gr_fb_width() - (lineage_version_.size() * char_width_)) / 2; + text_y = gr_fb_height() - 1 * (char_height_ + 4); + DrawTextLine(text_x, text_y, lineage_version_.c_str(), false); } - y += DrawHorizontalRule(y); } - - // 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. - SetColor(LOG); - int row = (text_top_ + text_rows_ - 1) % text_rows_; - size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; - ty -= char_height_, ++count) { - DrawTextLine(kMarginWidth, ty, text_[row], false); - --row; - if (row < 0) row = text_rows_ - 1; + else { + // 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. + SetColor(LOG); + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; + ty -= char_height_, ++count) { + DrawTextLine(kMarginWidth, ty, text_[row], false); + --row; + if (row < 0) row = text_rows_ - 1; + } } } @@ -459,6 +732,10 @@ void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { } } +void ScreenRecoveryUI::FreeBitmap(GRSurface* surface) { + res_free_surface(surface); +} + void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { int result = res_create_localized_alpha_surface(filename, locale_.c_str(), surface); if (result < 0) { @@ -491,6 +768,7 @@ bool ScreenRecoveryUI::InitTextParams() { } gr_font_size(gr_sys_font(), &char_width_, &char_height_); + gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_); text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; return true; @@ -509,11 +787,14 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); - menu_ = Alloc2d(text_rows_, text_cols_ + 1); text_col_ = text_row_ = 0; text_top_ = 1; + LoadBitmap("logo_image", &logo_image); + LoadBitmap("ic_back", &ic_back); + LoadBitmap("ic_back_sel", &ic_back_sel); + LoadBitmap("icon_error", &error_icon); LoadBitmap("progress_empty", &progressBarEmpty); @@ -580,8 +861,10 @@ void ScreenRecoveryUI::LoadAnimation() { void ScreenRecoveryUI::SetBackground(Icon icon) { pthread_mutex_lock(&updateMutex); - currentIcon = icon; - update_screen_locked(); + if (icon != currentIcon) { + currentIcon = icon; + update_screen_locked(); + } pthread_mutex_unlock(&updateMutex); } @@ -698,7 +981,7 @@ void ScreenRecoveryUI::ClearText() { pthread_mutex_unlock(&updateMutex); } -void ScreenRecoveryUI::ShowFile(FILE* fp) { +int ScreenRecoveryUI::ShowFile(FILE* fp) { std::vector<off_t> offsets; offsets.push_back(ftello(fp)); ClearText(); @@ -715,10 +998,16 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) { Redraw(); while (show_prompt) { show_prompt = false; - int key = WaitKey(); - if (key == KEY_POWER || key == KEY_ENTER) { - return; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + RecoveryUI::InputEvent evt = WaitInputEvent(); + if (evt.type() != RecoveryUI::EVENT_TYPE_KEY) { + show_prompt = true; + continue; + } + if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || + evt.key() == KEY_BACKSPACE || evt.key() == KEY_BACK || + evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) { + return evt.key(); + } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) { if (offsets.size() <= 1) { show_prompt = true; } else { @@ -727,7 +1016,7 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) { } } else { if (feof(fp)) { - return; + return -1; } offsets.push_back(ftello(fp)); } @@ -746,13 +1035,14 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) { } } } + return -1; } -void ScreenRecoveryUI::ShowFile(const char* filename) { +int ScreenRecoveryUI::ShowFile(const char* filename) { FILE* fp = fopen_path(filename, "re"); if (fp == nullptr) { Print(" Unable to open %s: %s\n", filename, strerror(errno)); - return; + return -1; } char** old_text = text_; @@ -764,30 +1054,38 @@ void ScreenRecoveryUI::ShowFile(const char* filename) { text_ = file_viewer_text_; ClearText(); - ShowFile(fp); + int key = ShowFile(fp); fclose(fp); text_ = old_text; text_col_ = old_text_col; text_row_ = old_text_row; text_top_ = old_text_top; + return key; } -void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items, +void ScreenRecoveryUI::StartMenu(bool is_main, + menu_type_t type, + const char* const* headers, + const MenuItemVector& items, int initial_selection) { pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - menu_headers_ = headers; - size_t i = 0; - for (; i < text_rows_ && items[i] != nullptr; ++i) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; - } - menu_items = i; - show_menu = true; - menu_sel = initial_selection; - update_screen_locked(); + menu_is_main_ = is_main; + menu_type_ = type; + menu_headers_ = headers; + for (auto& item : items) { + menu_items_.push_back(ScreenMenuItem(item)); + } + show_menu = true; + menu_sel = initial_selection; + draw_screen_locked(); + if (menu_sel < menu_show_start) { + menu_show_start = menu_sel; + } + if (menu_sel >= menu_show_start + menu_show_count) { + menu_show_start = menu_sel - (menu_show_count - 1); } + update_screen_locked(); pthread_mutex_unlock(&updateMutex); } @@ -798,8 +1096,16 @@ int ScreenRecoveryUI::SelectMenu(int sel) { menu_sel = sel; // Wrap at top and bottom. - if (menu_sel < 0) menu_sel = menu_items - 1; - if (menu_sel >= menu_items) menu_sel = 0; + if (menu_sel < 0) menu_sel = (int)menu_items_.size() - 1; + if (menu_sel >= (int)menu_items_.size()) menu_sel = 0; + + // Scroll + if (menu_sel < menu_show_start) { + menu_show_start = menu_sel; + } + if (menu_sel >= menu_show_start + menu_show_count) { + menu_show_start = menu_sel - (menu_show_count - 1); + } sel = menu_sel; if (menu_sel != old_sel) update_screen_locked(); @@ -808,11 +1114,57 @@ int ScreenRecoveryUI::SelectMenu(int sel) { return sel; } +int ScreenRecoveryUI::SelectMenu(const Point& point) { + int sel = Device::kNoAction; + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + pthread_mutex_lock(&updateMutex); + if (show_menu) { + if (point.y() < menu_start_y_) { + if (!menu_is_main_ && + point.x() >= h_unit / 2 && point.x() < h_unit * 3 / 2 && + point.y() >= v_unit * 1 / 2 && point.y() < v_unit * 3 / 2) { + sel = Device::kGoBack; + } + } + else { + int row = -1, col = -1; + switch (menu_type_) { + case MT_LIST: + sel = (point.y() - menu_start_y_) / (menu_char_height_ * 3); + break; + case MT_GRID: + row = (point.y() - menu_start_y_) / (gr_fb_height() * 3 / 16); + col = (point.x()) / (gr_fb_width() / 9); + if ((col % 4) != 0) { + sel = row * 2 + ((col - 1) / 4); + } + break; + default: + break; + } + if (sel >= (int)menu_items_.size()) { + sel = Device::kNoAction; + } + } + if (sel != -1 && sel != menu_sel) { + menu_sel = sel; + update_screen_locked(); + usleep(100*1000); + } + } + pthread_mutex_unlock(&updateMutex); + return sel; +} + void ScreenRecoveryUI::EndMenu() { pthread_mutex_lock(&updateMutex); if (show_menu && text_rows_ > 0 && text_cols_ > 0) { show_menu = false; } + menu_type_ = MT_NONE; + menu_headers_ = nullptr; + menu_items_.clear(); pthread_mutex_unlock(&updateMutex); } |