Skip to content

Commit b3f442d

Browse files
committed
add timer stage toggl
1 parent f49e172 commit b3f442d

File tree

4 files changed

+127
-31
lines changed

4 files changed

+127
-31
lines changed

application.fam

+1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ App(
77
stack_size=1 * 1024,
88
fap_category="Productivity",
99
fap_icon_assets="images",
10+
fap_icon="flipp_pomodoro_10.png",
1011
)

flipp_pomodoro.c

+126-31
Original file line numberDiff line numberDiff line change
@@ -2,80 +2,175 @@
22
#include <furi_hal.h>
33

44
#include <gui/gui.h>
5+
#include <gui/elements.h>
56
#include <input/input.h>
67

78
/* Magic happens here -- this file is generated by fbt.
89
* Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
910
#include "flipp_pomodoro_icons.h"
1011

12+
typedef enum {
13+
TimerTickType,
14+
InputEventType,
15+
} ActionType;
16+
17+
typedef struct {
18+
ActionType type;
19+
void* payload;
20+
} Action;
21+
22+
/**
23+
* Flipp Pomodoro state management
24+
*/
25+
26+
typedef enum {
27+
Work,
28+
Rest,
29+
} PomodoroStage;
30+
1131
typedef struct {
12-
uint8_t x, y;
13-
} ImagePosition;
32+
uint8_t seconds;
33+
uint8_t minutes;
34+
uint8_t hours;
35+
uint32_t total_seconds;
36+
} TimeDifference;
1437

15-
static ImagePosition image_position = {.x = 0, .y = 0};
38+
typedef struct {
39+
PomodoroStage stage;
40+
uint32_t started_at_timestamp;
41+
} FlippPomodoroState;
42+
43+
/// @brief Calculates difference between two provided timestamps
44+
/// @param begin
45+
/// @param end
46+
/// @return
47+
static TimeDifference get_timestamp_difference_seconds(uint32_t begin, uint32_t end) {
48+
const uint32_t duration_seconds = end - begin;
49+
return (TimeDifference){.total_seconds=duration_seconds};
50+
}
51+
52+
static void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
53+
state->stage = state->stage == Work ? Rest : Work;
54+
state->started_at_timestamp = furi_hal_rtc_get_timestamp();
55+
}
56+
57+
static char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
58+
return state->stage == Work ? "To rest" : "To work";
59+
};
60+
61+
static TimeDifference flipp_pomodoro__stage_duration(FlippPomodoroState* state) {
62+
const uint32_t now = furi_hal_rtc_get_timestamp();
63+
return get_timestamp_difference_seconds(state->started_at_timestamp, now);
64+
}
65+
66+
static void flipp_pomodoro__destroy(FlippPomodoroState* state) {
67+
free(state);
68+
}
69+
70+
static FlippPomodoroState flipp_pomodoro__new() {
71+
const uint32_t now = furi_hal_rtc_get_timestamp();
72+
const FlippPomodoroState new_state = {.stage=Work, .started_at_timestamp=now};
73+
return new_state;
74+
}
1675

1776
// Screen is 128x64 px
1877
static void app_draw_callback(Canvas* canvas, void* ctx) {
19-
UNUSED(ctx);
20-
78+
// WARNING: place no side-effects into rener cycle
2179
canvas_clear(canvas);
22-
canvas_draw_icon(canvas, image_position.x % 128, image_position.y % 64, &I_dolphin_71x25);
80+
FlippPomodoroState* state = ctx;
81+
82+
FuriString* timer_string = furi_string_alloc();
83+
84+
const uint32_t now = flipp_pomodoro__stage_duration(state).total_seconds;
85+
86+
furi_string_printf(timer_string, "%lu", now);
87+
88+
elements_text_box(canvas, 50, 20, 30, 50, AlignCenter, AlignCenter, furi_string_get_cstr(timer_string), false);
89+
elements_button_right(canvas, flipp_pomodoro__next_stage_label(state));
90+
91+
furi_string_free(timer_string);
92+
}
93+
94+
static void clock_tick_callback(void* ctx) {
95+
96+
furi_assert(ctx);
97+
FuriMessageQueue* queue = ctx;
98+
Action action = {.type = TimerTickType};
99+
// It's OK to loose this event if system overloaded
100+
furi_message_queue_put(queue, &action, 0);
23101
}
24102

25103
static void app_input_callback(InputEvent* input_event, void* ctx) {
26104
furi_assert(ctx);
27105

106+
Action action = {.type=InputEventType, .payload=input_event};
107+
28108
FuriMessageQueue* event_queue = ctx;
29-
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
109+
furi_message_queue_put(event_queue, &action, FuriWaitForever);
110+
};
111+
112+
static bool input_events_reducer (FlippPomodoroState* state, InputEvent* input_event) {
113+
bool keep_running = true;
114+
if((input_event->type == InputTypePress) || (input_event->type == InputTypeRepeat)) {
115+
switch(input_event->key) {
116+
case InputKeyRight:
117+
flipp_pomodoro__toggle_stage(state);
118+
break;
119+
case InputKeyBack:
120+
keep_running = false;
121+
break;
122+
default:
123+
break;
124+
}
125+
}
126+
return keep_running;
30127
}
31128

32129
int32_t flipp_pomodoro_main(void* p) {
33130
UNUSED(p);
34-
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
131+
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
132+
133+
FlippPomodoroState state = flipp_pomodoro__new();
35134

36135
// Configure view port
37136
ViewPort* view_port = view_port_alloc();
38-
view_port_draw_callback_set(view_port, app_draw_callback, view_port);
137+
view_port_draw_callback_set(view_port, app_draw_callback, &state);
39138
view_port_input_callback_set(view_port, app_input_callback, event_queue);
40139

140+
FuriTimer* timer = furi_timer_alloc(clock_tick_callback, FuriTimerTypePeriodic, &event_queue);
141+
41142
// Register view port in GUI
42143
Gui* gui = furi_record_open(RECORD_GUI);
43144
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
44145

45-
InputEvent event;
146+
furi_timer_start(timer, 500);
147+
148+
Action action;
46149

47150
bool running = true;
48151
while(running) {
49-
if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
50-
if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
51-
switch(event.key) {
52-
case InputKeyLeft:
53-
image_position.x -= 2;
54-
break;
55-
case InputKeyRight:
56-
image_position.x += 2;
57-
break;
58-
case InputKeyUp:
59-
image_position.y -= 2;
60-
break;
61-
case InputKeyDown:
62-
image_position.y += 2;
63-
break;
64-
default:
65-
running = false;
66-
break;
67-
}
152+
if(furi_message_queue_get(event_queue, &action, 200) == FuriStatusOk) {
153+
switch (action.type) {
154+
case InputEventType:
155+
running = input_events_reducer(&state, action.payload);
156+
break;
157+
case TimerTickType:
158+
// TODO: track time is over and make switch
159+
break;
160+
default:
161+
break;
68162
}
163+
view_port_update(view_port);
69164
}
70-
view_port_update(view_port);
71165
}
72166

73167
view_port_enabled_set(view_port, false);
74168
gui_remove_view_port(gui, view_port);
75169
view_port_free(view_port);
76170
furi_message_queue_free(event_queue);
77-
78171
furi_record_close(RECORD_GUI);
172+
furi_timer_free(timer);
173+
flipp_pomodoro__destroy(&state);
79174

80175
return 0;
81176
}

flipp_pomodoro_10.png

157 Bytes
Loading

images/flipp_pomodoro_14.png

195 Bytes
Loading

0 commit comments

Comments
 (0)