Mikhail G 3 лет назад
Родитель
Сommit
b3f442d003
4 измененных файлов с 127 добавлено и 31 удалено
  1. 1 0
      application.fam
  2. 126 31
      flipp_pomodoro.c
  3. BIN
      flipp_pomodoro_10.png
  4. BIN
      images/flipp_pomodoro_14.png

+ 1 - 0
application.fam

@@ -7,4 +7,5 @@ App(
     stack_size=1 * 1024,
     fap_category="Productivity",
     fap_icon_assets="images",
+    fap_icon="flipp_pomodoro_10.png",
 )

+ 126 - 31
flipp_pomodoro.c

@@ -2,80 +2,175 @@
 #include <furi_hal.h>
 
 #include <gui/gui.h>
+#include <gui/elements.h>
 #include <input/input.h>
 
 /* Magic happens here -- this file is generated by fbt.
  * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
 #include "flipp_pomodoro_icons.h"
 
+typedef enum {
+    TimerTickType,
+    InputEventType,
+} ActionType;
+
+typedef struct {
+    ActionType type;
+    void* payload;
+} Action;
+
+/**
+ * Flipp Pomodoro state management
+*/
+
+typedef enum {
+    Work,
+    Rest,
+} PomodoroStage;
+
 typedef struct {
-    uint8_t x, y;
-} ImagePosition;
+    uint8_t seconds;
+    uint8_t minutes;
+    uint8_t hours;
+    uint32_t total_seconds;
+} TimeDifference;
 
-static ImagePosition image_position = {.x = 0, .y = 0};
+typedef struct {
+    PomodoroStage stage;
+    uint32_t started_at_timestamp;
+} FlippPomodoroState;
+
+/// @brief Calculates difference between two provided timestamps
+/// @param begin 
+/// @param end 
+/// @return 
+static TimeDifference get_timestamp_difference_seconds(uint32_t begin, uint32_t end) {
+    const uint32_t duration_seconds = end - begin;
+    return (TimeDifference){.total_seconds=duration_seconds};
+}
+
+static void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
+    state->stage = state->stage == Work ? Rest : Work;
+    state->started_at_timestamp = furi_hal_rtc_get_timestamp();
+}
+
+static char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
+    return state->stage == Work ? "To rest" : "To work";
+};
+
+static TimeDifference flipp_pomodoro__stage_duration(FlippPomodoroState* state) {
+    const uint32_t now = furi_hal_rtc_get_timestamp();
+    return get_timestamp_difference_seconds(state->started_at_timestamp, now);
+}
+
+static void flipp_pomodoro__destroy(FlippPomodoroState* state) {
+    free(state);
+}
+
+static FlippPomodoroState flipp_pomodoro__new() {
+    const uint32_t now = furi_hal_rtc_get_timestamp();
+    const FlippPomodoroState new_state = {.stage=Work, .started_at_timestamp=now};
+    return new_state;
+}
 
 // Screen is 128x64 px
 static void app_draw_callback(Canvas* canvas, void* ctx) {
-    UNUSED(ctx);
-
+    // WARNING: place no side-effects into rener cycle
     canvas_clear(canvas);
-    canvas_draw_icon(canvas, image_position.x % 128, image_position.y % 64, &I_dolphin_71x25);
+    FlippPomodoroState* state = ctx;
+
+    FuriString* timer_string = furi_string_alloc();
+
+    const uint32_t now = flipp_pomodoro__stage_duration(state).total_seconds;
+
+    furi_string_printf(timer_string, "%lu", now);
+
+    elements_text_box(canvas, 50, 20, 30, 50, AlignCenter, AlignCenter, furi_string_get_cstr(timer_string), false);
+    elements_button_right(canvas, flipp_pomodoro__next_stage_label(state));
+
+    furi_string_free(timer_string);
+}
+
+static void clock_tick_callback(void* ctx) {
+
+    furi_assert(ctx);
+    FuriMessageQueue* queue = ctx;
+    Action action = {.type = TimerTickType};
+    // It's OK to loose this event if system overloaded
+    furi_message_queue_put(queue, &action, 0);
 }
 
 static void app_input_callback(InputEvent* input_event, void* ctx) {
     furi_assert(ctx);
 
+    Action action = {.type=InputEventType, .payload=input_event};
+
     FuriMessageQueue* event_queue = ctx;
-    furi_message_queue_put(event_queue, input_event, FuriWaitForever);
+    furi_message_queue_put(event_queue, &action, FuriWaitForever);
+};
+
+static bool input_events_reducer (FlippPomodoroState* state, InputEvent* input_event) {
+    bool keep_running = true;
+    if((input_event->type == InputTypePress) || (input_event->type == InputTypeRepeat)) {
+        switch(input_event->key) {
+        case InputKeyRight:
+            flipp_pomodoro__toggle_stage(state);
+            break;
+        case InputKeyBack:
+            keep_running = false;
+            break;
+        default:
+            break;
+        }
+    }
+    return keep_running;
 }
 
 int32_t flipp_pomodoro_main(void* p) {
     UNUSED(p);
-    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
+    FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
+
+    FlippPomodoroState state = flipp_pomodoro__new();
 
     // Configure view port
     ViewPort* view_port = view_port_alloc();
-    view_port_draw_callback_set(view_port, app_draw_callback, view_port);
+    view_port_draw_callback_set(view_port, app_draw_callback, &state);
     view_port_input_callback_set(view_port, app_input_callback, event_queue);
 
+    FuriTimer* timer = furi_timer_alloc(clock_tick_callback, FuriTimerTypePeriodic, &event_queue);
+
     // Register view port in GUI
     Gui* gui = furi_record_open(RECORD_GUI);
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 
-    InputEvent event;
+    furi_timer_start(timer, 500);
+
+    Action action;
 
     bool running = true;
     while(running) {
-        if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
-            if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
-                switch(event.key) {
-                case InputKeyLeft:
-                    image_position.x -= 2;
-                    break;
-                case InputKeyRight:
-                    image_position.x += 2;
-                    break;
-                case InputKeyUp:
-                    image_position.y -= 2;
-                    break;
-                case InputKeyDown:
-                    image_position.y += 2;
-                    break;
-                default:
-                    running = false;
-                    break;
-                }
+        if(furi_message_queue_get(event_queue, &action, 200) == FuriStatusOk) {
+            switch (action.type) {
+            case InputEventType:
+                running = input_events_reducer(&state, action.payload);
+                break;
+            case TimerTickType:
+                // TODO: track time is over and make switch
+                break;
+            default:
+                break;
             }
+            view_port_update(view_port);
         }
-        view_port_update(view_port);
     }
 
     view_port_enabled_set(view_port, false);
     gui_remove_view_port(gui, view_port);
     view_port_free(view_port);
     furi_message_queue_free(event_queue);
-
     furi_record_close(RECORD_GUI);
+    furi_timer_free(timer);
+    flipp_pomodoro__destroy(&state);
 
     return 0;
 }

BIN
flipp_pomodoro_10.png


BIN
images/flipp_pomodoro_14.png