recovery: simple graphical ui
Change-Id: If1635438af5b8456283c3a138ccdccea6054c478
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 4d1f9b2..e7fde5d 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -31,6 +31,7 @@
#include "minui/minui.h"
static GRFont* gr_font = nullptr;
+static GRFont* gr_font_menu = nullptr;
static MinuiBackend* gr_backend = nullptr;
static int overscan_offset_x = 0;
@@ -54,6 +55,10 @@
return gr_font;
}
+const GRFont* gr_menu_font() {
+ return gr_font_menu;
+}
+
PixelFormat gr_pixel_format() {
return pixel_format;
}
@@ -359,6 +364,11 @@
printf("Failed to init font: %d, continuing graphic backend initialization without font file\n",
ret);
}
+ ret = gr_init_font("font_menu", &gr_font_menu);
+ if (ret != 0) {
+ printf("Failed to init menu font: %d. Falling back to system font\n", ret);
+ gr_font_menu = gr_font;
+ }
auto backend = std::unique_ptr<MinuiBackend>{ std::make_unique<MinuiBackendAdf>() };
gr_draw = backend->Init();
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index a6b7b20..b77e932 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -126,6 +126,7 @@
void gr_texticon(int x, int y, const GRSurface* icon);
const GRFont* gr_sys_font();
+const GRFont* gr_menu_font();
int gr_init_font(const char* name, GRFont** dest);
void gr_text(const GRFont* font, int x, int y, const char* s, bool bold);
// Returns -1 if font is nullptr.
diff --git a/recovery.cpp b/recovery.cpp
index 29562d1..972ae2f 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -190,8 +190,8 @@
std::vector<std::string> headers{ "Wipe all user data?", " THIS CAN NOT BE UNDONE!" };
std::vector<std::string> items{ " Cancel", " Factory data reset" };
- size_t chosen_item = ui->ShowPromptWipeDataConfirmationMenu(
- headers, items,
+ size_t chosen_item = ui->ShowMenu(
+ headers, items, 0, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
return (chosen_item == 1);
diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
index ec03c05..4e5dbb9 100644
--- a/recovery_ui/include/recovery_ui/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -37,6 +37,7 @@
MENU_SEL_BG,
MENU_SEL_BG_ACTIVE,
MENU_SEL_FG,
+ SCROLLBAR,
LOG,
TEXT_FILL,
INFO
@@ -56,6 +57,9 @@
// Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis.
virtual int DrawHorizontalRule(int y) const = 0;
+ // Draws a vertical line from y to y + height, on the right of the screen.
+ virtual void DrawScrollBar(int y, int height) const = 0;
+
// Draws a line of text. Returns the offset it should be moving along Y-axis.
virtual int DrawTextLine(int x, int y, const std::string& line, bool bold) const = 0;
@@ -76,6 +80,11 @@
// keeps symmetrical margins of 'x' at each end of a line. Returns the offset it should be moving
// along Y-axis.
virtual int DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const = 0;
+
+ virtual int MenuCharHeight() const = 0;
+ virtual int MenuCharWidth() const = 0;
+ virtual int MenuItemPadding() const = 0;
+ virtual int MenuItemHeight() const = 0;
};
// Interface for classes that maintain the menu selection and display.
@@ -189,6 +198,51 @@
std::vector<std::unique_ptr<GRSurface>> graphic_items_;
};
+class MenuDrawFunctions : public DrawInterface {
+ public:
+ MenuDrawFunctions(const DrawInterface& wrappee);
+ void SetColor(UIElement e) const override {
+ wrappee_.SetColor(e);
+ }
+ void DrawHighlightBar(int x, int y, int width, int height) const override {
+ wrappee_.DrawHighlightBar(x, y, width, height);
+ };
+ void DrawScrollBar(int y, int height) const override {
+ wrappee_.DrawScrollBar(y, height);
+ }
+ int DrawHorizontalRule(int y) const override {
+ return wrappee_.DrawHorizontalRule(y);
+ }
+ void DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
+ int dy) const override {
+ wrappee_.DrawSurface(surface, sx, sy, w, h, dx, dy);
+ }
+ void DrawFill(int x, int y, int w, int h) const override {
+ wrappee_.DrawFill(x, y, w, h);
+ }
+ void DrawTextIcon(int x, int y, const GRSurface* surface) const override {
+ wrappee_.DrawTextIcon(x, y, surface);
+ }
+ int MenuCharHeight() const override {
+ return wrappee_.MenuCharHeight();
+ };
+ int MenuCharWidth() const override {
+ return wrappee_.MenuCharWidth();
+ };
+ int MenuItemPadding() const override {
+ return wrappee_.MenuItemPadding();
+ };
+ int MenuItemHeight() const override {
+ return wrappee_.MenuItemHeight();
+ };
+ int DrawTextLine(int x, int y, const std::string& line, bool bold) const override;
+ int DrawTextLines(int x, int y, const std::vector<std::string>& lines) const override;
+ int DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const override;
+
+ private:
+ const DrawInterface& wrappee_;
+};
+
// Implementation of RecoveryUI appropriate for devices with a screen
// (shows an icon + a progress bar, text logging, menu, etc.)
class ScreenRecoveryUI : public RecoveryUI, public DrawInterface {
@@ -246,7 +300,7 @@
const std::function<int(int, bool)>& key_handler) override;
protected:
- static constexpr int kMenuIndent = 4;
+ static constexpr int kMenuIndent = 24;
// The margin that we don't want to use for showing texts (e.g. round screen, or screen with
// rounded corners).
@@ -323,6 +377,7 @@
// Implementation of the draw functions in DrawInterface.
void SetColor(UIElement e) const override;
void DrawHighlightBar(int x, int y, int width, int height) const override;
+ void DrawScrollBar(int y, int height) const override;
int DrawHorizontalRule(int y) const override;
void DrawSurface(const GRSurface* surface, int sx, int sy, int w, int h, int dx,
int dy) const override;
@@ -331,6 +386,20 @@
int DrawTextLine(int x, int y, const std::string& line, bool bold) const override;
int DrawTextLines(int x, int y, const std::vector<std::string>& lines) const override;
int DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const override;
+ int MenuCharHeight() const override {
+ return menu_char_height_;
+ }
+ int MenuCharWidth() const override {
+ return menu_char_width_;
+ }
+ int MenuItemPadding() const override {
+ return menu_char_height_ * 2 / 3;
+ }
+ int MenuItemHeight() const override {
+ return MenuCharHeight() + 2 * MenuItemPadding();
+ }
+
+ std::unique_ptr<MenuDrawFunctions> menu_draw_funcs_;
// The layout to use.
int layout_;
@@ -348,6 +417,7 @@
std::unique_ptr<GRSurface> wipe_data_confirmation_text_;
std::unique_ptr<GRSurface> wipe_data_menu_header_text_;
+ std::unique_ptr<GRSurface> lineage_logo_;
std::unique_ptr<GRSurface> fastbootd_logo_;
// current_icon_ points to one of the frames in intro_frames_ or loop_frames_, indexed by
@@ -397,6 +467,8 @@
int char_width_;
int char_height_;
+ int menu_char_height_;
+ int menu_char_width_;
// The locale that's used to show the rendered texts.
std::string locale_;
diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp
index 1e6c5a0..33aad03 100644
--- a/recovery_ui/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -69,7 +69,6 @@
max_display_items_(max_items),
max_item_length_(max_length),
text_headers_(headers),
- menu_start_(0),
char_height_(char_height) {
CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max()));
@@ -78,6 +77,7 @@
for (size_t i = 0; i < items_count; ++i) {
text_items_.emplace_back(items[i].substr(0, max_item_length_));
}
+ menu_start_ = std::max(0, (int)selection_ - (int)max_display_items_ + 1);
CHECK(!text_items_.empty());
}
@@ -156,12 +156,6 @@
offset += draw_funcs_.DrawWrappedTextLines(x, y + offset, text_headers());
} else {
offset += draw_funcs_.DrawTextLines(x, y + offset, text_headers());
- // Show the current menu item number in relation to total number if items don't fit on the
- // screen.
- std::string cur_selection_str;
- if (ItemsOverflow(&cur_selection_str)) {
- offset += draw_funcs_.DrawTextLine(x, y + offset, cur_selection_str, true);
- }
}
return offset;
@@ -169,20 +163,21 @@
int TextMenu::DrawItems(int x, int y, int screen_width, bool long_press) const {
int offset = 0;
+ int padding = draw_funcs_.MenuItemPadding();
draw_funcs_.SetColor(UIElement::MENU);
- // Do not draw the horizontal rule for wear devices.
- if (!scrollable()) {
- offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
- }
+ offset += draw_funcs_.DrawHorizontalRule(y + offset) + 4;
+
+ int item_container_offset = offset; // store it for drawing scrollbar on most top
+
for (size_t i = MenuStart(); i < MenuEnd(); ++i) {
bool bold = false;
if (i == selection()) {
// Draw the highlight bar.
draw_funcs_.SetColor(long_press ? UIElement::MENU_SEL_BG_ACTIVE : UIElement::MENU_SEL_BG);
- int bar_height = char_height_ + 4;
- draw_funcs_.DrawHighlightBar(0, y + offset - 2, screen_width, bar_height);
+ int bar_height = padding + char_height_ + padding;
+ draw_funcs_.DrawHighlightBar(0, y + offset, screen_width, bar_height);
// Bold white text for the selected item.
draw_funcs_.SetColor(UIElement::MENU_SEL_FG);
@@ -194,6 +189,15 @@
}
offset += draw_funcs_.DrawHorizontalRule(y + offset);
+ std::string unused;
+ if (ItemsOverflow(&unused)) {
+ int container_height = max_display_items_ * (2 * padding + char_height_);
+ int bar_height = container_height / (text_items_.size() - max_display_items_ + 1);
+ int start_y = y + item_container_offset + bar_height * menu_start_;
+ draw_funcs_.SetColor(UIElement::SCROLLBAR);
+ draw_funcs_.DrawScrollBar(start_y, bar_height);
+ }
+
return offset;
}
@@ -299,7 +303,52 @@
return true;
}
-ScreenRecoveryUI::ScreenRecoveryUI() : ScreenRecoveryUI(false) {}
+MenuDrawFunctions::MenuDrawFunctions(const DrawInterface& wrappee)
+ : wrappee_(wrappee) {
+}
+
+int MenuDrawFunctions::DrawTextLine(int x, int y, const std::string& line, bool bold) const {
+ gr_text(gr_menu_font(), x, y + MenuItemPadding(), line.c_str(), bold);
+ return 2 * MenuItemPadding() + MenuCharHeight();
+}
+
+int MenuDrawFunctions::DrawTextLines(int x, int y, const std::vector<std::string>& lines) const {
+ int offset = 0;
+ for (const auto& line : lines) {
+ offset += DrawTextLine(x, y + offset, line, false);
+ }
+ return offset;
+}
+
+int MenuDrawFunctions::DrawWrappedTextLines(int x, int y, const std::vector<std::string>& lines) const {
+ // Keep symmetrical margins based on the given offset (i.e. x).
+ size_t text_cols = (gr_fb_width() - x * 2) / MenuCharWidth();
+ int offset = 0;
+ for (const auto& line : lines) {
+ size_t next_start = 0;
+ while (next_start < line.size()) {
+ std::string sub = line.substr(next_start, text_cols + 1);
+ if (sub.size() <= text_cols) {
+ next_start += sub.size();
+ } else {
+ // Line too long and must be wrapped to text_cols columns.
+ size_t last_space = sub.find_last_of(" \t\n");
+ if (last_space == std::string::npos) {
+ // No space found, just draw as much as we can.
+ sub.resize(text_cols);
+ next_start += text_cols;
+ } else {
+ sub.resize(last_space);
+ next_start += last_space + 1;
+ }
+ }
+ offset += DrawTextLine(x, y + offset, sub, false);
+ }
+ }
+ return offset;
+}
+
+ScreenRecoveryUI::ScreenRecoveryUI() : ScreenRecoveryUI(true) {}
constexpr int kDefaultMarginHeight = 0;
constexpr int kDefaultMarginWidth = 0;
@@ -491,6 +540,7 @@
}
}
+/* Lineage teal: #167c80 */
void ScreenRecoveryUI::SetColor(UIElement e) const {
switch (e) {
case UIElement::INFO:
@@ -501,13 +551,14 @@
break;
case UIElement::MENU:
case UIElement::MENU_SEL_BG:
- gr_color(0, 106, 157, 255);
+ gr_color(0xd8, 0xd8, 0xd8, 255);
break;
case UIElement::MENU_SEL_BG_ACTIVE:
gr_color(0, 156, 100, 255);
break;
case UIElement::MENU_SEL_FG:
- gr_color(255, 255, 255, 255);
+ case UIElement::SCROLLBAR:
+ gr_color(0x16, 0x7c, 0x80, 255);
break;
case UIElement::LOG:
gr_color(196, 196, 196, 255);
@@ -618,9 +669,17 @@
}
void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const {
+ if (y + height > ScreenHeight())
+ height = ScreenHeight() - y;
gr_fill(x, y, x + width, y + height);
}
+void ScreenRecoveryUI::DrawScrollBar(int y, int height) const {
+ int x = ScreenWidth() - margin_width_;
+ int width = 8;
+ gr_fill(x - width, y, x, y + height);
+}
+
void ScreenRecoveryUI::DrawFill(int x, int y, int w, int h) const {
gr_fill(x, y, w, h);
}
@@ -718,12 +777,19 @@
SetColor(UIElement::INFO);
- for (size_t i = 0; i < title_lines_.size(); i++) {
- y += DrawTextLine(x, y, title_lines_[i], i == 0);
+ if (lineage_logo_) {
+ auto logo_width = gr_get_width(lineage_logo_.get());
+ auto logo_height = gr_get_height(lineage_logo_.get());
+ auto centered_x = ScreenWidth() / 2 - logo_width / 2;
+ DrawSurface(lineage_logo_.get(), 0, 0, logo_width, logo_height, centered_x, y);
+ y += logo_height;
+ } else {
+ for (size_t i = 0; i < title_lines_.size(); i++) {
+ y += DrawTextLine(x, y, title_lines_[i], i == 0);
+ }
+ y += DrawTextLines(x, y, help_message);
}
- y += DrawTextLines(x, y, help_message);
-
y += menu_->DrawHeader(x, y);
y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress());
}
@@ -852,6 +918,7 @@
return false;
}
gr_font_size(gr_sys_font(), &char_width_, &char_height_);
+ gr_font_size(gr_menu_font(), &menu_char_width_, &menu_char_height_);
text_rows_ = (ScreenHeight() - margin_height_ * 2) / char_height_;
text_cols_ = (ScreenWidth() - margin_width_ * 2) / char_width_;
return true;
@@ -877,6 +944,7 @@
if (!InitTextParams()) {
return false;
}
+ menu_draw_funcs_ = std::make_unique<MenuDrawFunctions>(*this);
if (blank_unblank_on_init_) {
gr_fb_blank(true);
@@ -907,6 +975,7 @@
no_command_text_ = LoadLocalizedBitmap("no_command_text");
error_text_ = LoadLocalizedBitmap("error_text");
+ lineage_logo_ = LoadBitmap("logo_image");
if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) ||
android::base::GetBoolProperty("ro.fastbootd.available", false)) {
fastbootd_logo_ = LoadBitmap("fastbootd");
@@ -1169,14 +1238,14 @@
std::unique_ptr<Menu> ScreenRecoveryUI::CreateMenu(const std::vector<std::string>& text_headers,
const std::vector<std::string>& text_items,
size_t initial_selection) const {
- if (text_rows_ > 0 && text_cols_ > 1) {
- return std::make_unique<TextMenu>(scrollable_menu_, text_rows_, text_cols_ - 1, text_headers,
- text_items, initial_selection, char_height_, *this);
- }
-
- fprintf(stderr, "Failed to create text menu, text_rows %zu, text_cols %zu.\n", text_rows_,
- text_cols_);
- return nullptr;
+ int menu_char_width = MenuCharWidth();
+ int menu_char_height = MenuCharHeight();
+ int menu_item_padding = MenuItemPadding();
+ int menu_rows = (ScreenHeight() - margin_height_*2 - gr_get_height(lineage_logo_.get()))
+ / (menu_char_height + 2 * menu_item_padding) - text_headers.size();
+ int menu_cols = (ScreenWidth() - margin_width_*2 - kMenuIndent) / menu_char_width;
+ return std::make_unique<TextMenu>(scrollable_menu_, menu_rows, menu_cols, text_headers, text_items,
+ initial_selection, menu_char_height, *menu_draw_funcs_);
}
int ScreenRecoveryUI::SelectMenu(int sel) {
diff --git a/res-hdpi/images/font_menu.png b/res-hdpi/images/font_menu.png
new file mode 100644
index 0000000..d1aad6d
--- /dev/null
+++ b/res-hdpi/images/font_menu.png
Binary files differ
diff --git a/res-hdpi/images/logo_image.png b/res-hdpi/images/logo_image.png
new file mode 100644
index 0000000..e41b170
--- /dev/null
+++ b/res-hdpi/images/logo_image.png
Binary files differ
diff --git a/res-mdpi/images/font_menu.png b/res-mdpi/images/font_menu.png
new file mode 100644
index 0000000..59b990a
--- /dev/null
+++ b/res-mdpi/images/font_menu.png
Binary files differ
diff --git a/res-mdpi/images/logo_image.png b/res-mdpi/images/logo_image.png
new file mode 100644
index 0000000..2785e0d
--- /dev/null
+++ b/res-mdpi/images/logo_image.png
Binary files differ
diff --git a/res-xhdpi/images/font_menu.png b/res-xhdpi/images/font_menu.png
new file mode 100644
index 0000000..6aead73
--- /dev/null
+++ b/res-xhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xhdpi/images/logo_image.png b/res-xhdpi/images/logo_image.png
new file mode 100644
index 0000000..f8cfa83
--- /dev/null
+++ b/res-xhdpi/images/logo_image.png
Binary files differ
diff --git a/res-xxhdpi/images/font_menu.png b/res-xxhdpi/images/font_menu.png
new file mode 100644
index 0000000..f3b54b3
--- /dev/null
+++ b/res-xxhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xxhdpi/images/logo_image.png b/res-xxhdpi/images/logo_image.png
new file mode 100644
index 0000000..5c16278
--- /dev/null
+++ b/res-xxhdpi/images/logo_image.png
Binary files differ
diff --git a/res-xxxhdpi/images/font_menu.png b/res-xxxhdpi/images/font_menu.png
new file mode 100644
index 0000000..d7c326a
--- /dev/null
+++ b/res-xxxhdpi/images/font_menu.png
Binary files differ
diff --git a/res-xxxhdpi/images/logo_image.png b/res-xxxhdpi/images/logo_image.png
new file mode 100644
index 0000000..981fe3d
--- /dev/null
+++ b/res-xxxhdpi/images/logo_image.png
Binary files differ