Merge "Move menu headers/items to std::vector<std::string>."

This commit is contained in:
Tao Bao
2018-05-03 23:13:09 +00:00
committed by Gerrit Code Review
10 changed files with 147 additions and 141 deletions

View File

@@ -16,9 +16,13 @@
#include "device.h" #include "device.h"
#include <android-base/logging.h>
#include <android-base/macros.h>
#include "ui.h" #include "ui.h"
static const char* MENU_ITEMS[] = { // clang-format off
static constexpr const char* kItems[]{
"Reboot system now", "Reboot system now",
"Reboot to bootloader", "Reboot to bootloader",
"Apply update from ADB", "Apply update from ADB",
@@ -32,10 +36,11 @@ static const char* MENU_ITEMS[] = {
"Run graphics test", "Run graphics test",
"Run locale test", "Run locale test",
"Power off", "Power off",
nullptr,
}; };
// clang-format on
static const Device::BuiltinAction MENU_ACTIONS[] = { // clang-format off
static constexpr Device::BuiltinAction kMenuActions[] {
Device::REBOOT, Device::REBOOT,
Device::REBOOT_BOOTLOADER, Device::REBOOT_BOOTLOADER,
Device::APPLY_ADB_SIDELOAD, Device::APPLY_ADB_SIDELOAD,
@@ -50,18 +55,20 @@ static const Device::BuiltinAction MENU_ACTIONS[] = {
Device::RUN_LOCALE_TEST, Device::RUN_LOCALE_TEST,
Device::SHUTDOWN, Device::SHUTDOWN,
}; };
// clang-format on
static_assert(sizeof(MENU_ITEMS) / sizeof(MENU_ITEMS[0]) == static_assert(arraysize(kItems) == arraysize(kMenuActions),
sizeof(MENU_ACTIONS) / sizeof(MENU_ACTIONS[0]) + 1, "kItems and kMenuActions should have the same length.");
"MENU_ITEMS and MENU_ACTIONS should have the same length, "
"except for the extra NULL entry in MENU_ITEMS.");
const char* const* Device::GetMenuItems() { static const std::vector<std::string> kMenuItems(kItems, kItems + arraysize(kItems));
return MENU_ITEMS;
const std::vector<std::string>& Device::GetMenuItems() {
return kMenuItems;
} }
Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { Device::BuiltinAction Device::InvokeMenuItem(size_t menu_position) {
return menu_position < 0 ? NO_ACTION : MENU_ACTIONS[menu_position]; // CHECK_LT(menu_position, );
return kMenuActions[menu_position];
} }
int Device::HandleMenuKey(int key, bool visible) { int Device::HandleMenuKey(int key, bool visible) {

View File

@@ -17,11 +17,37 @@
#ifndef _RECOVERY_DEVICE_H #ifndef _RECOVERY_DEVICE_H
#define _RECOVERY_DEVICE_H #define _RECOVERY_DEVICE_H
#include <stddef.h>
#include <string>
#include <vector>
// Forward declaration to avoid including "ui.h". // Forward declaration to avoid including "ui.h".
class RecoveryUI; class RecoveryUI;
class Device { class Device {
public: public:
static constexpr const int kNoAction = -1;
static constexpr const int kHighlightUp = -2;
static constexpr const int kHighlightDown = -3;
static constexpr const int kInvokeItem = -4;
enum BuiltinAction {
NO_ACTION = 0,
REBOOT = 1,
APPLY_SDCARD = 2,
// APPLY_CACHE was 3.
APPLY_ADB_SIDELOAD = 4,
WIPE_DATA = 5,
WIPE_CACHE = 6,
REBOOT_BOOTLOADER = 7,
SHUTDOWN = 8,
VIEW_RECOVERY_LOGS = 9,
MOUNT_SYSTEM = 10,
RUN_GRAPHICS_TEST = 11,
RUN_LOCALE_TEST = 12,
};
explicit Device(RecoveryUI* ui) : ui_(ui) {} explicit Device(RecoveryUI* ui) : ui_(ui) {}
virtual ~Device() {} virtual ~Device() {}
@@ -48,44 +74,23 @@ class Device {
// //
// Returns one of the defined constants below in order to: // Returns one of the defined constants below in order to:
// //
// - move the menu highlight (kHighlight{Up,Down}) // - move the menu highlight (kHighlight{Up,Down}: negative value)
// - invoke the highlighted item (kInvokeItem) // - invoke the highlighted item (kInvokeItem: negative value)
// - do nothing (kNoAction) // - do nothing (kNoAction: negative value)
// - invoke a specific action (a menu position: any non-negative number) // - invoke a specific action (a menu position: non-negative value)
virtual int HandleMenuKey(int key, bool visible); virtual int HandleMenuKey(int key, bool visible);
enum BuiltinAction { // Returns the list of menu items (a vector of strings). The menu_position passed to
NO_ACTION = 0, // InvokeMenuItem will correspond to the indexes into this array.
REBOOT = 1, virtual const std::vector<std::string>& GetMenuItems();
APPLY_SDCARD = 2,
// APPLY_CACHE was 3.
APPLY_ADB_SIDELOAD = 4,
WIPE_DATA = 5,
WIPE_CACHE = 6,
REBOOT_BOOTLOADER = 7,
SHUTDOWN = 8,
VIEW_RECOVERY_LOGS = 9,
MOUNT_SYSTEM = 10,
RUN_GRAPHICS_TEST = 11,
RUN_LOCALE_TEST = 12,
};
// Return the list of menu items (an array of strings, NULL-terminated). The menu_position passed // Performs a recovery action selected from the menu. 'menu_position' will be the index of the
// to InvokeMenuItem will correspond to the indexes into this array. // selected menu item, or a non-negative value returned from HandleMenuKey(). The menu will be
virtual const char* const* GetMenuItems(); // hidden when this is called; implementations can call ui_print() to print information to the
// Perform a recovery action selected from the menu. 'menu_position' will be the item number of
// the selected menu item, or a non-negative number returned from HandleMenuKey(). The menu will
// be hidden when this is called; implementations can call ui_print() to print information to the
// screen. If the menu position is one of the builtin actions, you can just return the // screen. If the menu position is one of the builtin actions, you can just return the
// corresponding enum value. If it is an action specific to your device, you actually perform it // corresponding enum value. If it is an action specific to your device, you actually perform it
// here and return NO_ACTION. // here and return NO_ACTION.
virtual BuiltinAction InvokeMenuItem(int menu_position); virtual BuiltinAction InvokeMenuItem(size_t menu_position);
static const int kNoAction = -1;
static const int kHighlightUp = -2;
static const int kHighlightDown = -3;
static const int kInvokeItem = -4;
// Called before and after we do a wipe data/factory reset operation, either via a reboot from the // Called before and after we do a wipe data/factory reset operation, either via a reboot from the
// main system with the --wipe_data flag, or when the user boots into recovery image manually and // main system with the --wipe_data flag, or when the user boots into recovery image manually and

View File

@@ -507,7 +507,7 @@ static std::string browse_directory(const std::string& path, Device* device) {
} }
std::vector<std::string> dirs; std::vector<std::string> dirs;
std::vector<std::string> zips = { "../" }; // "../" is always the first entry. std::vector<std::string> entries{ "../" }; // "../" is always the first entry.
dirent* de; dirent* de;
while ((de = readdir(d.get())) != nullptr) { while ((de = readdir(d.get())) != nullptr) {
@@ -518,31 +518,25 @@ static std::string browse_directory(const std::string& path, Device* device) {
if (name == "." || name == "..") continue; if (name == "." || name == "..") continue;
dirs.push_back(name + "/"); dirs.push_back(name + "/");
} else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) { } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
zips.push_back(name); entries.push_back(name);
} }
} }
std::sort(dirs.begin(), dirs.end()); std::sort(dirs.begin(), dirs.end());
std::sort(zips.begin(), zips.end()); std::sort(entries.begin(), entries.end());
// Append dirs to the zips list. // Append dirs to the entries list.
zips.insert(zips.end(), dirs.begin(), dirs.end()); entries.insert(entries.end(), dirs.begin(), dirs.end());
const char* entries[zips.size() + 1]; std::vector<std::string> headers{ "Choose a package to install:", path };
entries[zips.size()] = nullptr;
for (size_t i = 0; i < zips.size(); i++) {
entries[i] = zips[i].c_str();
}
const char* headers[] = { "Choose a package to install:", path.c_str(), nullptr }; size_t chosen_item = 0;
int chosen_item = 0;
while (true) { while (true) {
chosen_item = ui->ShowMenu( chosen_item = ui->ShowMenu(
headers, entries, chosen_item, true, headers, entries, chosen_item, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
const std::string& item = zips[chosen_item]; const std::string& item = entries[chosen_item];
if (chosen_item == 0) { if (chosen_item == 0) {
// Go up but continue browsing (if the caller is browse_directory). // Go up but continue browsing (if the caller is browse_directory).
return ""; return "";
@@ -564,10 +558,10 @@ static std::string browse_directory(const std::string& path, Device* device) {
} }
static bool yes_no(Device* device, const char* question1, const char* question2) { static bool yes_no(Device* device, const char* question1, const char* question2) {
const char* headers[] = { question1, question2, NULL }; std::vector<std::string> headers{ question1, question2 };
const char* items[] = { " No", " Yes", NULL }; std::vector<std::string> items{ " No", " Yes" };
int chosen_item = ui->ShowMenu( size_t chosen_item = ui->ShowMenu(
headers, items, 0, true, headers, items, 0, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
return (chosen_item == 1); return (chosen_item == 1);
@@ -601,20 +595,20 @@ static bool wipe_data(Device* device) {
static bool prompt_and_wipe_data(Device* device) { static bool prompt_and_wipe_data(Device* device) {
// Use a single string and let ScreenRecoveryUI handles the wrapping. // Use a single string and let ScreenRecoveryUI handles the wrapping.
const char* const headers[] = { std::vector<std::string> headers{
"Can't load Android system. Your data may be corrupt. " "Can't load Android system. Your data may be corrupt. "
"If you continue to get this message, you may need to " "If you continue to get this message, you may need to "
"perform a factory data reset and erase all user data " "perform a factory data reset and erase all user data "
"stored on this device.", "stored on this device.",
nullptr
}; };
const char* const items[] = { // clang-format off
std::vector<std::string> items {
"Try again", "Try again",
"Factory data reset", "Factory data reset",
NULL
}; };
// clang-format on
for (;;) { for (;;) {
int chosen_item = ui->ShowMenu( size_t chosen_item = ui->ShowMenu(
headers, items, 0, true, headers, items, 0, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
if (chosen_item != 1) { if (chosen_item != 1) {
@@ -806,17 +800,12 @@ static void choose_recovery_file(Device* device) {
entries.push_back("Back"); entries.push_back("Back");
std::vector<const char*> menu_entries(entries.size()); std::vector<std::string> headers{ "Select file to view" };
std::transform(entries.cbegin(), entries.cend(), menu_entries.begin(),
[](const std::string& entry) { return entry.c_str(); });
menu_entries.push_back(nullptr);
const char* headers[] = { "Select file to view", nullptr }; size_t chosen_item = 0;
int chosen_item = 0;
while (true) { while (true) {
chosen_item = ui->ShowMenu( chosen_item = ui->ShowMenu(
headers, menu_entries.data(), chosen_item, true, headers, entries, chosen_item, true,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
if (entries[chosen_item] == "Back") break; if (entries[chosen_item] == "Back") break;
@@ -963,14 +952,15 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
} }
ui->SetProgressType(RecoveryUI::EMPTY); ui->SetProgressType(RecoveryUI::EMPTY);
int chosen_item = ui->ShowMenu( size_t chosen_item = ui->ShowMenu(
nullptr, device->GetMenuItems(), 0, false, {}, device->GetMenuItems(), 0, false,
std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2)); std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
// Device-specific code may take some action here. It may return one of the core actions // Device-specific code may take some action here. It may return one of the core actions
// handled in the switch statement below. // handled in the switch statement below.
Device::BuiltinAction chosen_action = Device::BuiltinAction chosen_action = (chosen_item == static_cast<size_t>(-1))
(chosen_item == -1) ? Device::REBOOT : device->InvokeMenuItem(chosen_item); ? Device::REBOOT
: device->InvokeMenuItem(chosen_item);
bool should_wipe_cache = false; bool should_wipe_cache = false;
switch (chosen_action) { switch (chosen_action) {

View File

@@ -31,6 +31,7 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <algorithm>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@@ -52,8 +53,9 @@ static double now() {
return tv.tv_sec + tv.tv_usec / 1000000.0; return tv.tv_sec + tv.tv_usec / 1000000.0;
} }
Menu::Menu(bool scrollable, size_t max_items, size_t max_length, const char* const* headers, Menu::Menu(bool scrollable, size_t max_items, size_t max_length,
const char* const* items, int initial_selection) const std::vector<std::string>& headers, const std::vector<std::string>& items,
size_t initial_selection)
: scrollable_(scrollable), : scrollable_(scrollable),
max_display_items_(max_items), max_display_items_(max_items),
max_item_length_(max_length), max_item_length_(max_length),
@@ -63,15 +65,15 @@ Menu::Menu(bool scrollable, size_t max_items, size_t max_length, const char* con
CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max())); CHECK_LE(max_items, static_cast<size_t>(std::numeric_limits<int>::max()));
// It's fine to have more entries than text_rows_ if scrollable menu is supported. // It's fine to have more entries than text_rows_ if scrollable menu is supported.
size_t max_items_count = scrollable_ ? std::numeric_limits<int>::max() : max_display_items_; size_t items_count = scrollable_ ? items.size() : std::min(items.size(), max_display_items_);
for (size_t i = 0; i < max_items_count && items[i] != nullptr; ++i) { for (size_t i = 0; i < items_count; ++i) {
text_items_.emplace_back(items[i], strnlen(items[i], max_item_length_)); text_items_.emplace_back(items[i].substr(0, max_item_length_));
} }
CHECK(!text_items_.empty()); CHECK(!text_items_.empty());
} }
const char* const* Menu::text_headers() const { const std::vector<std::string>& Menu::text_headers() const {
return text_headers_; return text_headers_;
} }
@@ -99,7 +101,7 @@ bool Menu::ItemsOverflow(std::string* cur_selection_str) const {
} }
*cur_selection_str = *cur_selection_str =
android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount()); android::base::StringPrintf("Current item: %zu/%zu", selection_ + 1, ItemsCount());
return true; return true;
} }
@@ -503,10 +505,10 @@ void ScreenRecoveryUI::draw_screen_locked() {
gr_clear(); gr_clear();
// clang-format off // clang-format off
static std::vector<std::string> REGULAR_HELP = { static std::vector<std::string> REGULAR_HELP{
"Use volume up/down and power.", "Use volume up/down and power.",
}; };
static std::vector<std::string> LONG_PRESS_HELP = { static std::vector<std::string> LONG_PRESS_HELP{
"Any button cycles highlight.", "Any button cycles highlight.",
"Long-press activates.", "Long-press activates.",
}; };
@@ -532,22 +534,12 @@ void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
y += DrawTextLines(x, y, help_message); y += DrawTextLines(x, y, help_message);
auto convert_to_vector = [](const char* const* items) -> std::vector<std::string> {
if (items == nullptr) return {};
std::vector<std::string> result;
for (size_t i = 0; items[i] != nullptr; ++i) {
result.emplace_back(items[i]);
}
return result;
};
// Draw menu header. // Draw menu header.
SetColor(HEADER); SetColor(HEADER);
if (!menu_->scrollable()) { if (!menu_->scrollable()) {
y += DrawWrappedTextLines(x, y, convert_to_vector(menu_->text_headers())); y += DrawWrappedTextLines(x, y, menu_->text_headers());
} else { } else {
y += DrawTextLines(x, y, convert_to_vector(menu_->text_headers())); y += DrawTextLines(x, y, menu_->text_headers());
// Show the current menu item number in relation to total number if items don't fit on the // Show the current menu item number in relation to total number if items don't fit on the
// screen. // screen.
std::string cur_selection_str; std::string cur_selection_str;
@@ -979,8 +971,8 @@ void ScreenRecoveryUI::ShowFile(const std::string& filename) {
text_row_ = old_text_row; text_row_ = old_text_row;
} }
void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items, void ScreenRecoveryUI::StartMenu(const std::vector<std::string>& headers,
int initial_selection) { const std::vector<std::string>& items, size_t initial_selection) {
pthread_mutex_lock(&updateMutex); pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 1) { if (text_rows_ > 0 && text_cols_ > 1) {
menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_, text_cols_ - 1, headers, items, menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_, text_cols_ - 1, headers, items,
@@ -1013,9 +1005,10 @@ void ScreenRecoveryUI::EndMenu() {
pthread_mutex_unlock(&updateMutex); pthread_mutex_unlock(&updateMutex);
} }
int ScreenRecoveryUI::ShowMenu(const char* const* headers, const char* const* items, size_t ScreenRecoveryUI::ShowMenu(const std::vector<std::string>& headers,
int initial_selection, bool menu_only, const std::vector<std::string>& items, size_t initial_selection,
const std::function<int(int, bool)>& key_handler) { bool menu_only,
const std::function<int(int, bool)>& key_handler) {
// Throw away keys pressed previously, so user doesn't accidentally trigger menu items. // Throw away keys pressed previously, so user doesn't accidentally trigger menu items.
FlushKeys(); FlushKeys();
@@ -1031,7 +1024,7 @@ int ScreenRecoveryUI::ShowMenu(const char* const* headers, const char* const* it
} else { } else {
LOG(INFO) << "Timed out waiting for key input; rebooting."; LOG(INFO) << "Timed out waiting for key input; rebooting.";
EndMenu(); EndMenu();
return -1; return static_cast<size_t>(-1);
} }
} }

View File

@@ -35,14 +35,15 @@ class Menu {
public: public:
// Constructs a Menu instance with the given |headers|, |items| and properties. Sets the initial // Constructs a Menu instance with the given |headers|, |items| and properties. Sets the initial
// selection to |initial_selection|. // selection to |initial_selection|.
Menu(bool scrollable, size_t max_items, size_t max_length, const char* const* headers, Menu(bool scrollable, size_t max_items, size_t max_length,
const char* const* items, int initial_selection); const std::vector<std::string>& headers, const std::vector<std::string>& items,
size_t initial_selection);
bool scrollable() const { bool scrollable() const {
return scrollable_; return scrollable_;
} }
int selection() const { size_t selection() const {
return selection_; return selection_;
} }
@@ -66,7 +67,7 @@ class Menu {
// /cache/recovery/last_log.1 // /cache/recovery/last_log.1
// /cache/recovery/last_log.2 // /cache/recovery/last_log.2
// ... // ...
const char* const* text_headers() const; const std::vector<std::string>& text_headers() const;
std::string TextItem(size_t index) const; std::string TextItem(size_t index) const;
// Checks if the menu items fit vertically on the screen. Returns true and set the // Checks if the menu items fit vertically on the screen. Returns true and set the
@@ -84,15 +85,14 @@ class Menu {
const size_t max_display_items_; const size_t max_display_items_;
// The length of each item to fit horizontally on a screen. // The length of each item to fit horizontally on a screen.
const size_t max_item_length_; const size_t max_item_length_;
// The menu headers.
// Internal storage for the menu headers and items in text. std::vector<std::string> text_headers_;
const char* const* text_headers_; // The actual menu items trimmed to fit the given properties.
std::vector<std::string> text_items_; std::vector<std::string> text_items_;
// The first item to display on the screen. // The first item to display on the screen.
size_t menu_start_; size_t menu_start_;
// Current menu selection. // Current menu selection.
int selection_; size_t selection_;
}; };
// Implementation of RecoveryUI appropriate for devices with a screen // Implementation of RecoveryUI appropriate for devices with a screen
@@ -137,8 +137,9 @@ class ScreenRecoveryUI : public RecoveryUI {
void ShowFile(const std::string& filename) override; void ShowFile(const std::string& filename) override;
// menu display // menu display
int ShowMenu(const char* const* headers, const char* const* items, int initial_selection, size_t ShowMenu(const std::vector<std::string>& headers, const std::vector<std::string>& items,
bool menu_only, const std::function<int(int, bool)>& key_handler) override; size_t initial_selection, bool menu_only,
const std::function<int(int, bool)>& key_handler) override;
void KeyLongPress(int) override; void KeyLongPress(int) override;
@@ -166,8 +167,8 @@ class ScreenRecoveryUI : public RecoveryUI {
// Displays some header text followed by a menu of items, which appears at the top of the screen // Displays some header text followed by a menu of items, which appears at the top of the screen
// (in place of any scrolling ui_print() output, if necessary). // (in place of any scrolling ui_print() output, if necessary).
virtual void StartMenu(const char* const* headers, const char* const* items, virtual void StartMenu(const std::vector<std::string>& headers,
int initial_selection); const std::vector<std::string>& items, size_t initial_selection);
// Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item
// selected. // selected.

View File

@@ -19,6 +19,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <vector>
#include "ui.h" #include "ui.h"
@@ -57,9 +58,10 @@ class StubRecoveryUI : public RecoveryUI {
void ShowFile(const std::string& /* filename */) override {} void ShowFile(const std::string& /* filename */) override {}
// menu display // menu display
int ShowMenu(const char* const* /* headers */, const char* const* /* items */, size_t ShowMenu(const std::vector<std::string>& /* headers */,
int initial_selection, bool /* menu_only */, const std::vector<std::string>& /* items */, size_t initial_selection,
const std::function<int(int, bool)>& /* key_handler */) override { bool /* menu_only */,
const std::function<int(int, bool)>& /* key_handler */) override {
return initial_selection; return initial_selection;
} }
}; };

View File

@@ -14,19 +14,22 @@
* limitations under the License. * limitations under the License.
*/ */
#include "screen_ui.h" #include <stddef.h>
#include <string> #include <string>
#include <vector>
#include <gtest/gtest.h> #include <gtest/gtest.h>
constexpr const char* HEADER[] = { "header", nullptr }; #include "screen_ui.h"
constexpr const char* ITEMS[] = { "items1", "items2", "items3", "items4", "1234567890", nullptr };
static const std::vector<std::string> HEADERS{ "header" };
static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" };
TEST(ScreenUITest, StartPhoneMenuSmoke) { TEST(ScreenUITest, StartPhoneMenuSmoke) {
Menu menu(false, 10, 20, HEADER, ITEMS, 0); Menu menu(false, 10, 20, HEADERS, ITEMS, 0);
ASSERT_FALSE(menu.scrollable()); ASSERT_FALSE(menu.scrollable());
ASSERT_EQ(HEADER[0], menu.text_headers()[0]); ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
ASSERT_EQ(5u, menu.ItemsCount()); ASSERT_EQ(5u, menu.ItemsCount());
std::string message; std::string message;
@@ -39,9 +42,9 @@ TEST(ScreenUITest, StartPhoneMenuSmoke) {
} }
TEST(ScreenUITest, StartWearMenuSmoke) { TEST(ScreenUITest, StartWearMenuSmoke) {
Menu menu(true, 10, 8, HEADER, ITEMS, 1); Menu menu(true, 10, 8, HEADERS, ITEMS, 1);
ASSERT_TRUE(menu.scrollable()); ASSERT_TRUE(menu.scrollable());
ASSERT_EQ(HEADER[0], menu.text_headers()[0]); ASSERT_EQ(HEADERS[0], menu.text_headers()[0]);
ASSERT_EQ(5u, menu.ItemsCount()); ASSERT_EQ(5u, menu.ItemsCount());
std::string message; std::string message;
@@ -55,7 +58,7 @@ TEST(ScreenUITest, StartWearMenuSmoke) {
} }
TEST(ScreenUITest, StartPhoneMenuItemsOverflow) { TEST(ScreenUITest, StartPhoneMenuItemsOverflow) {
Menu menu(false, 1, 20, HEADER, ITEMS, 0); Menu menu(false, 1, 20, HEADERS, ITEMS, 0);
ASSERT_FALSE(menu.scrollable()); ASSERT_FALSE(menu.scrollable());
ASSERT_EQ(1u, menu.ItemsCount()); ASSERT_EQ(1u, menu.ItemsCount());
@@ -70,7 +73,7 @@ TEST(ScreenUITest, StartPhoneMenuItemsOverflow) {
} }
TEST(ScreenUITest, StartWearMenuItemsOverflow) { TEST(ScreenUITest, StartWearMenuItemsOverflow) {
Menu menu(true, 1, 20, HEADER, ITEMS, 0); Menu menu(true, 1, 20, HEADERS, ITEMS, 0);
ASSERT_TRUE(menu.scrollable()); ASSERT_TRUE(menu.scrollable());
ASSERT_EQ(5u, menu.ItemsCount()); ASSERT_EQ(5u, menu.ItemsCount());
@@ -88,7 +91,7 @@ TEST(ScreenUITest, StartWearMenuItemsOverflow) {
TEST(ScreenUITest, PhoneMenuSelectSmoke) { TEST(ScreenUITest, PhoneMenuSelectSmoke) {
int sel = 0; int sel = 0;
Menu menu(false, 10, 20, HEADER, ITEMS, sel); Menu menu(false, 10, 20, HEADERS, ITEMS, sel);
// Mimic down button 10 times (2 * items size) // Mimic down button 10 times (2 * items size)
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sel = menu.Select(++sel); sel = menu.Select(++sel);
@@ -117,7 +120,7 @@ TEST(ScreenUITest, PhoneMenuSelectSmoke) {
TEST(ScreenUITest, WearMenuSelectSmoke) { TEST(ScreenUITest, WearMenuSelectSmoke) {
int sel = 0; int sel = 0;
Menu menu(true, 10, 20, HEADER, ITEMS, sel); Menu menu(true, 10, 20, HEADERS, ITEMS, sel);
// Mimic pressing down button 10 times (2 * items size) // Mimic pressing down button 10 times (2 * items size)
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
sel = menu.Select(++sel); sel = menu.Select(++sel);
@@ -146,7 +149,7 @@ TEST(ScreenUITest, WearMenuSelectSmoke) {
TEST(ScreenUITest, WearMenuSelectItemsOverflow) { TEST(ScreenUITest, WearMenuSelectItemsOverflow) {
int sel = 1; int sel = 1;
Menu menu(true, 3, 20, HEADER, ITEMS, sel); Menu menu(true, 3, 20, HEADERS, ITEMS, sel);
ASSERT_EQ(5u, menu.ItemsCount()); ASSERT_EQ(5u, menu.ItemsCount());
// Scroll the menu to the end, and check the start & end of menu. // Scroll the menu to the end, and check the start & end of menu.

10
ui.h
View File

@@ -23,6 +23,7 @@
#include <functional> #include <functional>
#include <string> #include <string>
#include <vector>
// Abstract class for controlling the user interface during recovery. // Abstract class for controlling the user interface during recovery.
class RecoveryUI { class RecoveryUI {
@@ -139,10 +140,11 @@ class RecoveryUI {
// key_handler, which may be beyond the range of menu items. This could be used to trigger a // key_handler, which may be beyond the range of menu items. This could be used to trigger a
// device-specific action, even without that being listed in the menu. Caller needs to handle // device-specific action, even without that being listed in the menu. Caller needs to handle
// such a case accordingly (e.g. by calling Device::InvokeMenuItem() to process the action). // such a case accordingly (e.g. by calling Device::InvokeMenuItem() to process the action).
// Returns a non-negative value (the chosen item number or device-specific action code), or -1 if // Returns a non-negative value (the chosen item number or device-specific action code), or
// timed out waiting for input. // static_cast<size_t>(-1) if timed out waiting for input.
virtual int ShowMenu(const char* const* headers, const char* const* items, int initial_selection, virtual size_t ShowMenu(const std::vector<std::string>& headers,
bool menu_only, const std::function<int(int, bool)>& key_handler) = 0; const std::vector<std::string>& items, size_t initial_selection,
bool menu_only, const std::function<int(int, bool)>& key_handler) = 0;
protected: protected:
void EnqueueKey(int key_code); void EnqueueKey(int key_code);

View File

@@ -20,6 +20,7 @@
#include <string.h> #include <string.h>
#include <string> #include <string>
#include <vector>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/strings.h> #include <android-base/strings.h>
@@ -88,13 +89,12 @@ void WearRecoveryUI::update_progress_locked() {
void WearRecoveryUI::SetStage(int /* current */, int /* max */) {} void WearRecoveryUI::SetStage(int /* current */, int /* max */) {}
void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items, void WearRecoveryUI::StartMenu(const std::vector<std::string>& headers,
int initial_selection) { const std::vector<std::string>& items, size_t initial_selection) {
pthread_mutex_lock(&updateMutex); pthread_mutex_lock(&updateMutex);
if (text_rows_ > 0 && text_cols_ > 0) { if (text_rows_ > 0 && text_cols_ > 0) {
menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_ - kMenuUnusableRows - 1, menu_ = std::make_unique<Menu>(scrollable_menu_, text_rows_ - kMenuUnusableRows - 1,
text_cols_ - 1, headers, items, initial_selection); text_cols_ - 1, headers, items, initial_selection);
update_screen_locked(); update_screen_locked();
} }
pthread_mutex_unlock(&updateMutex); pthread_mutex_unlock(&updateMutex);

View File

@@ -17,6 +17,9 @@
#ifndef RECOVERY_WEAR_UI_H #ifndef RECOVERY_WEAR_UI_H
#define RECOVERY_WEAR_UI_H #define RECOVERY_WEAR_UI_H
#include <string>
#include <vector>
#include "screen_ui.h" #include "screen_ui.h"
class WearRecoveryUI : public ScreenRecoveryUI { class WearRecoveryUI : public ScreenRecoveryUI {
@@ -33,8 +36,8 @@ class WearRecoveryUI : public ScreenRecoveryUI {
// Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen.
const int kMenuUnusableRows; const int kMenuUnusableRows;
void StartMenu(const char* const* headers, const char* const* items, void StartMenu(const std::vector<std::string>& headers, const std::vector<std::string>& items,
int initial_selection) override; size_t initial_selection) override;
int GetProgressBaseline() const override; int GetProgressBaseline() const override;