summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Marshall <tdm@cyngn.com>2014-03-27 09:18:00 -0700
committerTom Marshall <tdm@cyngn.com>2015-11-25 15:35:22 -0800
commit8e614b89838dda1adff952c0bfbb02721bb5db2b (patch)
tree1424980ad0021219328b3779eee9bae9380d9bc6
parenta48fd33655b2d95c250f033eaf88999cfb66e8ea (diff)
downloadbootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.zip
bootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.tar.gz
bootable_recovery-8e614b89838dda1adff952c0bfbb02721bb5db2b.tar.bz2
sr: Touch UI
Change-Id: I4ee87f3474aec0496c47bb561ddecc74e151cbbf
-rw-r--r--Android.mk7
-rw-r--r--device.cpp4
-rw-r--r--device.h2
-rw-r--r--minui/graphics.cpp43
-rw-r--r--minui/minui.h1
-rw-r--r--recovery.cpp39
-rw-r--r--res-hdpi/images/font_log.pngbin0 -> 13408 bytes
-rw-r--r--res-hdpi/images/font_menu.pngbin0 -> 22354 bytes
-rw-r--r--res-hdpi/images/icon_header.pngbin0 -> 3288 bytes
-rw-r--r--res-mdpi/images/font_log.pngbin0 -> 7287 bytes
-rw-r--r--res-mdpi/images/font_menu.pngbin0 -> 13408 bytes
-rw-r--r--res-mdpi/images/icon_header.pngbin0 -> 2191 bytes
-rw-r--r--res-xhdpi/images/font_log.pngbin0 -> 22354 bytes
-rw-r--r--res-xhdpi/images/font_menu.pngbin0 -> 17287 bytes
-rw-r--r--res-xhdpi/images/icon_header.pngbin0 -> 5579 bytes
-rw-r--r--res-xxhdpi/images/font_log.pngbin0 -> 17287 bytes
-rw-r--r--res-xxhdpi/images/font_menu.pngbin0 -> 65852 bytes
-rw-r--r--res-xxhdpi/images/icon_header.pngbin0 -> 8357 bytes
-rw-r--r--res-xxxhdpi/images/font_log.pngbin0 -> 60087 bytes
-rw-r--r--res-xxxhdpi/images/font_menu.pngbin0 -> 101284 bytes
-rw-r--r--res-xxxhdpi/images/icon_header.pngbin0 -> 11760 bytes
-rw-r--r--screen_ui.cpp220
-rw-r--r--screen_ui.h23
-rw-r--r--ui.cpp426
-rw-r--r--ui.h93
-rw-r--r--verifier_test.cpp7
-rw-r--r--wear_ui.cpp8
-rw-r--r--wear_ui.h2
28 files changed, 750 insertions, 125 deletions
diff --git a/Android.mk b/Android.mk
index f7fbfd8..6b8177b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -112,6 +112,13 @@ endif
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+# Handling for EV_REL is disabled by default because some accelerometers
+# send EV_REL events. Actual EV_REL devices are rare on modern hardware
+# so it's cleaner just to disable it by default.
+ifneq ($(BOARD_RECOVERY_NEEDS_REL_INPUT),)
+ LOCAL_CFLAGS += -DBOARD_RECOVERY_NEEDS_REL_INPUT
+endif
+
ifeq ($(TARGET_USE_MDTP), true)
LOCAL_CFLAGS += -DUSE_MDTP
endif
diff --git a/device.cpp b/device.cpp
index 1f6bac7..c8b7954 100644
--- a/device.cpp
+++ b/device.cpp
@@ -55,6 +55,10 @@ int Device::HandleMenuKey(int key, int visible) {
return kNoAction;
}
+ if (key & KEY_FLAG_ABS) {
+ return key;
+ }
+
switch (key) {
case KEY_RIGHTSHIFT:
case KEY_DOWN:
diff --git a/device.h b/device.h
index c11f52e..1241982 100644
--- a/device.h
+++ b/device.h
@@ -19,6 +19,8 @@
#include "ui.h"
+#define KEY_FLAG_ABS 0x8000
+
class Device : public VoldWatcher {
public:
Device(RecoveryUI* ui) : ui_(ui) { }
diff --git a/minui/graphics.cpp b/minui/graphics.cpp
index 43c28ce..1f25531 100644
--- a/minui/graphics.cpp
+++ b/minui/graphics.cpp
@@ -41,6 +41,20 @@ struct GRFont {
int cheight;
};
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+typedef struct {
+ char name[80];
+ GRFont* font;
+} font_item;
+
+static font_item gr_fonts[] = {
+ { "menu", NULL },
+ { "log", NULL },
+};
+
static GRFont* gr_font = NULL;
static minui_backend* gr_backend = NULL;
@@ -261,6 +275,17 @@ void gr_fill(int x1, int y1, int x2, int y2)
}
}
+void gr_set_font(const char* name)
+{
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(gr_fonts); ++idx) {
+ if (strcmp(name, gr_fonts[idx].name) == 0) {
+ gr_font = gr_fonts[idx].font;
+ break;
+ }
+ }
+}
+
void gr_blit(GRSurface* source, int sx, int sy, int w, int h, int dx, int dy) {
if (source == NULL) return;
@@ -299,11 +324,14 @@ unsigned int gr_get_height(GRSurface* surface) {
return surface->height;
}
-static void gr_init_font(void)
+static void gr_init_one_font(int idx)
{
- gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+ char name[80];
+ GRFont* gr_font = reinterpret_cast<GRFont*>(calloc(sizeof(*gr_font), 1));
+ snprintf(name, sizeof(name), "font_%s", gr_fonts[idx].name);
+ gr_fonts[idx].font = gr_font;
- int res = res_create_alpha_surface("font", &(gr_font->texture));
+ int res = res_create_alpha_surface(name, &(gr_font->texture));
if (res == 0) {
// The font image should be a 96x2 array of character images. The
// columns are the printable ASCII characters 0x20 - 0x7f. The
@@ -335,6 +363,15 @@ static void gr_init_font(void)
}
}
+static void gr_init_font()
+{
+ unsigned int idx;
+ for (idx = 0; idx < ARRAY_SIZE(gr_fonts); ++idx) {
+ gr_init_one_font(idx);
+ }
+ gr_font = gr_fonts[0].font;
+}
+
#if 0
// Exercises many of the gr_*() functions; useful for testing.
static void gr_test() {
diff --git a/minui/minui.h b/minui/minui.h
index 7dec2d7..063fc86 100644
--- a/minui/minui.h
+++ b/minui/minui.h
@@ -45,6 +45,7 @@ void gr_fb_blank(bool blank);
void gr_clear(); // clear entire surface to current color
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x1, int y1, int x2, int y2);
+void gr_set_font(const char* name);
void gr_text(int x, int y, const char *s, bool bold);
void gr_texticon(int x, int y, GRSurface* icon);
int gr_measure(const char *s);
diff --git a/recovery.cpp b/recovery.cpp
index d3b1aec..f260854 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -644,6 +644,11 @@ get_menu_selection(const char* const * headers, const char* const * items,
// accidentally trigger menu items.
ui->FlushKeys();
+ // Count items to detect valid values for absolute selection
+ int item_count = 0;
+ while (items[item_count] != NULL)
+ ++item_count;
+
ui->StartMenu(headers, items, initial_selection);
int selected = initial_selection;
int chosen_item = -1;
@@ -669,6 +674,20 @@ get_menu_selection(const char* const * headers, const char* const * items,
int action = device->HandleMenuKey(key, visible);
+ if (action >= 0) {
+ if ((action & ~KEY_FLAG_ABS) >= item_count) {
+ action = Device::kNoAction;
+ }
+ else {
+ // Absolute selection. Update selected item and give some
+ // feedback in the UI by selecting the item for a short time.
+ selected = action & ~KEY_FLAG_ABS;
+ action = Device::kInvokeItem;
+ selected = ui->SelectMenu(selected, true);
+ usleep(50*1000);
+ }
+ }
+
if (action < 0) {
switch (action) {
case Device::kHighlightUp:
@@ -851,8 +870,15 @@ static bool wipe_cache(bool should_confirm, Device* device) {
modified_flash = true;
ui->Print("\n-- Wiping cache...\n");
+ ui->DialogShowInfo("Wiping cache ...");
bool success = erase_volume("/cache");
ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+ if (!should_confirm || success) {
+ ui->DialogDismiss();
+ }
+ else {
+ ui->DialogShowErrorLog("Cache wipe failed");
+ }
return success;
}
@@ -928,6 +954,9 @@ static int apply_from_storage(Device* device, const std::string& id, bool* wipe_
return INSTALL_NONE;
}
+ ui->ClearText();
+ ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
+
ui->Print("\n-- Install %s ...\n", path);
set_sdcard_update_bootloader_message();
void* token = start_sdcard_fuse(path);
@@ -995,6 +1024,10 @@ refresh:
status = apply_from_storage(device, id, &wipe_cache);
}
+ if (status != INSTALL_SUCCESS && status != INSTALL_NONE) {
+ ui->DialogShowErrorLog("Install failed");
+ }
+
return status;
}
@@ -1016,7 +1049,7 @@ prompt_and_wait(Device* device, int status) {
case INSTALL_ERROR:
case INSTALL_CORRUPT:
- ui->SetBackground(RecoveryUI::ERROR);
+ ui->SetBackground(RecoveryUI::D_ERROR);
break;
}
ui->SetProgressType(RecoveryUI::EMPTY);
@@ -1067,7 +1100,7 @@ prompt_and_wait(Device* device, int status) {
if (status >= 0 && status != INSTALL_NONE) {
if (status != INSTALL_SUCCESS) {
- ui->SetBackground(RecoveryUI::ERROR);
+ ui->SetBackground(RecoveryUI::D_ERROR);
ui->Print("Installation aborted.\n");
copy_logs();
} else if (!ui->IsTextVisible()) {
@@ -1455,7 +1488,7 @@ main(int argc, char **argv) {
if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) {
copy_logs();
- ui->SetBackground(RecoveryUI::ERROR);
+ ui->SetBackground(RecoveryUI::D_ERROR);
}
Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT;
diff --git a/res-hdpi/images/font_log.png b/res-hdpi/images/font_log.png
new file mode 100644
index 0000000..59b990a
--- /dev/null
+++ b/res-hdpi/images/font_log.png
Binary files differ
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/icon_header.png b/res-hdpi/images/icon_header.png
new file mode 100644
index 0000000..634489f
--- /dev/null
+++ b/res-hdpi/images/icon_header.png
Binary files differ
diff --git a/res-mdpi/images/font_log.png b/res-mdpi/images/font_log.png
new file mode 100644
index 0000000..bcbe242
--- /dev/null
+++ b/res-mdpi/images/font_log.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/icon_header.png b/res-mdpi/images/icon_header.png
new file mode 100644
index 0000000..4ddb812
--- /dev/null
+++ b/res-mdpi/images/icon_header.png
Binary files differ
diff --git a/res-xhdpi/images/font_log.png b/res-xhdpi/images/font_log.png
new file mode 100644
index 0000000..d1aad6d
--- /dev/null
+++ b/res-xhdpi/images/font_log.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/icon_header.png b/res-xhdpi/images/icon_header.png
new file mode 100644
index 0000000..3254528
--- /dev/null
+++ b/res-xhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxhdpi/images/font_log.png b/res-xxhdpi/images/font_log.png
new file mode 100644
index 0000000..6aead73
--- /dev/null
+++ b/res-xxhdpi/images/font_log.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/icon_header.png b/res-xxhdpi/images/icon_header.png
new file mode 100644
index 0000000..4b68cbc
--- /dev/null
+++ b/res-xxhdpi/images/icon_header.png
Binary files differ
diff --git a/res-xxxhdpi/images/font_log.png b/res-xxxhdpi/images/font_log.png
new file mode 100644
index 0000000..ec8d899
--- /dev/null
+++ b/res-xxxhdpi/images/font_log.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/icon_header.png b/res-xxxhdpi/images/icon_header.png
new file mode 100644
index 0000000..b4aebd7
--- /dev/null
+++ b/res-xxxhdpi/images/icon_header.png
Binary files differ
diff --git a/screen_ui.cpp b/screen_ui.cpp
index a64bad6..464e77d 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -40,9 +40,6 @@
#include "screen_ui.h"
#include "ui.h"
-static int char_width;
-static int char_height;
-
// Return the current time as a double (including fractions of a second).
static double now() {
struct timeval tv;
@@ -60,6 +57,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
progressScopeSize(0),
progress(0),
pagesIdentical(false),
+ log_text_cols_(0),
+ log_text_rows_(0),
text_cols_(0),
text_rows_(0),
text_(nullptr),
@@ -70,6 +69,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
show_text_ever(false),
dialog_icon(NONE),
dialog_text(nullptr),
+ dialog_show_log(false),
menu_(nullptr),
show_menu(false),
menu_items(0),
@@ -82,6 +82,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() :
rainbow(false),
wrap_count(0) {
+ headerIcon = nullptr;
for (int i = 0; i < NR_ICONS; i++) {
backgroundIcon[i] = nullptr;
}
@@ -136,7 +137,7 @@ void ScreenRecoveryUI::draw_background_locked(Icon icon) {
// Draw the progress bar (if any) on the screen. Does not flip pages.
// Should only be called with updateMutex locked.
void ScreenRecoveryUI::draw_progress_locked() {
- if (currentIcon == ERROR) return;
+ if (currentIcon == D_ERROR) return;
if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) {
GRSurface* icon = installation[installingFrame];
@@ -222,7 +223,7 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) {
void ScreenRecoveryUI::DrawTextLine(int* y, const char* line, bool bold) {
gr_text(4, *y, line, bold);
- *y += char_height + 4;
+ *y += char_height_ + 4;
}
void ScreenRecoveryUI::DrawTextLines(int* y, const char* const* lines) {
@@ -242,21 +243,73 @@ static const char* LONG_PRESS_HELP[] = {
NULL
};
+int ScreenRecoveryUI::draw_header_icon()
+{
+ GRSurface* surface = headerIcon;
+ int iw = header_width_;
+ int ih = header_height_;
+ int ix = (gr_fb_width() - iw) / 2;
+ int iy = 0;
+ gr_blit(surface, 0, 0, iw, ih, ix, iy);
+ return ih;
+}
+
+void ScreenRecoveryUI::draw_menu_item(int textrow, const char *text, int selected)
+{
+ if (selected) {
+ SetColor(MENU_SEL_BG);
+ gr_fill(0, (textrow) * char_height_,
+ gr_fb_width(), (textrow+3) * char_height_ - 1);
+ SetColor(MENU_SEL_FG);
+ gr_text(4, (textrow+1) * char_height_ - 1, text, 0);
+ SetColor(MENU);
+ }
+ else {
+ SetColor(MENU);
+ gr_text(4, (textrow+1) * char_height_ - 1, text, 0);
+ }
+}
+
void ScreenRecoveryUI::draw_dialog()
{
int x, y, w, h;
- draw_background_locked(dialog_icon);
+ if (dialog_show_log) {
+ draw_background_locked(NONE);
+ }
+ else {
+ draw_background_locked(dialog_icon);
+ }
+ draw_header_icon();
int iconHeight = gr_get_height(backgroundIcon[dialog_icon]);
- x = (gr_fb_width()/2 - (char_width*strlen(dialog_text))/2);
- y = (gr_fb_height()/2 + iconHeight/2);
+ x = (gr_fb_width()/2 - (char_width_ * strlen(dialog_text))/2);
+ if (dialog_show_log) {
+ y = gr_get_height(headerIcon) + char_height_;
+ }
+ else {
+ y = (gr_fb_height()/2 + iconHeight/2);
+ }
SetColor(ERROR_TEXT);
gr_text(x, y, dialog_text, 0);
+ y += char_height_ + 2;
+
+ if (dialog_show_log) {
+ int cx, cy;
+ gr_set_font("log");
+ gr_font_size(&cx, &cy);
- if (dialog_icon == ERROR) {
+ size_t row;
+ for (row = 0; row < log_text_rows_; ++row) {
+ gr_text(2, y, text_[row], 0);
+ y += cy + 2;
+ }
+ gr_set_font("menu");
+ }
+
+ if (dialog_icon == D_ERROR) {
/*
* This could be improved...
*
@@ -266,10 +319,10 @@ void ScreenRecoveryUI::draw_dialog()
* Rect width 4px
* Rect padding 8px
*/
- w = char_width*4;
- h = char_height;
+ w = char_width_ * 4;
+ h = char_height_;
x = gr_fb_width()/2 - w/2;
- y = gr_fb_height() - h - 4*char_height;
+ y = gr_fb_height() - h - 4 * char_height_;
SetColor(HEADER);
gr_fill(x-(4+8), y-(4+8), x+w+(4+8), y+h+(4+8));
SetColor(MENU_SEL_BG);
@@ -291,57 +344,42 @@ void ScreenRecoveryUI::draw_screen_locked() {
draw_dialog();
return;
}
-
gr_color(0, 0, 0, 255);
gr_clear();
- int y = 0;
- if (show_menu) {
- char recovery_fingerprint[PROPERTY_VALUE_MAX];
- property_get("ro.bootimage.build.fingerprint", recovery_fingerprint, "");
-
- SetColor(INFO);
- DrawTextLine(&y, "Android Recovery", true);
- for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) {
- DrawTextLine(&y, chunk.c_str(), false);
+ if (currentIcon == INSTALLING_UPDATE) {
+ size_t y = header_height_ + 4;
+
+ draw_background_locked(currentIcon);
+
+ SetColor(LOG);
+ int cx, cy;
+ gr_set_font("log");
+ gr_font_size(&cx, &cy);
+ // display from the bottom up, until we hit the top of the
+ // screen or we've displayed the entire text buffer.
+ size_t ty, count;
+ int row = (text_first_row_ + log_text_rows_ - 1) % log_text_rows_;
+ for (ty = gr_fb_height() - cy, count = 0;
+ ty > y + 2 && count < log_text_rows_;
+ ty -= (cy + 2), ++count) {
+ gr_text(4, ty, text_[row], 0);
+ --row;
+ if (row < 0) row = log_text_rows_ - 1;
}
- DrawTextLines(&y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP);
-
- SetColor(HEADER);
- DrawTextLines(&y, menu_headers_);
-
- SetColor(MENU);
- DrawHorizontalRule(&y);
- 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);
- gr_fill(0, y - 2, gr_fb_width(), y + char_height + 2);
- // Bold white text for the selected item.
- SetColor(MENU_SEL_FG);
- gr_text(4, y, menu_[i], true);
- SetColor(MENU);
- } else {
- gr_text(4, y, menu_[i], false);
- }
- y += char_height + 4;
- }
- DrawHorizontalRule(&y);
+ gr_set_font("menu");
+ return;
}
- // 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() - char_height;
- ty >= y && count < text_rows_;
- ty -= char_height, ++count) {
- gr_text(0, ty, text_[row], false);
- --row;
- if (row < 0) row = text_rows_ - 1;
+ if (show_menu) {
+ draw_header_icon();
+ int nr_items = menu_items - menu_show_start_;
+ if (nr_items > max_menu_rows_)
+ nr_items = max_menu_rows_;
+ for (int i = 0; i < nr_items; ++i) {
+ draw_menu_item(text_first_row_ + 3*i, menu_[menu_show_start_ + i],
+ ((menu_show_start_ + i) == menu_sel));
+ }
}
}
}
@@ -449,9 +487,15 @@ static char** Alloc2d(size_t rows, size_t cols) {
void ScreenRecoveryUI::Init() {
gr_init();
- gr_font_size(&char_width, &char_height);
- text_rows_ = gr_fb_height() / char_height;
- text_cols_ = gr_fb_width() / char_width;
+ gr_set_font("log");
+ gr_font_size(&log_char_width_, &log_char_height_);
+ gr_set_font("menu");
+ gr_font_size(&char_width_, &char_height_);
+ text_rows_ = gr_fb_height() / char_height_;
+ text_cols_ = gr_fb_width() / char_width_;
+
+ log_text_rows_ = gr_fb_height() / log_char_height_;
+ log_text_cols_ = gr_fb_width() / log_char_width_;
text_ = Alloc2d(text_rows_, text_cols_ + 1);
file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1);
@@ -460,13 +504,21 @@ void ScreenRecoveryUI::Init() {
text_col_ = text_row_ = 0;
text_top_ = 1;
+ LoadBitmap("icon_header", &headerIcon);
+ header_height_ = gr_get_height(headerIcon);
+ header_width_ = gr_get_width(headerIcon);
+
+ text_first_row_ = (header_height_ / char_height_) + 1;
+ menu_item_start_ = text_first_row_ * char_height_;
+ max_menu_rows_ = (text_rows_ - text_first_row_) / 3;
+
backgroundIcon[NONE] = nullptr;
LoadBitmapArray("icon_installing", &installing_frames, &installation);
backgroundIcon[INSTALLING_UPDATE] = installing_frames ? installation[0] : nullptr;
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
- LoadBitmap("icon_info", &backgroundIcon[INFO]);
- LoadBitmap("icon_error", &backgroundIcon[ERROR]);
- backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+ LoadBitmap("icon_info", &backgroundIcon[D_INFO]);
+ LoadBitmap("icon_error", &backgroundIcon[D_ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[D_ERROR];
LoadBitmap("progress_empty", &progressBarEmpty);
LoadBitmap("progress_fill", &progressBarFill);
@@ -476,7 +528,7 @@ void ScreenRecoveryUI::Init() {
LoadLocalizedBitmap("installing_text", &backgroundText[INSTALLING_UPDATE]);
LoadLocalizedBitmap("erasing_text", &backgroundText[ERASING]);
LoadLocalizedBitmap("no_command_text", &backgroundText[NO_COMMAND]);
- LoadLocalizedBitmap("error_text", &backgroundText[ERROR]);
+ LoadLocalizedBitmap("error_text", &backgroundText[D_ERROR]);
pthread_create(&progress_thread_, nullptr, ProgressThreadStartRoutine, this);
@@ -574,13 +626,13 @@ void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap)
}
pthread_mutex_lock(&updateMutex);
- if (text_rows_ > 0 && text_cols_ > 0) {
+ if (log_text_rows_ > 0 && log_text_cols_ > 0) {
for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) {
- if (*ptr == '\n' || text_col_ >= text_cols_) {
+ if (*ptr == '\n' || text_col_ >= log_text_cols_) {
text_[text_row_][text_col_] = '\0';
text_col_ = 0;
- text_row_ = (text_row_ + 1) % text_rows_;
- if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_;
+ text_row_ = (text_row_ + 1) % log_text_rows_;
+ if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % log_text_rows_;
}
if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr;
}
@@ -707,7 +759,8 @@ void ScreenRecoveryUI::DialogShowInfo(const char* text)
pthread_mutex_lock(&updateMutex);
free(dialog_text);
dialog_text = strdup(text);
- dialog_icon = INFO;
+ dialog_show_log = false;
+ dialog_icon = D_INFO;
update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -717,7 +770,19 @@ void ScreenRecoveryUI::DialogShowError(const char* text)
pthread_mutex_lock(&updateMutex);
free(dialog_text);
dialog_text = strdup(text);
- dialog_icon = ERROR;
+ dialog_show_log = false;
+ dialog_icon = D_ERROR;
+ update_screen_locked();
+ pthread_mutex_unlock(&updateMutex);
+}
+
+void ScreenRecoveryUI::DialogShowErrorLog(const char* text)
+{
+ pthread_mutex_lock(&updateMutex);
+ free(dialog_text);
+ dialog_text = strdup(text);
+ dialog_show_log = true;
+ dialog_icon = D_ERROR;
update_screen_locked();
pthread_mutex_unlock(&updateMutex);
}
@@ -749,9 +814,12 @@ void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const
pthread_mutex_unlock(&updateMutex);
}
-int ScreenRecoveryUI::SelectMenu(int sel) {
+int ScreenRecoveryUI::SelectMenu(int sel, bool abs /* = false */) {
int wrapped = 0;
pthread_mutex_lock(&updateMutex);
+ if (abs) {
+ sel += menu_show_start_;
+ }
if (show_menu) {
int old_sel = menu_sel;
menu_sel = sel;
@@ -766,11 +834,17 @@ int ScreenRecoveryUI::SelectMenu(int sel) {
}
if (menu_sel < 0) {
wrapped = -1;
- menu_sel = menu_items - 1;
+ menu_sel = menu_items + menu_sel;
}
if (menu_sel >= menu_items) {
wrapped = 1;
- menu_sel = 0;
+ menu_sel = menu_sel - menu_items;
+ }
+ if (menu_sel < menu_show_start_ && menu_show_start_ > 0) {
+ menu_show_start_ = menu_sel;
+ }
+ if (menu_sel - menu_show_start_ >= max_menu_rows_) {
+ menu_show_start_ = menu_sel - max_menu_rows_ + 1;
}
sel = menu_sel;
if (wrapped != 0) {
diff --git a/screen_ui.h b/screen_ui.h
index e198503..fff0f0e 100644
--- a/screen_ui.h
+++ b/screen_ui.h
@@ -54,14 +54,17 @@ class ScreenRecoveryUI : public RecoveryUI {
void DialogShowInfo(const char* text);
void DialogShowError(const char* text);
+ void DialogShowErrorLog(const char* text);
int DialogShowing() const { return (dialog_text != NULL); }
- bool DialogDismissable() const { return (dialog_icon == ERROR); }
+ bool DialogDismissable() const { return (dialog_icon == D_ERROR); }
void DialogDismiss();
// menu display
+ virtual int MenuItemStart() const { return menu_item_start_; }
+ virtual int MenuItemHeight() const { return 3 * char_height_; }
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
- int SelectMenu(int sel);
+ int SelectMenu(int sel, bool abs = false);
void EndMenu();
void KeyLongPress(int);
@@ -80,6 +83,7 @@ class ScreenRecoveryUI : public RecoveryUI {
bool rtl_locale;
pthread_mutex_t updateMutex;
+ GRSurface* headerIcon;
GRSurface* backgroundIcon[NR_ICONS];
GRSurface* backgroundText[NR_ICONS];
GRSurface** installation;
@@ -96,6 +100,7 @@ class ScreenRecoveryUI : public RecoveryUI {
// true when both graphics pages are the same (except for the progress bar).
bool pagesIdentical;
+ size_t log_text_cols_, log_text_rows_;
size_t text_cols_, text_rows_;
// Log text overlay, displayed when a magic key is pressed.
@@ -107,15 +112,21 @@ class ScreenRecoveryUI : public RecoveryUI {
Icon dialog_icon;
char *dialog_text;
+ bool dialog_show_log;
char** menu_;
const char* const* menu_headers_;
bool show_menu;
int menu_items, menu_sel;
+ int menu_show_start_;
+ int max_menu_rows_;
+
// An alternate text screen, swapped with 'text_' when we're viewing a log file.
char** file_viewer_text_;
+ int menu_item_start_;
+
pthread_t progress_thread_;
int animation_fps;
@@ -128,8 +139,16 @@ class ScreenRecoveryUI : public RecoveryUI {
bool rainbow;
int wrap_count;
+ int log_char_height_, log_char_width_;
+ int char_height_, char_width_;
+
+ int header_height_, header_width_;
+ int text_first_row_;
+
void draw_background_locked(Icon icon);
void draw_progress_locked();
+ int draw_header_icon();
+ void draw_menu_item(int textrow, const char *text, int selected);
void draw_dialog();
void draw_screen_locked();
void update_screen_locked();
diff --git a/ui.cpp b/ui.cpp
index a5896ce..1594580 100644
--- a/ui.cpp
+++ b/ui.cpp
@@ -31,6 +31,7 @@
#include <cutils/properties.h>
#include <cutils/android_reboot.h>
+#include <cutils/properties.h>
#include "common.h"
#include "roots.h"
@@ -45,6 +46,11 @@
#define UI_WAIT_KEY_TIMEOUT_SEC 120
+/* Some extra input defines */
+#ifndef ABS_MT_ANGLE
+#define ABS_MT_ANGLE 0x38
+#endif
+
static int string_split(char* s, char** fields, int maxfields)
{
int n = 0;
@@ -62,9 +68,14 @@ static int string_split(char* s, char** fields, int maxfields)
return n+1;
}
+struct ms_info {
+ RecoveryUI* ui;
+ MessageSocket* sock;
+};
+
static int message_socket_client_event(int fd, uint32_t epevents, void *data)
{
- MessageSocket* client = (MessageSocket*)data;
+ ms_info* info = (ms_info*)data;
printf("message_socket client event\n");
if (!(epevents & EPOLLIN)) {
@@ -73,12 +84,13 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
char buf[256];
ssize_t nread;
- nread = client->Read(buf, sizeof(buf));
+ nread = info->sock->Read(buf, sizeof(buf));
if (nread <= 0) {
ev_del_fd(fd);
- self->DialogDismiss();
- client->Close();
- delete client;
+ info->ui->DialogDismiss();
+ info->sock->Close();
+ delete info->sock;
+ delete info;
return 0;
}
@@ -96,10 +108,10 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
printf("field[0]=%s, field[1]=%s\n", fields[0], fields[1]);
if (strcmp(fields[0], "dialog") == 0) {
if (strcmp(fields[1], "show") == 0 && nfields > 2) {
- self->DialogShowInfo(fields[2]);
+ info->ui->DialogShowInfo(fields[2]);
}
if (strcmp(fields[1], "dismiss") == 0) {
- self->DialogDismiss();
+ info->ui->DialogDismiss();
}
}
@@ -108,12 +120,15 @@ static int message_socket_client_event(int fd, uint32_t epevents, void *data)
static int message_socket_listen_event(int fd, uint32_t epevents, void *data)
{
- MessageSocket* ms = (MessageSocket*)data;
- MessageSocket* client = ms->Accept();
+ ms_info* info = (ms_info*)data;
+ MessageSocket* sock = info->sock->Accept();
printf("message_socket_listen_event: event on %d\n", fd);
- if (client) {
+ if (sock) {
printf("message_socket client connected\n");
- ev_add_fd(client->fd(), message_socket_client_event, client);
+ ms_info* clientinfo = new ms_info;
+ clientinfo->ui = info->ui;
+ clientinfo->sock = sock;
+ ev_add_fd(sock->fd(), message_socket_client_event, clientinfo);
}
return 0;
}
@@ -160,10 +175,14 @@ static void* InputThreadLoop(void*) {
}
void RecoveryUI::Init() {
+ calibrate_swipe();
ev_init(InputCallback, this);
message_socket.ServerInit();
- ev_add_fd(message_socket.fd(), message_socket_listen_event, &message_socket);
+ ms_info* info = new ms_info;
+ info->ui = this;
+ info->sock = &message_socket;
+ ev_add_fd(message_socket.fd(), message_socket_listen_event, info);
ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1));
@@ -176,31 +195,45 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
return -1;
}
- if (ev.type == EV_SYN) {
- return 0;
- } else if (ev.type == EV_REL) {
- if (ev.code == REL_Y) {
- // accumulate the up or down motion reported by
- // the trackball. When it exceeds a threshold
- // (positive or negative), fake an up/down
- // key event.
- rel_sum += ev.value;
- if (rel_sum > 3) {
- ProcessKey(KEY_DOWN, 1); // press down key
- ProcessKey(KEY_DOWN, 0); // and release it
- rel_sum = 0;
- } else if (rel_sum < -3) {
- ProcessKey(KEY_UP, 1); // press up key
- ProcessKey(KEY_UP, 0); // and release it
- rel_sum = 0;
- }
+ input_device* dev = NULL;
+ int n;
+ for (n = 0; n < MAX_NR_INPUT_DEVICES; ++n) {
+ if (input_devices[n].fd == fd) {
+ dev = &input_devices[n];
+ break;
}
- } else {
- rel_sum = 0;
+ if (input_devices[n].fd == -1) {
+ dev = &input_devices[n];
+ memset(dev, 0, sizeof(input_device));
+ dev->fd = fd;
+ dev->tracking_id = -1;
+ calibrate_touch(dev);
+ setup_vkeys(dev);
+ break;
+ }
+ }
+ if (!dev) {
+ LOGE("input_callback: no more available input devices\n");
+ return -1;
+ }
+
+ if (ev.type != EV_REL) {
+ dev->rel_sum = 0;
}
- if (ev.type == EV_KEY && ev.code <= KEY_MAX) {
- ProcessKey(ev.code, ev.value);
+ switch (ev.type) {
+ case EV_SYN:
+ ProcessSyn(dev, ev.code, ev.value);
+ break;
+ case EV_ABS:
+ ProcessAbs(dev, ev.code, ev.value);
+ break;
+ case EV_REL:
+ ProcessRel(dev, ev.code, ev.value);
+ break;
+ case EV_KEY:
+ ProcessKey(dev, ev.code, ev.value);
+ break;
}
return 0;
@@ -218,11 +251,14 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) {
// a key is registered.
//
// updown == 1 for key down events; 0 for key up events
-void RecoveryUI::ProcessKey(int key_code, int updown) {
+void RecoveryUI::ProcessKey(input_device* dev, int key_code, int updown) {
bool register_key = false;
bool long_press = false;
bool reboot_enabled;
+ if (key_code > KEY_MAX)
+ return;
+
pthread_mutex_lock(&key_queue_mutex);
key_pressed[key_code] = updown;
if (updown) {
@@ -272,6 +308,146 @@ void RecoveryUI::ProcessKey(int key_code, int updown) {
}
}
+void RecoveryUI::ProcessSyn(input_device* dev, int code, int value) {
+ /*
+ * Type A device release:
+ * 1. Lack of position update
+ * 2. BTN_TOUCH | ABS_PRESSURE | SYN_MT_REPORT
+ * 3. SYN_REPORT
+ *
+ * Type B device release:
+ * 1. ABS_MT_TRACKING_ID == -1 for "first" slot
+ * 2. SYN_REPORT
+ */
+
+ if (code == SYN_MT_REPORT) {
+ if (!dev->in_touch && (dev->saw_pos_x && dev->saw_pos_y)) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a press\n");
+#endif
+ handle_press(dev);
+ }
+ dev->saw_mt_report = true;
+ return;
+ }
+ if (code == SYN_REPORT) {
+ if (dev->in_touch) {
+ handle_gestures(dev);
+ }
+ else {
+ if (dev->saw_tracking_id) {
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b press\n");
+#endif
+ handle_press(dev);
+ }
+ }
+
+ /* Detect release */
+ if (dev->saw_mt_report) {
+ if (dev->in_touch && !dev->saw_pos_x && !dev->saw_pos_y) {
+ /* type A release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type a release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+ else {
+ if (dev->in_touch && dev->saw_tracking_id && dev->tracking_id == -1 &&
+ dev->slot_current == dev->slot_first) {
+ /* type B release */
+#ifdef DEBUG_TOUCH
+ LOGI("process_syn: type b release\n");
+#endif
+ handle_release(dev);
+ dev->slot_first = 0;
+ }
+ }
+
+ dev->saw_pos_x = dev->saw_pos_y = false;
+ dev->saw_mt_report = dev->saw_tracking_id = false;
+ }
+}
+
+void RecoveryUI::ProcessAbs(input_device* dev, int code, int value) {
+ if (code == ABS_MT_SLOT) {
+ dev->slot_current = value;
+ if (dev->slot_first == -1) {
+ dev->slot_first = value;
+ }
+ return;
+ }
+ if (code == ABS_MT_TRACKING_ID) {
+ /*
+ * Some devices send an initial ABS_MT_SLOT event before switching
+ * to type B events, so discard any type A state related to slot.
+ */
+ dev->saw_tracking_id = true;
+ dev->slot_first = dev->slot_current = 0;
+
+ if (value != dev->tracking_id) {
+ dev->tracking_id = value;
+ if (dev->tracking_id < 0) {
+ dev->slot_nr_active--;
+ }
+ else {
+ dev->slot_nr_active++;
+ }
+ }
+ return;
+ }
+ /*
+ * For type A devices, we "lock" onto the first coordinates by ignoring
+ * position updates from the time we see a SYN_MT_REPORT until the next
+ * SYN_REPORT
+ *
+ * For type B devices, we "lock" onto the first slot seen until all slots
+ * are released
+ */
+ if (dev->slot_nr_active == 0) {
+ /* type A */
+ if (dev->saw_pos_x && dev->saw_pos_y) {
+ return;
+ }
+ }
+ else {
+ if (dev->slot_current != dev->slot_first) {
+ return;
+ }
+ }
+ if (code == ABS_MT_POSITION_X) {
+ dev->saw_pos_x = true;
+ dev->touch_pos.x = value * fb_dimensions.x / (dev->touch_max.x - dev->touch_min.x);
+ }
+ else if (code == ABS_MT_POSITION_Y) {
+ dev->saw_pos_y = true;
+ dev->touch_pos.y = value * fb_dimensions.y / (dev->touch_max.y - dev->touch_min.y);
+ }
+}
+
+void RecoveryUI::ProcessRel(input_device* dev, int code, int value) {
+#ifdef BOARD_RECOVERY_NEEDS_REL_INPUT
+ if (code == REL_Y) {
+ // accumulate the up or down motion reported by
+ // the trackball. When it exceeds a threshold
+ // (positive or negative), fake an up/down
+ // key event.
+ dev->rel_sum += value;
+ if (dev->rel_sum > 3) {
+ process_key(dev, KEY_DOWN, 1); // press down key
+ process_key(dev, KEY_DOWN, 0); // and release it
+ dev->rel_sum = 0;
+ } else if (dev->rel_sum < -3) {
+ process_key(dev, KEY_UP, 1); // press up key
+ process_key(dev, KEY_UP, 0); // and release it
+ dev->rel_sum = 0;
+ }
+ }
+#endif
+}
+
void* RecoveryUI::time_key_helper(void* cookie) {
key_timer_t* info = (key_timer_t*) cookie;
info->ui->time_key(info->key_code, info->count);
@@ -290,6 +466,186 @@ void RecoveryUI::time_key(int key_code, int count) {
if (long_press) KeyLongPress(key_code);
}
+void RecoveryUI::calibrate_touch(input_device* dev) {
+ fb_dimensions.x = gr_fb_width();
+ fb_dimensions.y = gr_fb_height();
+
+ struct input_absinfo info;
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_X), &info) == 0) {
+ dev->touch_min.x = info.minimum;
+ dev->touch_max.x = info.maximum;
+ dev->touch_pos.x = info.value;
+ }
+ memset(&info, 0, sizeof(info));
+ if (ioctl(dev->fd, EVIOCGABS(ABS_MT_POSITION_Y), &info) == 0) {
+ dev->touch_min.y = info.minimum;
+ dev->touch_max.y = info.maximum;
+ dev->touch_pos.y = info.value;
+ }
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_touch: fd=%d, (%d,%d)-(%d,%d) pos (%d,%d)\n", dev->fd,
+ dev->touch_min.x, dev->touch_min.y,
+ dev->touch_max.x, dev->touch_max.y,
+ dev->touch_pos.x, dev->touch_pos.y);
+#endif
+}
+
+void RecoveryUI::setup_vkeys(input_device* dev) {
+ int n;
+ char name[256];
+ char path[PATH_MAX];
+ char buf[64*MAX_NR_VKEYS];
+
+ for (n = 0; n < MAX_NR_VKEYS; ++n) {
+ dev->virtual_keys[n].keycode = -1;
+ }
+
+ memset(name, 0, sizeof(name));
+ if (ioctl(dev->fd, EVIOCGNAME(sizeof(name)), name) < 0) {
+ LOGI("setup_vkeys: no vkeys\n");
+ return;
+ }
+ sprintf(path, "/sys/board_properties/virtualkeys.%s", name);
+ int vkfd = open(path, O_RDONLY);
+ if (vkfd < 0) {
+ LOGI("setup_vkeys: could not open %s\n", path);
+ return;
+ }
+ ssize_t len = read(vkfd, buf, sizeof(buf));
+ close(vkfd);
+ if (len <= 0) {
+ LOGE("setup_vkeys: could not read %s\n", path);
+ return;
+ }
+ buf[len] = '\0';
+
+ char* p = buf;
+ char* endp;
+ for (n = 0; n < MAX_NR_VKEYS && p < buf+len && *p == '0'; ++n) {
+ int val[6];
+ int f;
+ for (f = 0; *p && f < 6; ++f) {
+ val[f] = strtol(p, &endp, 0);
+ if (p == endp)
+ break;
+ p = endp+1;
+ }
+ if (f != 6 || val[0] != 0x01)
+ break;
+ dev->virtual_keys[n].keycode = val[1];
+ dev->virtual_keys[n].min.x = val[2] - val[4]/2;
+ dev->virtual_keys[n].min.y = val[3] - val[5]/2;
+ dev->virtual_keys[n].max.x = val[2] + val[4]/2;
+ dev->virtual_keys[n].max.y = val[3] + val[5]/2;
+
+#ifdef DEBUG_TOUCH
+ LOGI("vkey: fd=%d, [%d]=(%d,%d)-(%d,%d)\n", dev->fd,
+ dev->virtual_keys[n].keycode,
+ dev->virtual_keys[n].min.x, dev->virtual_keys[n].min.y,
+ dev->virtual_keys[n].max.x, dev->virtual_keys[n].max.y);
+#endif
+ }
+}
+
+void RecoveryUI::calibrate_swipe() {
+ char strvalue[PROPERTY_VALUE_MAX];
+ int intvalue;
+ property_get("ro.sf.lcd_density", strvalue, "160");
+ intvalue = atoi(strvalue);
+ int screen_density = (intvalue >= 160 ? intvalue : 160);
+ min_swipe_px.x = screen_density * 50 / 100; // Roughly 0.5in
+ min_swipe_px.y = screen_density * 30 / 100; // Roughly 0.3in
+#ifdef DEBUG_TOUCH
+ LOGI("calibrate_swipe: density=%d, min_swipe=(%d,%d)\n",
+ screen_density, min_swipe_px.x, min_swipe_px.y);
+#endif
+}
+
+void RecoveryUI::handle_press(input_device* dev) {
+ dev->touch_start = dev->touch_track = dev->touch_pos;
+ dev->in_touch = true;
+ dev->in_swipe = false;
+}
+
+void RecoveryUI::handle_release(input_device* dev) {
+ struct point diff = dev->touch_pos - dev->touch_start;
+ bool in_touch = dev->in_touch;
+ bool in_swipe = dev->in_swipe;
+
+ dev->in_touch = dev->in_swipe = false;
+
+ if (!in_swipe) {
+ int n;
+ for (n = 0; dev->virtual_keys[n].keycode != -1 && n < MAX_NR_VKEYS; ++n) {
+ vkey* vk = &dev->virtual_keys[n];
+ if (dev->touch_start.x >= vk->min.x && dev->touch_start.x < vk->max.x &&
+ dev->touch_start.y >= vk->min.y && dev->touch_start.y < vk->max.y) {
+#ifdef DEBUG_TOUCH
+ LOGI("handle_release: vkey %d\n", vk->keycode);
+#endif
+ EnqueueKey(vk->keycode);
+ return;
+ }
+ }
+ }
+
+ if (DialogShowing()) {
+ if (DialogDismissable() && !dev->in_swipe) {
+ DialogDismiss();
+ }
+ return;
+ }
+
+ if (in_swipe) {
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ int key = (diff.x > 0 ? KEY_ENTER : KEY_BACK);
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ else {
+ /* Vertical swipe, handled realtime */
+ }
+ }
+ else {
+ int sel, start_menu_pos;
+ // Make sure touch pos is not less than menu start pos.
+ // No need to check if beyond end of menu items, since
+ // that is checked by get_menu_selection().
+ start_menu_pos = MenuItemStart();
+ if (dev->touch_pos.y >= start_menu_pos) {
+ sel = (dev->touch_pos.y - start_menu_pos)/MenuItemHeight();
+ EnqueueKey(KEY_FLAG_ABS | sel);
+ }
+ }
+}
+
+void RecoveryUI::handle_gestures(input_device* dev) {
+ struct point diff;
+ diff = dev->touch_pos - dev->touch_start;
+
+ if (abs(diff.x) > abs(diff.y)) {
+ if (abs(diff.x) > min_swipe_px.x) {
+ /* Horizontal swipe, handle it on release */
+ dev->in_swipe = true;
+ }
+ }
+ else {
+ diff.y = dev->touch_pos.y - dev->touch_track.y;
+ if (abs(diff.y) > MenuItemHeight()) {
+ dev->in_swipe = true;
+ if (!DialogShowing()) {
+ dev->touch_track = dev->touch_pos;
+ int key = (diff.y < 0) ? KEY_VOLUMEUP : KEY_VOLUMEDOWN;
+ ProcessKey(dev, key, 1);
+ ProcessKey(dev, key, 0);
+ }
+ }
+ }
+}
+
void RecoveryUI::EnqueueKey(int key_code) {
if (DialogShowing()) {
if (DialogDismissable()) {
diff --git a/ui.h b/ui.h
index 123dc34..acf3645 100644
--- a/ui.h
+++ b/ui.h
@@ -24,6 +24,72 @@
#include "messagesocket.h"
#include "voldclient.h"
+#define MAX_NR_INPUT_DEVICES 8
+#define MAX_NR_VKEYS 8
+
+/*
+ * Simple representation of a (x,y) coordinate with convenience operators
+ */
+struct point {
+ point() : x(0), y(0) {}
+ point operator+(const point& rhs) const {
+ point tmp;
+ tmp.x = x + rhs.x;
+ tmp.y = y + rhs.y;
+ return tmp;
+ }
+ point operator-(const point& rhs) const {
+ point tmp;
+ tmp.x = x - rhs.x;
+ tmp.y = y - rhs.y;
+ return tmp;
+ }
+
+ int x;
+ int y;
+};
+
+/*
+ * Virtual key representation. Valid when keycode != -1.
+ */
+struct vkey {
+ vkey() : keycode(-1) {}
+ int keycode;
+ point min;
+ point max;
+};
+
+/*
+ * Input device representation. Valid when fd != -1.
+ * This holds all information and state related to a given input device.
+ */
+struct input_device {
+ input_device() : fd(-1) {}
+
+ int fd;
+ vkey virtual_keys[MAX_NR_VKEYS];
+ point touch_min;
+ point touch_max;
+
+ int rel_sum; // Accumulated relative movement
+
+ bool saw_pos_x; // Did sequence have ABS_MT_POSITION_X?
+ bool saw_pos_y; // Did sequence have ABS_MT_POSITION_Y?
+ bool saw_mt_report; // Did sequence have SYN_MT_REPORT?
+ bool saw_tracking_id; // Did sequence have SYN_TRACKING_ID?
+ bool in_touch; // Are we in a touch event?
+ bool in_swipe; // Are we in a swipe event?
+
+ point touch_pos; // Current touch coordinates
+ point touch_start; // Coordinates of touch start
+ point touch_track; // Last tracked coordinates
+
+ int slot_nr_active;
+ int slot_first;
+ int slot_current;
+ int tracking_id;
+};
+
// Abstract class for controlling the user interface during recovery.
class RecoveryUI {
public:
@@ -40,7 +106,7 @@ class RecoveryUI {
virtual void SetLocale(const char* locale) = 0;
// Set the overall recovery state ("background image").
- enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, INFO, ERROR, NR_ICONS };
+ enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, D_INFO, D_ERROR, NR_ICONS };
virtual void SetBackground(Icon icon) = 0;
// --- progress indicator ---
@@ -71,9 +137,11 @@ class RecoveryUI {
virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0;
virtual void ShowFile(const char* filename) = 0;
+ virtual void ClearText() = 0;
virtual void DialogShowInfo(const char* text) = 0;
virtual void DialogShowError(const char* text) = 0;
+ virtual void DialogShowErrorLog(const char* text) = 0;
virtual int DialogShowing() const = 0;
virtual bool DialogDismissable() const = 0;
virtual void DialogDismiss() = 0;
@@ -119,6 +187,9 @@ class RecoveryUI {
// --- menu display ---
+ virtual int MenuItemStart() const = 0;
+ virtual int MenuItemHeight() const = 0;
+
// Display 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).
@@ -127,7 +198,7 @@ class RecoveryUI {
// Set the menu highlight to the given index, wrapping if necessary.
// Returns the actual item selected.
- virtual int SelectMenu(int sel) = 0;
+ virtual int SelectMenu(int sel, bool abs = false) = 0;
// End menu mode, resetting the text overlay so that ui_print()
// statements will be displayed.
@@ -159,6 +230,11 @@ private:
bool has_up_key;
bool has_down_key;
+ input_device input_devices[MAX_NR_INPUT_DEVICES];
+
+ point fb_dimensions;
+ point min_swipe_px;
+
struct key_timer_t {
RecoveryUI* ui;
int key_code;
@@ -173,7 +249,10 @@ private:
static int InputCallback(int fd, uint32_t epevents, void* data);
int OnInputEvent(int fd, uint32_t epevents);
- void ProcessKey(int key_code, int updown);
+ void ProcessKey(input_device* dev, int key_code, int updown);
+ void ProcessSyn(input_device* dev, int code, int value);
+ void ProcessAbs(input_device* dev, int code, int value);
+ void ProcessRel(input_device* dev, int code, int value);
bool IsUsbConnected();
@@ -181,6 +260,14 @@ private:
static void* time_key_helper(void* cookie);
void time_key(int key_code, int count);
+
+ void process_touch(int fd, struct input_event *ev);
+ void calibrate_touch(input_device* dev);
+ void setup_vkeys(input_device* dev);
+ void calibrate_swipe();
+ void handle_press(input_device* dev);
+ void handle_release(input_device* dev);
+ void handle_gestures(input_device* dev);
};
#endif // RECOVERY_UI_H
diff --git a/verifier_test.cpp b/verifier_test.cpp
index ed3dce1..d97e115 100644
--- a/verifier_test.cpp
+++ b/verifier_test.cpp
@@ -148,17 +148,22 @@ class FakeUI : public RecoveryUI {
va_end(ap);
}
void ShowFile(const char*) { }
+ void ClearText() {}
virtual void DialogShowInfo(const char* text) {}
virtual void DialogShowError(const char* text) {}
+ virtual void DialogShowErrorLog(const char* text) {}
virtual int DialogShowing() const { return 0; }
bool DialogDismissable() const { return false; }
virtual void DialogDismiss() {}
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection) { }
- int SelectMenu(int sel) { return 0; }
+ int SelectMenu(int sel, bool abs = false) { return 0; }
void EndMenu() { }
+
+ virtual int MenuItemStart() const { return 0; }
+ virtual int MenuItemHeight() const { return 0; }
};
void
diff --git a/wear_ui.cpp b/wear_ui.cpp
index 4a07f39..9d4ec09 100644
--- a/wear_ui.cpp
+++ b/wear_ui.cpp
@@ -122,7 +122,7 @@ void WearRecoveryUI::draw_background_locked(Icon icon)
// Should only be called with updateMutex locked.
void WearRecoveryUI::draw_progress_locked()
{
- if (currentIcon == ERROR) return;
+ if (currentIcon == D_ERROR) return;
if (progressBarType != DETERMINATE) return;
int width = progress_bar_width;
@@ -348,8 +348,8 @@ void WearRecoveryUI::Init()
LoadBitmap("icon_installing", &backgroundIcon[INSTALLING_UPDATE]);
backgroundIcon[ERASING] = backgroundIcon[INSTALLING_UPDATE];
- LoadBitmap("icon_error", &backgroundIcon[ERROR]);
- backgroundIcon[NO_COMMAND] = backgroundIcon[ERROR];
+ LoadBitmap("icon_error", &backgroundIcon[D_ERROR]);
+ backgroundIcon[NO_COMMAND] = backgroundIcon[D_ERROR];
introFrames = (GRSurface**)malloc(intro_frames * sizeof(GRSurface*));
for (int i = 0; i < intro_frames; ++i) {
@@ -503,7 +503,7 @@ void WearRecoveryUI::StartMenu(const char* const * headers, const char* const *
pthread_mutex_unlock(&updateMutex);
}
-int WearRecoveryUI::SelectMenu(int sel) {
+int WearRecoveryUI::SelectMenu(int sel, bool abs /* = false */) {
int old_sel;
pthread_mutex_lock(&updateMutex);
if (show_menu > 0) {
diff --git a/wear_ui.h b/wear_ui.h
index d7624fe..2947d9c 100644
--- a/wear_ui.h
+++ b/wear_ui.h
@@ -53,7 +53,7 @@ class WearRecoveryUI : public RecoveryUI {
// menu display
void StartMenu(const char* const * headers, const char* const * items,
int initial_selection);
- int SelectMenu(int sel);
+ int SelectMenu(int sel, bool abs = false);
void EndMenu();
void Redraw();