diff options
| author | Tom Marshall <tdm@cyngn.com> | 2020-03-10 20:17:49 +0100 |
|---|---|---|
| committer | Alessandro Astone <ales.astone@gmail.com> | 2020-03-11 14:03:34 +0100 |
| commit | 7e2c60075489f9a2bc88046ee5b2ef3e76262994 (patch) | |
| tree | 660a6a0d678e2c9a937b167f47370b18e0cadf02 | |
| parent | c6ddbac6f10c93248bdf831e0ff5fc1d2c2f0885 (diff) | |
| download | android_bootable_recovery-7e2c60075489f9a2bc88046ee5b2ef3e76262994.tar.gz android_bootable_recovery-7e2c60075489f9a2bc88046ee5b2ef3e76262994.tar.bz2 android_bootable_recovery-7e2c60075489f9a2bc88046ee5b2ef3e76262994.zip | |
recovery: touch UI
[aleasto] make scrolling natural
Change-Id: Ibf64aa70e21d88f9d0b2c60fc1b66a9995837464
| -rw-r--r-- | recovery_ui/device.cpp | 5 | ||||
| -rw-r--r-- | recovery_ui/include/recovery_ui/device.h | 2 | ||||
| -rw-r--r-- | recovery_ui/include/recovery_ui/screen_ui.h | 39 | ||||
| -rw-r--r-- | recovery_ui/include/recovery_ui/ui.h | 75 | ||||
| -rw-r--r-- | recovery_ui/screen_ui.cpp | 166 | ||||
| -rw-r--r-- | recovery_ui/ui.cpp | 189 | ||||
| -rw-r--r-- | res-hdpi/images/ic_back.png | bin | 0 -> 217 bytes | |||
| -rw-r--r-- | res-mdpi/images/ic_back.png | bin | 0 -> 190 bytes | |||
| -rw-r--r-- | res-xhdpi/images/ic_back.png | bin | 0 -> 276 bytes | |||
| -rw-r--r-- | res-xxhdpi/images/ic_back.png | bin | 0 -> 364 bytes | |||
| -rw-r--r-- | res-xxxhdpi/images/ic_back.png | bin | 0 -> 452 bytes |
11 files changed, 347 insertions, 129 deletions
diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp index 3d4f594c..d205f77d 100644 --- a/recovery_ui/device.cpp +++ b/recovery_ui/device.cpp @@ -91,6 +91,11 @@ int Device::HandleMenuKey(int key, bool visible) { case KEY_SEARCH: return kHighlightUp; + case KEY_SCROLLUP: + return kScrollUp; + case KEY_SCROLLDOWN: + return kScrollDown; + case KEY_ENTER: case KEY_POWER: case BTN_MOUSE: diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 39969101..73b4b89c 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -36,6 +36,8 @@ class Device { static constexpr const int kGoBack = -5; static constexpr const int kGoHome = -6; static constexpr const int kRefresh = -7; + static constexpr const int kScrollUp = -8; + static constexpr const int kScrollDown = -9; // ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows // using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from diff --git a/recovery_ui/include/recovery_ui/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h index 4e5dbb98..b6b0b499 100644 --- a/recovery_ui/include/recovery_ui/screen_ui.h +++ b/recovery_ui/include/recovery_ui/screen_ui.h @@ -96,10 +96,18 @@ class Menu { // Sets the current selection to |sel|. Handle the overflow cases depending on if the menu is // scrollable. virtual int Select(int sel) = 0; + // Select by index within the currently visible items. + // Matches Select() if not scrollable + virtual int SelectVisible(int relative_sel) = 0; + // Scroll the menu by updown, if scrollable + virtual int Scroll(int updown) = 0; // Displays the menu headers on the screen at offset x, y virtual int DrawHeader(int x, int y) const = 0; // Iterates over the menu items and displays each of them at offset x, y. virtual int DrawItems(int x, int y, int screen_width, bool long_press) const = 0; + // Returns count of menu items. + virtual size_t ItemsCount() const = 0; + virtual bool IsMain() const = 0; protected: Menu(size_t initial_selection, const DrawInterface& draw_func); @@ -119,16 +127,20 @@ class TextMenu : public Menu { size_t initial_selection, int char_height, const DrawInterface& draw_funcs); int Select(int sel) override; + int SelectVisible(int relative_sel) override; + int Scroll(int updown) override; int DrawHeader(int x, int y) const override; int DrawItems(int x, int y, int screen_width, bool long_press) const override; + size_t ItemsCount() const override; + bool IsMain() const override { + // Main menus have no headers + return text_headers_.size() == 0; + } bool scrollable() const { return scrollable_; } - // Returns count of menu items. - size_t ItemsCount() const; - // Returns the index of the first menu item. size_t MenuStart() const; @@ -180,8 +192,18 @@ class GraphicMenu : public Menu { size_t initial_selection, const DrawInterface& draw_funcs); int Select(int sel) override; + int SelectVisible(int sel) override { + return Select(sel); + } + int Scroll(int updown __unused) override { + return selection_; + }; int DrawHeader(int x, int y) const override; int DrawItems(int x, int y, int screen_width, bool long_press) const override; + size_t ItemsCount() const override; + bool IsMain() const override { + return true; + } // Checks if all the header and items are valid GRSurface's; and that they can fit in the area // defined by |max_width| and |max_height|. @@ -299,6 +321,10 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items, const std::function<int(int, bool)>& key_handler) override; + int MenuItemHeight() const override { + return MenuCharHeight() + 2 * MenuItemPadding(); + } + protected: static constexpr int kMenuIndent = 24; @@ -342,6 +368,8 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item // selected. virtual int SelectMenu(int sel); + virtual int SelectMenu(const Point& point); + virtual int ScrollMenu(int updown); virtual void draw_background_locked(); virtual void draw_foreground_locked(); @@ -395,9 +423,6 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { 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_; @@ -418,6 +443,7 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { std::unique_ptr<GRSurface> wipe_data_menu_header_text_; std::unique_ptr<GRSurface> lineage_logo_; + std::unique_ptr<GRSurface> back_icon_; std::unique_ptr<GRSurface> fastbootd_logo_; // current_icon_ points to one of the frames in intro_frames_ or loop_frames_, indexed by @@ -456,6 +482,7 @@ class ScreenRecoveryUI : public RecoveryUI, public DrawInterface { bool scrollable_menu_; std::unique_ptr<Menu> menu_; + int menu_start_y_; // An alternate text screen, swapped with 'text_' when we're viewing a log file. char** file_viewer_text_; diff --git a/recovery_ui/include/recovery_ui/ui.h b/recovery_ui/include/recovery_ui/ui.h index 764f4be8..653a48ef 100644 --- a/recovery_ui/include/recovery_ui/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -102,6 +102,42 @@ class RecoveryUI { INTERRUPTED = -2, }; + enum EventType { + EXTRA, + KEY, + TOUCH, + }; + + class InputEvent { + public: + InputEvent() : type_(EventType::EXTRA), evt_({ 0 }) { + evt_.key = static_cast<int>(KeyError::TIMED_OUT); + } + explicit InputEvent(EventType type, KeyError key) + : type_(type), evt_({ static_cast<int>(key) }) {} + explicit InputEvent(int key) : type_(EventType::KEY), evt_({ key }) {} + explicit InputEvent(const Point& pos) : type_(EventType::TOUCH), evt_({ 0 }) { + evt_.pos = pos; + } + + EventType type() const { + return type_; + } + int key() const { + return evt_.key; + } + const Point& pos() const { + return evt_.pos; + } + + private: + EventType type_; + union { + int key; + Point pos; + } evt_; + }; + RecoveryUI(); virtual ~RecoveryUI(); @@ -152,7 +188,7 @@ class RecoveryUI { // Waits for a key and return it. May return TIMED_OUT after timeout and // KeyError::INTERRUPTED on a key interrupt. - virtual int WaitKey(); + virtual InputEvent WaitInputEvent(); virtual void CancelWaitKey(); // Wakes up the UI if it is waiting on key input, causing WaitKey to return KeyError::INTERRUPTED. @@ -222,6 +258,10 @@ class RecoveryUI { const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items, const std::function<int(int, bool)>& key_handler) = 0; + virtual int MenuItemHeight() const { + return 1; + } + // Set whether or not the fastbootd logo is displayed. void SetEnableFastbootdLogo(bool enable) { fastbootd_logo_enabled_ = enable; @@ -239,6 +279,7 @@ class RecoveryUI { protected: void EnqueueKey(int key_code); + void EnqueueTouch(const Point& pos); // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% of // the max_brightness). Because the absolute values may vary across devices. These two values can @@ -267,7 +308,9 @@ class RecoveryUI { void OnTouchDeviceDetected(int fd); void OnKeyDetected(int key_code); - void OnTouchEvent(); + void OnTouchPress(); + void OnTouchTrack(); + void OnTouchRelease(); int OnInputEvent(int fd, uint32_t epevents); void ProcessKey(int key_code, int updown); void TimeKey(int key_code, int count); @@ -277,15 +320,16 @@ class RecoveryUI { bool InitScreensaver(); void SetScreensaverState(ScreensaverState state); // Key event input queue - std::mutex key_queue_mutex; - std::condition_variable key_queue_cond; + std::mutex event_queue_mutex; + std::condition_variable event_queue_cond; bool key_interrupted_; - int key_queue[256], key_queue_len; - char key_pressed[KEY_MAX + 1]; // under key_queue_mutex - int key_last_down; // under key_queue_mutex - bool key_long_press; // under key_queue_mutex - int key_down_count; // under key_queue_mutex - bool enable_reboot; // under key_queue_mutex + InputEvent event_queue[256]; + int event_queue_len; + char key_pressed[KEY_MAX + 1]; // under event_queue_mutex + int key_last_down; // under event_queue_mutex + bool key_long_press; // under event_queue_mutex + int key_down_count; // under event_queue_mutex + bool enable_reboot; // under event_queue_mutex int rel_sum; int consecutive_power_keys; @@ -297,6 +341,10 @@ class RecoveryUI { bool has_touch_screen; struct vkey_t { + bool inside(const Point& p) const { + return (p.x() >= min_.x() && p.x() < max_.x() && p.y() >= min_.y() && p.y() < max_.y()); + } + int keycode; Point min_; Point max_; @@ -304,10 +352,13 @@ class RecoveryUI { // Touch event related variables. See the comments in RecoveryUI::OnInputEvent(). int touch_slot_; + bool touch_finger_down_; + bool touch_saw_x_; + bool touch_saw_y_; + bool touch_reported_; Point touch_pos_; Point touch_start_; - bool touch_finger_down_; - bool touch_swiping_; + Point touch_track_; std::vector<vkey_t> virtual_keys_; bool is_bootreason_recovery_ui_; diff --git a/recovery_ui/screen_ui.cpp b/recovery_ui/screen_ui.cpp index 33aad03d..bbc83773 100644 --- a/recovery_ui/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -148,6 +148,34 @@ int TextMenu::Select(int sel) { return selection_; } +int TextMenu::SelectVisible(int relative_sel) { + int sel = relative_sel; + if (scrollable_ && menu_start_ > 0) { + sel += menu_start_; + } + + return Select(sel); +} + +int TextMenu::Scroll(int updown) { + if(!scrollable_) return selection_; + + if ((updown > 0 && menu_start_ + max_display_items_ < ItemsCount()) || + (updown < 0 && menu_start_ > 0)) { + menu_start_ += updown; + + /* We can receive a kInvokeItem event from a different source than touch, + like from Power button. For this reason, selection should not get out of + the screen. Constrain it to the first or last visible item of the list */ + if (selection_ < menu_start_) { + selection_ = menu_start_; + } else if (selection_ >= menu_start_ + max_display_items_) { + selection_ = menu_start_ + max_display_items_ - 1; + } + } + return selection_; +} + int TextMenu::DrawHeader(int x, int y) const { int offset = 0; @@ -261,6 +289,10 @@ int GraphicMenu::DrawItems(int x, int y, int screen_width, bool long_press) cons return offset; } +size_t GraphicMenu::ItemsCount() const { + return graphic_items_.size(); +} + bool GraphicMenu::Validate(size_t max_width, size_t max_height, const GRSurface* graphic_headers, const std::vector<const GRSurface*>& graphic_items) { int offset = 0; @@ -634,16 +666,20 @@ void ScreenRecoveryUI::CheckBackgroundTextImages() { FlushKeys(); while (true) { - int key = WaitKey(); - if (key == static_cast<int>(KeyError::INTERRUPTED)) break; - if (key == KEY_POWER || key == KEY_ENTER) { - break; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { - selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1; - SelectAndShowBackgroundText(locales_entries, selected); - } else if (key == KEY_DOWN || key == KEY_VOLUMEDOWN) { - selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1; - SelectAndShowBackgroundText(locales_entries, selected); + InputEvent evt = WaitInputEvent(); + if (evt.type() == EventType::EXTRA) { + if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) break; + } + if (evt.type() == EventType::KEY) { + if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER) { + break; + } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP) { + selected = (selected == 0) ? locales_entries.size() - 1 : selected - 1; + SelectAndShowBackgroundText(locales_entries, selected); + } else if (evt.key() == KEY_DOWN || evt.key() == KEY_VOLUMEDOWN) { + selected = (selected == locales_entries.size() - 1) ? 0 : selected + 1; + SelectAndShowBackgroundText(locales_entries, selected); + } } } @@ -777,12 +813,22 @@ void ScreenRecoveryUI::draw_menu_and_text_buffer_locked( SetColor(UIElement::INFO); - if (lineage_logo_) { + if (lineage_logo_ && back_icon_) { 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; + + if (!menu_->IsMain()) { + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + auto icon_w = gr_get_width(back_icon_.get()); + auto icon_h = gr_get_height(back_icon_.get()); + auto icon_x = margin_width_ + (h_unit *1/2) + ((h_unit * 2) - icon_w) / 2; + auto icon_y = margin_height_ + (v_unit * 3/2) + ((v_unit * 3/2) - icon_h) / 2; + gr_blit(back_icon_.get(), 0, 0, icon_w, icon_h, icon_x, icon_y); + } } else { for (size_t i = 0; i < title_lines_.size(); i++) { y += DrawTextLine(x, y, title_lines_[i], i == 0); @@ -791,6 +837,7 @@ void ScreenRecoveryUI::draw_menu_and_text_buffer_locked( } y += menu_->DrawHeader(x, y); + menu_start_y_ = y + 12; // Skip horizontal rule and some margin y += menu_->DrawItems(x, y, ScreenWidth(), IsLongPress()); } @@ -976,6 +1023,7 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { error_text_ = LoadLocalizedBitmap("error_text"); lineage_logo_ = LoadBitmap("logo_image"); + back_icon_ = LoadBitmap("ic_back"); if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) || android::base::GetBoolProperty("ro.fastbootd.available", false)) { fastbootd_logo_ = LoadBitmap("fastbootd"); @@ -1161,11 +1209,20 @@ void ScreenRecoveryUI::ShowFile(FILE* fp) { Redraw(); while (show_prompt) { show_prompt = false; - int key = WaitKey(); - if (key == static_cast<int>(KeyError::INTERRUPTED)) return; - if (key == KEY_POWER || key == KEY_ENTER) { + InputEvent evt = WaitInputEvent(); + if (evt.type() == EventType::EXTRA) { + if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) { + return; + } + } + if (evt.type() != EventType::KEY) { + show_prompt = true; + continue; + } + if (evt.key() == KEY_POWER || evt.key() == KEY_ENTER || evt.key() == KEY_BACKSPACE || + evt.key() == KEY_BACK || evt.key() == KEY_HOME || evt.key() == KEY_HOMEPAGE) { return; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + } else if (evt.key() == KEY_UP || evt.key() == KEY_VOLUMEUP || evt.key() == KEY_SCROLLUP) { if (offsets.size() <= 1) { show_prompt = true; } else { @@ -1261,6 +1318,39 @@ int ScreenRecoveryUI::SelectMenu(int sel) { return sel; } +int ScreenRecoveryUI::SelectMenu(const Point& point) { + int new_sel = Device::kNoAction; + int h_unit = gr_fb_width() / 9; + int v_unit = gr_fb_height() / 16; + std::lock_guard<std::mutex> lg(updateMutex); + if (menu_) { + if (!menu_->IsMain() && point.x() >= h_unit * 1/2 && point.x() < h_unit * 5/2 && + point.y() >= v_unit * 3/2 && point.y() < v_unit * 3) { + return Device::kGoBack; + } + + if (point.y() >= menu_start_y_) { + int old_sel = menu_->selection(); + int relative_sel = (point.y() - menu_start_y_) / MenuItemHeight(); + new_sel = menu_->SelectVisible(relative_sel); + if (new_sel != -1 && new_sel != old_sel) { + update_screen_locked(); + } + } + } + return new_sel; +} + +int ScreenRecoveryUI::ScrollMenu(int updown) { + std::lock_guard<std::mutex> lg(updateMutex); + int sel = Device::kNoAction; + if (menu_) { + sel = menu_->Scroll(updown); + update_screen_locked(); + } + return sel; +} + size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only, const std::function<int(int, bool)>& key_handler) { // Throw away keys pressed previously, so user doesn't accidentally trigger menu items. @@ -1279,23 +1369,37 @@ size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only, int selected = menu_->selection(); int chosen_item = -1; while (chosen_item < 0) { - int key = WaitKey(); - if (key == static_cast<int>(KeyError::INTERRUPTED)) { // WaitKey() was interrupted. - return static_cast<size_t>(KeyError::INTERRUPTED); + InputEvent evt = WaitInputEvent(); + if (evt.type() == EventType::EXTRA) { + if (evt.key() == static_cast<int>(KeyError::INTERRUPTED)) { + // WaitKey() was interrupted. + return static_cast<size_t>(KeyError::INTERRUPTED); + } + if (evt.key() == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out. + if (WasTextEverVisible()) { + continue; + } else { + LOG(INFO) << "Timed out waiting for key input; rebooting."; + menu_.reset(); + Redraw(); + return static_cast<size_t>(KeyError::TIMED_OUT); + } + } } - if (key == static_cast<int>(KeyError::TIMED_OUT)) { // WaitKey() timed out. - if (WasTextEverVisible()) { - continue; + + int action = Device::kNoAction; + if (evt.type() == EventType::TOUCH) { + int touch_sel = SelectMenu(evt.pos()); + if (touch_sel < 0) { + action = touch_sel; } else { - LOG(INFO) << "Timed out waiting for key input; rebooting."; - menu_.reset(); - Redraw(); - return static_cast<size_t>(KeyError::TIMED_OUT); + action = Device::kInvokeItem; + selected = touch_sel; } + } else { + bool visible = IsTextVisible(); + action = key_handler(evt.key(), visible); } - - bool visible = IsTextVisible(); - int action = key_handler(key, visible); if (action < 0) { switch (action) { case Device::kHighlightUp: @@ -1304,6 +1408,12 @@ size_t ScreenRecoveryUI::ShowMenu(std::unique_ptr<Menu>&& menu, bool menu_only, case Device::kHighlightDown: selected = SelectMenu(++selected); break; + case Device::kScrollUp: + selected = ScrollMenu(-1); + break; + case Device::kScrollDown: + selected = ScrollMenu(1); + break; case Device::kInvokeItem: chosen_item = selected; break; diff --git a/recovery_ui/ui.cpp b/recovery_ui/ui.cpp index ef02fea0..45d747b9 100644 --- a/recovery_ui/ui.cpp +++ b/recovery_ui/ui.cpp @@ -64,7 +64,7 @@ RecoveryUI::RecoveryUI() touch_high_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_high_threshold", kDefaultTouchHighThreshold)), key_interrupted_(false), - key_queue_len(0), + event_queue_len(0), key_last_down(-1), key_long_press(false), key_down_count(0), @@ -76,6 +76,10 @@ RecoveryUI::RecoveryUI() has_down_key(false), has_touch_screen(false), touch_slot_(0), + touch_finger_down_(false), + touch_saw_x_(false), + touch_saw_y_(false), + touch_reported_(false), is_bootreason_recovery_ui_(false), screensaver_state_(ScreensaverState::DISABLED) { memset(key_pressed, 0, sizeof(key_pressed)); @@ -223,58 +227,57 @@ bool RecoveryUI::Init(const std::string& /* locale */) { return true; } -void RecoveryUI::OnTouchEvent() { - Point delta = touch_pos_ - touch_start_; - enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction; +void RecoveryUI::OnTouchPress() { + touch_start_ = touch_track_ = touch_pos_; +} - // We only consider a valid swipe if: - // - the delta along one axis is below touch_low_threshold_; - // - and the delta along the other axis is beyond touch_high_threshold_. - if (abs(delta.y()) < touch_low_threshold_ && abs(delta.x()) > touch_high_threshold_) { - direction = delta.x() < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT; - } else if (abs(delta.x()) < touch_low_threshold_ && abs(delta.y()) > touch_high_threshold_) { - direction = delta.y() < 0 ? SwipeDirection::UP : SwipeDirection::DOWN; - } else { +void RecoveryUI::OnTouchTrack() { + if (touch_pos_.y() <= gr_fb_height()) { + while (abs(touch_pos_.y() - touch_track_.y()) >= MenuItemHeight()) { + int dy = touch_pos_.y() - touch_track_.y(); + int key = (dy < 0) ? KEY_SCROLLDOWN : KEY_SCROLLUP; + ProcessKey(key, 1); // press key + ProcessKey(key, 0); // and release it + int sgn = (dy > 0) - (dy < 0); + touch_track_.y(touch_track_.y() + sgn * MenuItemHeight()); + } + } +} + +void RecoveryUI::OnTouchRelease() { + // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui. + if (is_bootreason_recovery_ui_ && !IsTextVisible()) { + ShowText(true); + return; + } + + // Check vkeys. Only report if touch both starts and ends in the vkey. + if (touch_start_.y() > gr_fb_height() && touch_pos_.y() > gr_fb_height()) { for (const auto& vk : virtual_keys_) { - if (touch_start_.x() >= vk.min_.x() && touch_start_.x() < vk.max_.x() && - touch_start_.y() >= vk.min_.y() && touch_start_.y() < vk.max_.y()) { + if (vk.inside(touch_start_) && vk.inside(touch_pos_)) { ProcessKey(vk.keycode, 1); // press key ProcessKey(vk.keycode, 0); // and release it - return; } } - LOG(DEBUG) << "Ignored " << delta.x() << " " << delta.y() << " (low: " << touch_low_threshold_ - << ", high: " << touch_high_threshold_ << ")"; return; } - // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui. - if (is_bootreason_recovery_ui_ && !IsTextVisible()) { - ShowText(true); + // If we tracked a vertical swipe, ignore the release + if (touch_track_ != touch_start_) { return; } - LOG(DEBUG) << "Swipe direction=" << direction; - switch (direction) { - case SwipeDirection::UP: - ProcessKey(KEY_UP, 1); // press up key - ProcessKey(KEY_UP, 0); // and release it - break; - - case SwipeDirection::DOWN: - ProcessKey(KEY_DOWN, 1); // press down key - ProcessKey(KEY_DOWN, 0); // and release it - break; + // Check for horizontal swipe + Point delta = touch_pos_ - touch_start_; + if (abs(delta.y()) < touch_low_threshold_ && abs(delta.x()) > touch_high_threshold_) { + int key = (delta.x() < 0) ? KEY_BACK : KEY_POWER; + ProcessKey(key, 1); // press key + ProcessKey(key, 0); // and release it + return; + } - case SwipeDirection::LEFT: - ProcessKey(KEY_BACK, 1); // press back key - ProcessKey(KEY_BACK, 0); // and release it - break; - case SwipeDirection::RIGHT: - ProcessKey(KEY_POWER, 1); // press power key - ProcessKey(KEY_POWER, 0); // and release it - break; - }; + // Simple touch + EnqueueTouch(touch_pos_); } int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { @@ -285,10 +288,6 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { // Touch inputs handling. // - // We handle the touch inputs by tracking the position changes between initial contacting and - // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon - // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes. - // // Per the doc Multi-touch Protocol at below, there are two protocols. // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt // @@ -305,14 +304,17 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { if (ev.type == EV_SYN) { if (touch_screen_allowed_ && ev.code == SYN_REPORT) { - // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the - // contact. - if (touch_finger_down_ && !touch_swiping_) { - touch_start_ = touch_pos_; - touch_swiping_ = true; - } else if (!touch_finger_down_ && touch_swiping_) { - touch_swiping_ = false; - OnTouchEvent(); + // There might be multiple SYN_REPORT events. Only report press/release once. + if (!touch_reported_ && touch_finger_down_) { + if (touch_saw_x_ && touch_saw_y_) { + OnTouchPress(); + touch_reported_ = true; + touch_saw_x_ = touch_saw_y_ = false; + } + } else if (touch_reported_ && !touch_finger_down_) { + OnTouchRelease(); + touch_reported_ = false; + touch_saw_x_ = touch_saw_y_ = false; } } return 0; @@ -348,13 +350,23 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { switch (ev.code) { case ABS_MT_POSITION_X: - touch_pos_.x(ev.value); touch_finger_down_ = true; + touch_saw_x_ = true; + touch_pos_.x(ev.value); + if (touch_reported_ && touch_saw_y_) { + OnTouchTrack(); + touch_saw_x_ = touch_saw_y_ = false; + } break; case ABS_MT_POSITION_Y: - touch_pos_.y(ev.value); touch_finger_down_ = true; + touch_saw_y_ = true; + touch_pos_.y(ev.value); + if (touch_reported_ && touch_saw_x_) { + OnTouchTrack(); + touch_saw_x_ = touch_saw_y_ = false; + } break; case ABS_MT_TRACKING_ID: @@ -401,7 +413,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { bool long_press = false; { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); key_pressed[key_code] = updown; if (updown) { ++key_down_count; @@ -448,7 +460,7 @@ void RecoveryUI::TimeKey(int key_code, int count) { std::this_thread::sleep_for(750ms); // 750 ms == "long" bool long_press = false; { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); if (key_last_down == key_code && key_down_count == count) { long_press = key_long_press = true; } @@ -457,11 +469,22 @@ void RecoveryUI::TimeKey(int key_code, int count) { } void RecoveryUI::EnqueueKey(int key_code) { - std::lock_guard<std::mutex> lg(key_queue_mutex); - const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); - if (key_queue_len < queue_max) { - key_queue[key_queue_len++] = key_code; - key_queue_cond.notify_one(); + std::lock_guard<std::mutex> lg(event_queue_mutex); + const int queue_max = sizeof(event_queue) / sizeof(event_queue[0]); + if (event_queue_len < queue_max) { + InputEvent event(key_code); + event_queue[event_queue_len++] = event; + event_queue_cond.notify_one(); + } +} + +void RecoveryUI::EnqueueTouch(const Point& pos) { + std::lock_guard<std::mutex> lg(event_queue_mutex); + const int queue_max = sizeof(event_queue) / sizeof(event_queue[0]); + if (event_queue_len < queue_max) { + InputEvent event(pos); + event_queue[event_queue_len++] = event; + event_queue_cond.notify_one(); } } @@ -500,23 +523,23 @@ void RecoveryUI::SetScreensaverState(ScreensaverState state) { } } -int RecoveryUI::WaitKey() { - std::unique_lock<std::mutex> lk(key_queue_mutex); +RecoveryUI::InputEvent RecoveryUI::WaitInputEvent() { + std::unique_lock<std::mutex> lk(event_queue_mutex); // Check for a saved key queue interruption. if (key_interrupted_) { SetScreensaverState(ScreensaverState::NORMAL); - return static_cast<int>(KeyError::INTERRUPTED); + return InputEvent(EventType::EXTRA, KeyError::INTERRUPTED); } // Time out after UI_WAIT_KEY_TIMEOUT_SEC, unless a USB cable is plugged in. do { - bool rc = key_queue_cond.wait_for(lk, std::chrono::seconds(UI_WAIT_KEY_TIMEOUT_SEC), [this] { - return this->key_queue_len != 0 || key_interrupted_; + bool rc = event_queue_cond.wait_for(lk, std::chrono::seconds(UI_WAIT_KEY_TIMEOUT_SEC), [this] { + return this->event_queue_len != 0 || key_interrupted_; }); if (key_interrupted_) { SetScreensaverState(ScreensaverState::NORMAL); - return static_cast<int>(KeyError::INTERRUPTED); + return InputEvent(EventType::EXTRA, KeyError::INTERRUPTED); } if (screensaver_state_ != ScreensaverState::DISABLED) { if (!rc) { @@ -529,8 +552,8 @@ int RecoveryUI::WaitKey() { } else if (screensaver_state_ != ScreensaverState::NORMAL) { // Drop the first key if it's changing from OFF to NORMAL. if (screensaver_state_ == ScreensaverState::OFF) { - if (key_queue_len > 0) { - memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + if (event_queue_len > 0) { + memcpy(&event_queue[0], &event_queue[1], sizeof(int) * --event_queue_len); } } @@ -538,14 +561,14 @@ int RecoveryUI::WaitKey() { SetScreensaverState(ScreensaverState::NORMAL); } } - } while (IsUsbConnected() && key_queue_len == 0); + } while (IsUsbConnected() && event_queue_len == 0); - int key = static_cast<int>(KeyError::TIMED_OUT); - if (key_queue_len > 0) { - key = key_queue[0]; - memcpy(&key_queue[0], &key_queue[1], sizeof(int) * --key_queue_len); + InputEvent event; + if (event_queue_len > 0) { + event = event_queue[0]; + memcpy(&event_queue[0], &event_queue[1], sizeof(InputEvent) * --event_queue_len); } - return key; + return event; } void RecoveryUI::CancelWaitKey() { @@ -554,10 +577,10 @@ void RecoveryUI::CancelWaitKey() { void RecoveryUI::InterruptKey() { { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); key_interrupted_ = true; } - key_queue_cond.notify_one(); + event_queue_cond.notify_one(); } bool RecoveryUI::IsUsbConnected() { @@ -577,13 +600,13 @@ bool RecoveryUI::IsUsbConnected() { } bool RecoveryUI::IsKeyPressed(int key) { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); int pressed = key_pressed[key]; return pressed; } bool RecoveryUI::IsLongPress() { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); bool result = key_long_press; return result; } @@ -601,13 +624,13 @@ bool RecoveryUI::HasTouchScreen() const { } void RecoveryUI::FlushKeys() { - std::lock_guard<std::mutex> lg(key_queue_mutex); - key_queue_len = 0; + std::lock_guard<std::mutex> lg(event_queue_mutex); + event_queue_len = 0; } RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); key_long_press = false; } @@ -651,6 +674,6 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { void RecoveryUI::KeyLongPress(int) {} void RecoveryUI::SetEnableReboot(bool enabled) { - std::lock_guard<std::mutex> lg(key_queue_mutex); + std::lock_guard<std::mutex> lg(event_queue_mutex); enable_reboot = enabled; } diff --git a/res-hdpi/images/ic_back.png b/res-hdpi/images/ic_back.png Binary files differnew file mode 100644 index 00000000..ec1ea23e --- /dev/null +++ b/res-hdpi/images/ic_back.png diff --git a/res-mdpi/images/ic_back.png b/res-mdpi/images/ic_back.png Binary files differnew file mode 100644 index 00000000..4af9ce63 --- /dev/null +++ b/res-mdpi/images/ic_back.png diff --git a/res-xhdpi/images/ic_back.png b/res-xhdpi/images/ic_back.png Binary files differnew file mode 100644 index 00000000..6dbb7066 --- /dev/null +++ b/res-xhdpi/images/ic_back.png diff --git a/res-xxhdpi/images/ic_back.png b/res-xxhdpi/images/ic_back.png Binary files differnew file mode 100644 index 00000000..fef22710 --- /dev/null +++ b/res-xxhdpi/images/ic_back.png diff --git a/res-xxxhdpi/images/ic_back.png b/res-xxxhdpi/images/ic_back.png Binary files differnew file mode 100644 index 00000000..b41f5572 --- /dev/null +++ b/res-xxxhdpi/images/ic_back.png |
