C++ class for device-specific code

Replace the device-specific functions with a class.  Move some of the
key handling (for log visibility toggling and rebooting) into the UI
class.  Fix up the key handling so there is less crosstalk between the
immediate keys and the queued keys (an increasing annoyance on
button-limited devices).

Change-Id: I698f6fd21c67a1e55429312a0484b6c393cad46f
diff --git a/screen_ui.cpp b/screen_ui.cpp
index e6a31db..a60b046 100644
--- a/screen_ui.cpp
+++ b/screen_ui.cpp
@@ -31,9 +31,9 @@
 #include "common.h"
 #include <cutils/android_reboot.h>
 #include "minui/minui.h"
-#include "recovery_ui.h"
 #include "ui.h"
 #include "screen_ui.h"
+#include "device.h"
 
 #define CHAR_WIDTH 10
 #define CHAR_HEIGHT 18
@@ -78,7 +78,8 @@
     menu_top(0),
     menu_items(0),
     menu_sel(0),
-    key_queue_len(0) {
+    key_queue_len(0),
+    key_last_down(-1) {
     pthread_mutex_init(&updateMutex, NULL);
     pthread_mutex_init(&key_queue_mutex, NULL);
     pthread_cond_init(&key_queue_cond, NULL);
@@ -281,7 +282,6 @@
 {
     struct input_event ev;
     int ret;
-    int fake_key = 0;
 
     ret = ev_get_input(fd, revents, &ev);
     if (ret)
@@ -297,16 +297,12 @@
             // key event.
             self->rel_sum += ev.value;
             if (self->rel_sum > 3) {
-                fake_key = 1;
-                ev.type = EV_KEY;
-                ev.code = KEY_DOWN;
-                ev.value = 1;
+                self->process_key(KEY_DOWN, 1);   // press down key
+                self->process_key(KEY_DOWN, 0);   // and release it
                 self->rel_sum = 0;
             } else if (self->rel_sum < -3) {
-                fake_key = 1;
-                ev.type = EV_KEY;
-                ev.code = KEY_UP;
-                ev.value = 1;
+                self->process_key(KEY_UP, 1);     // press up key
+                self->process_key(KEY_UP, 0);     // and release it
                 self->rel_sum = 0;
             }
         }
@@ -314,38 +310,65 @@
         self->rel_sum = 0;
     }
 
-    if (ev.type != EV_KEY || ev.code > KEY_MAX)
-        return 0;
-
-    pthread_mutex_lock(&self->key_queue_mutex);
-    if (!fake_key) {
-        // our "fake" keys only report a key-down event (no
-        // key-up), so don't record them in the key_pressed
-        // table.
-        self->key_pressed[ev.code] = ev.value;
-    }
-    const int queue_max = sizeof(self->key_queue) / sizeof(self->key_queue[0]);
-    if (ev.value > 0 && self->key_queue_len < queue_max) {
-        self->key_queue[self->key_queue_len++] = ev.code;
-        pthread_cond_signal(&self->key_queue_cond);
-    }
-    pthread_mutex_unlock(&self->key_queue_mutex);
-
-    if (ev.value > 0 && device_toggle_display(self->key_pressed, ev.code)) {
-        pthread_mutex_lock(&self->updateMutex);
-        self->show_text = !self->show_text;
-        if (self->show_text) self->show_text_ever = true;
-        self->update_screen_locked();
-        pthread_mutex_unlock(&self->updateMutex);
-    }
-
-    if (ev.value > 0 && device_reboot_now(self->key_pressed, ev.code)) {
-        android_reboot(ANDROID_RB_RESTART, 0, 0);
-    }
+    if (ev.type == EV_KEY && ev.code <= KEY_MAX)
+        self->process_key(ev.code, ev.value);
 
     return 0;
 }
 
+// Process a key-up or -down event.  A key is "registered" when it is
+// pressed and then released, with no other keypresses or releases in
+// between.  Registered keys are passed to CheckKey() to see if it
+// should trigger a visibility toggle, an immediate reboot, or be
+// queued to be processed next time the foreground thread wants a key
+// (eg, for the menu).
+//
+// We also keep track of which keys are currently down so that
+// CheckKey can call IsKeyPressed to see what other keys are held when
+// a key is registered.
+//
+// updown == 1 for key down events; 0 for key up events
+void ScreenRecoveryUI::process_key(int key_code, int updown) {
+    bool register_key = false;
+
+    pthread_mutex_lock(&key_queue_mutex);
+    key_pressed[key_code] = updown;
+    if (updown) {
+        key_last_down = key_code;
+    } else {
+        if (key_last_down == key_code)
+            register_key = true;
+        key_last_down = -1;
+    }
+    pthread_mutex_unlock(&key_queue_mutex);
+
+    if (register_key) {
+        switch (CheckKey(key_code)) {
+          case RecoveryUI::TOGGLE:
+            pthread_mutex_lock(&updateMutex);
+            show_text = !show_text;
+            if (show_text) show_text_ever = true;
+            update_screen_locked();
+            pthread_mutex_unlock(&updateMutex);
+            break;
+
+          case RecoveryUI::REBOOT:
+            android_reboot(ANDROID_RB_RESTART, 0, 0);
+            break;
+
+          case RecoveryUI::ENQUEUE:
+            pthread_mutex_lock(&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;
+                pthread_cond_signal(&key_queue_cond);
+            }
+            pthread_mutex_unlock(&key_queue_mutex);
+            break;
+        }
+    }
+}
+
 // Reads input events, handles special hot keys, and adds to the key queue.
 void* ScreenRecoveryUI::input_thread(void *cookie)
 {
@@ -622,8 +645,10 @@
 
 bool ScreenRecoveryUI::IsKeyPressed(int key)
 {
-    // This is a volatile static array, don't bother locking
-    return key_pressed[key];
+    pthread_mutex_lock(&key_queue_mutex);
+    int pressed = key_pressed[key];
+    pthread_mutex_unlock(&key_queue_mutex);
+    return pressed;
 }
 
 void ScreenRecoveryUI::FlushKeys() {
@@ -631,3 +656,7 @@
     key_queue_len = 0;
     pthread_mutex_unlock(&key_queue_mutex);
 }
+
+RecoveryUI::KeyAction ScreenRecoveryUI::CheckKey(int key) {
+    return RecoveryUI::ENQUEUE;
+}