|
@@ -1,8 +1,9 @@
|
|
|
|
|
+#include "flipp_pomodoro_app_i.h"
|
|
|
#include <furi.h>
|
|
#include <furi.h>
|
|
|
-#include <furi_hal.h>
|
|
|
|
|
|
|
|
|
|
#include <notification/notification_messages.h>
|
|
#include <notification/notification_messages.h>
|
|
|
#include <gui/gui.h>
|
|
#include <gui/gui.h>
|
|
|
|
|
+#include <dolphin/dolphin.h>
|
|
|
#include <gui/elements.h>
|
|
#include <gui/elements.h>
|
|
|
#include <input/input.h>
|
|
#include <input/input.h>
|
|
|
|
|
|
|
@@ -10,8 +11,6 @@
|
|
|
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
|
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
|
|
|
#include "flipp_pomodoro_icons.h"
|
|
#include "flipp_pomodoro_icons.h"
|
|
|
|
|
|
|
|
-const int SECONDS_IN_MINUTE = 60;
|
|
|
|
|
-
|
|
|
|
|
/// @brief Actions to be processed in a queue
|
|
/// @brief Actions to be processed in a queue
|
|
|
typedef enum {
|
|
typedef enum {
|
|
|
TimerTickType = 42,
|
|
TimerTickType = 42,
|
|
@@ -97,42 +96,17 @@ static const PomodoroStage stage_rotaion_map[] = {
|
|
|
[Rest] = Work,
|
|
[Rest] = Work,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-static const int32_t stage_duration_seconds_map[] = {
|
|
|
|
|
- [Work] = 25 * SECONDS_IN_MINUTE,
|
|
|
|
|
- [Rest] = 5 * SECONDS_IN_MINUTE,
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
const PomodoroStage default_stage = Work;
|
|
const PomodoroStage default_stage = Work;
|
|
|
|
|
|
|
|
-/// @brief Container for a time period
|
|
|
|
|
-typedef struct {
|
|
|
|
|
- uint8_t seconds;
|
|
|
|
|
- uint8_t minutes;
|
|
|
|
|
- uint32_t total_seconds;
|
|
|
|
|
-} TimeDifference;
|
|
|
|
|
-
|
|
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
PomodoroStage stage;
|
|
PomodoroStage stage;
|
|
|
uint32_t started_at_timestamp;
|
|
uint32_t started_at_timestamp;
|
|
|
} FlippPomodoroState;
|
|
} FlippPomodoroState;
|
|
|
|
|
|
|
|
-/// @brief Calculates difference between two provided timestamps
|
|
|
|
|
-/// @param begin - start timestamp of the period
|
|
|
|
|
-/// @param end - end timestamp of the period to measure
|
|
|
|
|
-/// @return TimeDifference struct
|
|
|
|
|
-static TimeDifference get_timestamp_difference_seconds(uint32_t begin, uint32_t end) {
|
|
|
|
|
- const uint32_t duration_seconds = end - begin;
|
|
|
|
|
-
|
|
|
|
|
- uint32_t minutes = (duration_seconds / SECONDS_IN_MINUTE) % SECONDS_IN_MINUTE;
|
|
|
|
|
- uint32_t seconds = duration_seconds % SECONDS_IN_MINUTE;
|
|
|
|
|
-
|
|
|
|
|
- return (TimeDifference){.total_seconds=duration_seconds, .minutes=minutes, .seconds=seconds};
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
static void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
|
|
static void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
|
|
|
furi_assert(state);
|
|
furi_assert(state);
|
|
|
state->stage = stage_rotaion_map[state->stage];
|
|
state->stage = stage_rotaion_map[state->stage];
|
|
|
- state->started_at_timestamp = furi_hal_rtc_get_timestamp();
|
|
|
|
|
|
|
+ state->started_at_timestamp = time_now();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
|
|
static char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
|
|
@@ -146,25 +120,32 @@ static void flipp_pomodoro__destroy(FlippPomodoroState* state) {
|
|
|
free(state);
|
|
free(state);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) {
|
|
|
|
|
+ const int32_t stage_duration_seconds_map[] = {
|
|
|
|
|
+ [Work] = 25 * TIME_SECONDS_IN_MINUTE,
|
|
|
|
|
+ [Rest] = 5 * TIME_SECONDS_IN_MINUTE,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return stage_duration_seconds_map[state->stage];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
static uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) {
|
|
static uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) {
|
|
|
- return state->started_at_timestamp + stage_duration_seconds_map[state->stage];
|
|
|
|
|
|
|
+ return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) {
|
|
static TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) {
|
|
|
- const uint32_t now = furi_hal_rtc_get_timestamp();
|
|
|
|
|
const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state);
|
|
const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state);
|
|
|
- return get_timestamp_difference_seconds(now, stage_ends_at);
|
|
|
|
|
|
|
+ return time_difference_seconds(time_now(), stage_ends_at);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) {
|
|
static bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) {
|
|
|
- const uint32_t now = furi_hal_rtc_get_timestamp();
|
|
|
|
|
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
|
|
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
|
|
|
const uint8_t seamless_change_span_seconds = 1;
|
|
const uint8_t seamless_change_span_seconds = 1;
|
|
|
- return (now - seamless_change_span_seconds) >= expired_by;
|
|
|
|
|
|
|
+ return (time_now() - seamless_change_span_seconds) >= expired_by;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static FlippPomodoroState flipp_pomodoro__new() {
|
|
static FlippPomodoroState flipp_pomodoro__new() {
|
|
|
- const uint32_t now = furi_hal_rtc_get_timestamp();
|
|
|
|
|
|
|
+ const uint32_t now = time_now();
|
|
|
const FlippPomodoroState new_state = {.stage=default_stage, .started_at_timestamp=now};
|
|
const FlippPomodoroState new_state = {.stage=default_stage, .started_at_timestamp=now};
|
|
|
return new_state;
|
|
return new_state;
|
|
|
}
|
|
}
|
|
@@ -238,23 +219,30 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
|
|
|
furi_message_queue_put(event_queue, &action, 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;
|
|
|
|
|
|
|
+static bool is_input_event(Action action) {
|
|
|
|
|
+ return action.type == InputEventType;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static bool is_press_or_repeat(InputEvent* input_event) {
|
|
|
|
|
+ return (input_event->type == InputTypePress) || (input_event->type == InputTypeRepeat);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static bool is_button_back_pressed(Action action) {
|
|
|
|
|
+ return is_input_event(action)
|
|
|
|
|
+ && is_press_or_repeat(action.payload)
|
|
|
|
|
+ && ((InputEvent*)action.payload)->key == InputKeyBack;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static bool is_button_right_pressed(Action action) {
|
|
|
|
|
+ return is_input_event(action)
|
|
|
|
|
+ && is_press_or_repeat(action.payload)
|
|
|
|
|
+ && ((InputEvent*)action.payload)->key == InputKeyRight;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+static bool is_timer_tick(Action action) {
|
|
|
|
|
+ return action.type == TimerTickType;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
int32_t flipp_pomodoro_main(void* p) {
|
|
int32_t flipp_pomodoro_main(void* p) {
|
|
|
UNUSED(p);
|
|
UNUSED(p);
|
|
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
|
|
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
|
|
@@ -279,29 +267,45 @@ int32_t flipp_pomodoro_main(void* p) {
|
|
|
|
|
|
|
|
|
|
|
|
|
bool running = true;
|
|
bool running = true;
|
|
|
|
|
+
|
|
|
|
|
+ const int queue_reading_window_tics = 200;
|
|
|
|
|
+
|
|
|
while(running) {
|
|
while(running) {
|
|
|
Action action;
|
|
Action action;
|
|
|
- if(furi_message_queue_get(event_queue, &action, 200) != FuriStatusOk) {
|
|
|
|
|
|
|
+ if(furi_message_queue_get(event_queue, &action, queue_reading_window_tics) != FuriStatusOk) {
|
|
|
|
|
+ // Queue read is failed
|
|
|
continue;
|
|
continue;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
if(!action.type) {
|
|
if(!action.type) {
|
|
|
|
|
+ // No item in queue
|
|
|
continue;
|
|
continue;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- switch (action.type) {
|
|
|
|
|
- case InputEventType:
|
|
|
|
|
- running = input_events_reducer(&state, action.payload);
|
|
|
|
|
- break;
|
|
|
|
|
- case TimerTickType:
|
|
|
|
|
|
|
+
|
|
|
|
|
+ if(is_button_back_pressed(action)) {
|
|
|
|
|
+ running = false;
|
|
|
|
|
+ continue;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ if(is_timer_tick(action)) {
|
|
|
if(flipp_pomodoro__is_stage_expired(&state)) {
|
|
if(flipp_pomodoro__is_stage_expired(&state)) {
|
|
|
|
|
+ if(state.stage == Work) {
|
|
|
|
|
+ // REGISTER a deed on work stage complete to get an acheivement
|
|
|
|
|
+ DOLPHIN_DEED(DolphinDeedPluginGameWin);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
flipp_pomodoro__toggle_stage(&state);
|
|
flipp_pomodoro__toggle_stage(&state);
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
notification_message(notification_app, stage_start_notification_sequence_map[state.stage]);
|
|
notification_message(notification_app, stage_start_notification_sequence_map[state.stage]);
|
|
|
};
|
|
};
|
|
|
- break;
|
|
|
|
|
- default:
|
|
|
|
|
- break;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if(is_button_right_pressed(action)) {
|
|
|
|
|
+ flipp_pomodoro__toggle_stage(&state);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
view_port_update(view_port); // Only re-draw on event
|
|
view_port_update(view_port); // Only re-draw on event
|
|
|
}
|
|
}
|
|
|
|
|
|