Skip to content

Commit fa63582

Browse files
authored
feat: add happiness management (#10)
* small refactoring of time functions * Add deed on pomodoro complete * Event queue refactoring
1 parent 57b1487 commit fa63582

File tree

5 files changed

+121
-62
lines changed

5 files changed

+121
-62
lines changed

application.fam

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ App(
33
name="Flipp Pomodoro",
44
apptype=FlipperAppType.EXTERNAL,
55
entry_point="flipp_pomodoro_main",
6-
requires=["gui", "notification"],
6+
requires=["gui", "notification", "dolphin"],
77
stack_size=1 * 1024,
88
fap_category="Productivity",
99
fap_icon_assets="images",
1010
fap_icon="flipp_pomodoro_10.png",
11-
)
11+
)

flipp_pomodoro.c flipp_pomodoro_app.c

+64-60
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
1+
#include "flipp_pomodoro_app_i.h"
12
#include <furi.h>
2-
#include <furi_hal.h>
33

44
#include <notification/notification_messages.h>
55
#include <gui/gui.h>
6+
#include <dolphin/dolphin.h>
67
#include <gui/elements.h>
78
#include <input/input.h>
89

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

13-
const int SECONDS_IN_MINUTE = 60;
14-
1514
/// @brief Actions to be processed in a queue
1615
typedef enum {
1716
TimerTickType = 42,
@@ -97,42 +96,17 @@ static const PomodoroStage stage_rotaion_map[] = {
9796
[Rest] = Work,
9897
};
9998

100-
static const int32_t stage_duration_seconds_map[] = {
101-
[Work] = 25 * SECONDS_IN_MINUTE,
102-
[Rest] = 5 * SECONDS_IN_MINUTE,
103-
};
104-
10599
const PomodoroStage default_stage = Work;
106100

107-
/// @brief Container for a time period
108-
typedef struct {
109-
uint8_t seconds;
110-
uint8_t minutes;
111-
uint32_t total_seconds;
112-
} TimeDifference;
113-
114101
typedef struct {
115102
PomodoroStage stage;
116103
uint32_t started_at_timestamp;
117104
} FlippPomodoroState;
118105

119-
/// @brief Calculates difference between two provided timestamps
120-
/// @param begin - start timestamp of the period
121-
/// @param end - end timestamp of the period to measure
122-
/// @return TimeDifference struct
123-
static TimeDifference get_timestamp_difference_seconds(uint32_t begin, uint32_t end) {
124-
const uint32_t duration_seconds = end - begin;
125-
126-
uint32_t minutes = (duration_seconds / SECONDS_IN_MINUTE) % SECONDS_IN_MINUTE;
127-
uint32_t seconds = duration_seconds % SECONDS_IN_MINUTE;
128-
129-
return (TimeDifference){.total_seconds=duration_seconds, .minutes=minutes, .seconds=seconds};
130-
}
131-
132106
static void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
133107
furi_assert(state);
134108
state->stage = stage_rotaion_map[state->stage];
135-
state->started_at_timestamp = furi_hal_rtc_get_timestamp();
109+
state->started_at_timestamp = time_now();
136110
}
137111

138112
static char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
@@ -146,25 +120,32 @@ static void flipp_pomodoro__destroy(FlippPomodoroState* state) {
146120
free(state);
147121
}
148122

123+
static uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) {
124+
const int32_t stage_duration_seconds_map[] = {
125+
[Work] = 25 * TIME_SECONDS_IN_MINUTE,
126+
[Rest] = 5 * TIME_SECONDS_IN_MINUTE,
127+
};
128+
129+
return stage_duration_seconds_map[state->stage];
130+
}
131+
149132
static uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) {
150-
return state->started_at_timestamp + stage_duration_seconds_map[state->stage];
133+
return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state);
151134
}
152135

153136
static TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) {
154-
const uint32_t now = furi_hal_rtc_get_timestamp();
155137
const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state);
156-
return get_timestamp_difference_seconds(now, stage_ends_at);
138+
return time_difference_seconds(time_now(), stage_ends_at);
157139
}
158140

159141
static bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) {
160-
const uint32_t now = furi_hal_rtc_get_timestamp();
161142
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
162143
const uint8_t seamless_change_span_seconds = 1;
163-
return (now - seamless_change_span_seconds) >= expired_by;
144+
return (time_now() - seamless_change_span_seconds) >= expired_by;
164145
}
165146

166147
static FlippPomodoroState flipp_pomodoro__new() {
167-
const uint32_t now = furi_hal_rtc_get_timestamp();
148+
const uint32_t now = time_now();
168149
const FlippPomodoroState new_state = {.stage=default_stage, .started_at_timestamp=now};
169150
return new_state;
170151
}
@@ -238,23 +219,30 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
238219
furi_message_queue_put(event_queue, &action, FuriWaitForever);
239220
};
240221

241-
static bool input_events_reducer (FlippPomodoroState* state, InputEvent* input_event) {
242-
bool keep_running = true;
243-
if((input_event->type == InputTypePress) || (input_event->type == InputTypeRepeat)) {
244-
switch(input_event->key) {
245-
case InputKeyRight:
246-
flipp_pomodoro__toggle_stage(state);
247-
break;
248-
case InputKeyBack:
249-
keep_running = false;
250-
break;
251-
default:
252-
break;
253-
}
254-
}
255-
return keep_running;
222+
static bool is_input_event(Action action) {
223+
return action.type == InputEventType;
224+
}
225+
226+
static bool is_press_or_repeat(InputEvent* input_event) {
227+
return (input_event->type == InputTypePress) || (input_event->type == InputTypeRepeat);
228+
}
229+
230+
static bool is_button_back_pressed(Action action) {
231+
return is_input_event(action)
232+
&& is_press_or_repeat(action.payload)
233+
&& ((InputEvent*)action.payload)->key == InputKeyBack;
234+
}
235+
236+
static bool is_button_right_pressed(Action action) {
237+
return is_input_event(action)
238+
&& is_press_or_repeat(action.payload)
239+
&& ((InputEvent*)action.payload)->key == InputKeyRight;
256240
}
257241

242+
static bool is_timer_tick(Action action) {
243+
return action.type == TimerTickType;
244+
};
245+
258246
int32_t flipp_pomodoro_main(void* p) {
259247
UNUSED(p);
260248
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
@@ -279,29 +267,45 @@ int32_t flipp_pomodoro_main(void* p) {
279267

280268

281269
bool running = true;
270+
271+
const int queue_reading_window_tics = 200;
272+
282273
while(running) {
283274
Action action;
284-
if(furi_message_queue_get(event_queue, &action, 200) != FuriStatusOk) {
275+
if(furi_message_queue_get(event_queue, &action, queue_reading_window_tics) != FuriStatusOk) {
276+
// Queue read is failed
285277
continue;
286278
};
287279

288280
if(!action.type) {
281+
// No item in queue
289282
continue;
290283
}
291-
292-
switch (action.type) {
293-
case InputEventType:
294-
running = input_events_reducer(&state, action.payload);
295-
break;
296-
case TimerTickType:
284+
285+
if(is_button_back_pressed(action)) {
286+
running = false;
287+
continue;
288+
};
289+
290+
if(is_timer_tick(action)) {
297291
if(flipp_pomodoro__is_stage_expired(&state)) {
292+
if(state.stage == Work) {
293+
// REGISTER a deed on work stage complete to get an acheivement
294+
DOLPHIN_DEED(DolphinDeedPluginGameWin);
295+
};
296+
298297
flipp_pomodoro__toggle_stage(&state);
298+
299+
300+
299301
notification_message(notification_app, stage_start_notification_sequence_map[state.stage]);
300302
};
301-
break;
302-
default:
303-
break;
304303
}
304+
305+
if(is_button_right_pressed(action)) {
306+
flipp_pomodoro__toggle_stage(&state);
307+
};
308+
305309
view_port_update(view_port); // Only re-draw on event
306310
}
307311

flipp_pomodoro_app_i.h

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
/**
4+
* Index of dependencies for the main app
5+
*/
6+
7+
#include <furi.h>
8+
#include <furi_hal.h>
9+
#include "helpers/time.h"

helpers/time.c

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <furi.h>
2+
#include <furi_hal.h>
3+
#include "time.h"
4+
5+
const int TIME_SECONDS_IN_MINUTE = 60;
6+
const int TIME_MINUTES_IN_HOUR = 60;
7+
8+
uint32_t time_now() {
9+
return furi_hal_rtc_get_timestamp();
10+
};
11+
12+
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) {
13+
const uint32_t duration_seconds = end - begin;
14+
15+
uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR;
16+
uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE;
17+
18+
return (TimeDifference){.total_seconds=duration_seconds, .minutes=minutes, .seconds=seconds};
19+
}
20+
21+

helpers/time.h

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#pragma once
2+
3+
#include <furi.h>
4+
#include <furi_hal.h>
5+
6+
extern const int TIME_SECONDS_IN_MINUTE;
7+
extern const int TIME_MINUTES_IN_HOUR;
8+
9+
/// @brief Container for a time period
10+
typedef struct {
11+
uint8_t seconds;
12+
uint8_t minutes;
13+
uint32_t total_seconds;
14+
} TimeDifference;
15+
16+
/// @brief Time by the moment of calling
17+
/// @return A timestamp(seconds percision)
18+
uint32_t time_now();
19+
20+
/// @brief Calculates difference between two provided timestamps
21+
/// @param begin - start timestamp of the period
22+
/// @param end - end timestamp of the period to measure
23+
/// @return TimeDifference struct
24+
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end);
25+

0 commit comments

Comments
 (0)