Просмотр исходного кода

UI alert + long press to exit.

antirez 3 лет назад
Родитель
Сommit
c93ae3ca77
4 измененных файлов с 95 добавлено и 22 удалено
  1. 18 3
      app.c
  2. 18 6
      app.h
  3. 49 7
      ui.c
  4. 10 6
      view_info.c

+ 18 - 3
app.c

@@ -59,6 +59,9 @@ static void render_callback(Canvas *const canvas, void *ctx) {
     case ViewDirectSampling: render_view_direct_sampling(canvas,app); break;
     default: furi_crash(TAG "Invalid view selected"); break;
     }
+
+    /* Draw the alert box if set. */
+    ui_draw_alert_if_needed(canvas, app);
 }
 
 /* Here all we do is putting the events into the queue that will be handled
@@ -106,6 +109,7 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) {
      * the main thing. */
     app->current_subview[old] = 0;
     memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN);
+    ui_dismiss_alert(app);
 }
 
 /* Allocate the application state and initialize a number of stuff.
@@ -132,6 +136,7 @@ ProtoViewApp* protoview_app_alloc() {
     app->view_dispatcher = NULL;
     app->text_input = NULL;
     app->show_text_input = false;
+    app->alert_dismiss_time = 0;
     app->current_view = ViewRawPulses;
     for (int j = 0; j < ViewLast; j++) app->current_subview[j] = 0;
     app->direct_sampling_enabled = false;
@@ -269,17 +274,27 @@ int32_t protoview_app_entry(void* p) {
             if (input.type == InputTypeShort &&
                 input.key == InputKeyBack)
             {
-                /* Exit the app. */
+                if (app->current_view != ViewRawPulses) {
+                    /* If this is not the main app view, go there. */
+                    app_switch_view(app,ViewRawPulses);
+                } else {
+                    /* If we are in the main app view, warn the user
+                     * they needs to long press to really quit. */
+                    ui_show_alert(app,"Long press to exit",1000);
+                }
+            } else if (input.type == InputTypeLong &&
+                       input.key == InputKeyBack)
+            {
                 app->running = 0;
             } else if (input.type == InputTypeShort &&
                        input.key == InputKeyRight &&
-                       get_current_subview(app) == 0)
+                       ui_get_current_subview(app) == 0)
             {
                 /* Go to the next view. */
                 app_switch_view(app,ViewGoNext);
             } else if (input.type == InputTypeShort &&
                        input.key == InputKeyLeft &&
-                       get_current_subview(app) == 0)
+                       ui_get_current_subview(app) == 0)
             {
                 /* Go to the previous view. */
                 app_switch_view(app,ViewGoPrev);

+ 18 - 6
app.h

@@ -116,6 +116,7 @@ typedef struct ProtoViewMsgInfo {
 } ProtoViewMsgInfo;
 
 /* Our main application context. */
+#define ALERT_MAX_LEN 32
 struct ProtoViewApp {
     /* GUI */
     Gui *gui;
@@ -125,6 +126,8 @@ struct ProtoViewApp {
     ProtoViewCurrentView current_view;      /* Active left-right view ID. */
     int current_subview[ViewLast];  /* Active up-down subview ID. */
     FuriMessageQueue *event_queue;  /* Keypress events go here. */
+
+    /* Input text state. */
     ViewDispatcher *view_dispatcher; /* Used only when we want to show
                                         the text_input view for a moment.
                                         Otherwise it is set to null. */
@@ -134,6 +137,12 @@ struct ProtoViewApp {
     uint32_t text_input_buffer_len;
     void (*text_input_done_callback)(void*);
 
+    /* Alert state. */
+    uint32_t alert_dismiss_time;    /* Millisecond when the alert will be
+                                       no longer shown. Or zero if the alert
+                                       is currently not set at all. */
+    char alert_text[ALERT_MAX_LEN]; /* Alert content. */
+
     /* Radio related. */
     ProtoViewTxRx *txrx;     /* Radio state. */
     SubGhzSetting *setting;  /* A list of valid frequencies. */
@@ -222,13 +231,16 @@ void view_exit_direct_sampling(ProtoViewApp *app);
 void view_exit_settings(ProtoViewApp *app);
 
 /* ui.c */
-int get_current_subview(ProtoViewApp *app);
-void show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview);
-bool process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview);
-void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color);
-void show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
+int ui_get_current_subview(ProtoViewApp *app);
+void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview);
+bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview);
+void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
                    void (*done_callback)(void*));
-void dismiss_keyboard(ProtoViewApp *app);
+void ui_dismiss_keyboard(ProtoViewApp *app);
+void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl);
+void ui_dismiss_alert(ProtoViewApp *app);
+void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app);
+void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color);
 
 /* crc.c */
 uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly);

+ 49 - 7
ui.c

@@ -10,17 +10,17 @@
 
 /* Return the ID of the currently selected subview, of the current
  * view. */
-int get_current_subview(ProtoViewApp *app) {
+int ui_get_current_subview(ProtoViewApp *app) {
     return app->current_subview[app->current_view];
 }
 
 /* Called by view rendering callback that has subviews, to show small triangles
  * facing down/up if there are other subviews the user can access with up
  * and down. */
-void show_available_subviews(Canvas *canvas, ProtoViewApp *app,
+void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app,
                              int last_subview)
 {
-    int subview = get_current_subview(app);
+    int subview = ui_get_current_subview(app);
     if (subview != 0)
         canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop);
     if (subview != last_subview-1)
@@ -30,8 +30,8 @@ void show_available_subviews(Canvas *canvas, ProtoViewApp *app,
 /* Handle up/down keys when we are in a subview. If the function catched
  * such keypress, it returns true, so that the actual view input callback
  * knows it can just return ASAP without doing anything. */
-bool process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) {
-    int subview = get_current_subview(app);
+bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) {
+    int subview = ui_get_current_subview(app);
     if (input.type == InputTypePress) {
         if (input.key == InputKeyUp) {
             if (subview != 0)
@@ -62,7 +62,7 @@ bool process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subvie
  *
  * Note: if the buffer is not a null-termined zero string, what it contains will
  * be used as initial input for the user. */
-void show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
+void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
                    void (*done_callback)(void*))
 {
     app->show_text_input = true;
@@ -71,10 +71,52 @@ void show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen,
     app->text_input_done_callback = done_callback;
 }
 
-void dismiss_keyboard(ProtoViewApp *app) {
+void ui_dismiss_keyboard(ProtoViewApp *app) {
     view_dispatcher_stop(app->view_dispatcher);
 }
 
+/* ================================= Alert ================================== */
+
+/* Set an alert message to be shown over any currently active view, for
+ * the specified amount of time of 'ttl' milliseconds. */
+void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) {
+    app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl);
+    snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text);
+}
+
+/* Cancel the alert before its time has elapsed. */
+void ui_dismiss_alert(ProtoViewApp *app) {
+    app->alert_dismiss_time = 0;
+}
+
+/* Show the alert if an alert is set. This is called after the currently
+ * active view displayed its stuff, so we overwrite the screen with the
+ * alert message. */
+void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) {
+    if (app->alert_dismiss_time == 0) {
+        /* No active alert. */
+        return;
+    } else if (app->alert_dismiss_time < furi_get_tick()) {
+        /* Alert just expired. */
+        ui_dismiss_alert(app);
+        return;
+    }
+
+    /* Show the alert. A box with black border and a text inside. */
+    canvas_set_font(canvas, FontPrimary);
+    uint8_t w = canvas_string_width(canvas, app->alert_text);
+    uint8_t h = 8; // Font height.
+    uint8_t text_x = 64-(w/2);
+    uint8_t text_y = 32+4;
+    uint8_t padding = 3;
+    canvas_set_color(canvas,ColorBlack);
+    canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2);
+    canvas_set_color(canvas,ColorWhite);
+    canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2);
+    canvas_set_color(canvas,ColorBlack);
+    canvas_draw_str(canvas,text_x,text_y,app->alert_text);
+}
+
 /* =========================== Canvas extensions ============================ */
 
 void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color)

+ 10 - 6
view_info.c

@@ -96,7 +96,7 @@ void render_view_info(Canvas *const canvas, ProtoViewApp *app) {
         return;
     }
 
-    show_available_subviews(canvas,app,SubViewInfoLast);
+    ui_show_available_subviews(canvas,app,SubViewInfoLast);
     switch(app->current_subview[app->current_view]) {
     case SubViewInfoMain: render_subview_main(canvas,app); break;
     case SubViewInfoSave: render_subview_save(canvas,app); break;
@@ -115,7 +115,7 @@ void text_input_done_callback(void* context) {
     furi_string_free(save_path);
 
     free(privdata->filename);
-    dismiss_keyboard(app);
+    ui_dismiss_keyboard(app);
 }
 
 /* Replace all the occurrences of character c1 with c2 in the specified
@@ -253,9 +253,13 @@ void notify_signal_sent(ProtoViewApp *app) {
 
 /* Handle input for the info view. */
 void process_input_info(ProtoViewApp *app, InputEvent input) {
-    if (process_subview_updown(app,input,SubViewInfoLast)) return;
+    /* If we don't have a decoded signal, we don't allow to go up/down
+     * in the subviews: they are only useful when a loaded signal. */
+    if (app->signal_decoded &&
+        ui_process_subview_updown(app,input,SubViewInfoLast)) return;
+
     InfoViewPrivData *privdata = app->view_privdata;
-    int subview = get_current_subview(app);
+    int subview = ui_get_current_subview(app);
 
     /* Main subview. */
     if (subview == SubViewInfoMain) {
@@ -274,8 +278,8 @@ void process_input_info(ProtoViewApp *app, InputEvent input) {
         {
             privdata->filename = malloc(SAVE_FILENAME_LEN);
             set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
-            show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
-                          text_input_done_callback);
+            ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
+                             text_input_done_callback);
         } else if (input.type == InputTypeShort && input.key == InputKeyOk) {
             SendSignalCtx send_state;
             send_signal_init(&send_state,app);