Skip to content

Commit

Permalink
feat: add happiness management (#10)
Browse files Browse the repository at this point in the history
* small refactoring of time functions
* Add deed on pomodoro complete
* Event queue refactoring
  • Loading branch information
Th3Un1q3 committed Jan 2, 2023
1 parent 57b1487 commit fa63582
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 62 deletions.
4 changes: 2 additions & 2 deletions application.fam
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ App(
name="Flipp Pomodoro",
apptype=FlipperAppType.EXTERNAL,
entry_point="flipp_pomodoro_main",
requires=["gui", "notification"],
requires=["gui", "notification", "dolphin"],
stack_size=1 * 1024,
fap_category="Productivity",
fap_icon_assets="images",
fap_icon="flipp_pomodoro_10.png",
)
)
124 changes: 64 additions & 60 deletions flipp_pomodoro.c → flipp_pomodoro_app.c
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
#include "flipp_pomodoro_app_i.h"
#include <furi.h>
#include <furi_hal.h>

#include <notification/notification_messages.h>
#include <gui/gui.h>
#include <dolphin/dolphin.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"

const int SECONDS_IN_MINUTE = 60;

/// @brief Actions to be processed in a queue
typedef enum {
TimerTickType = 42,
Expand Down Expand Up @@ -97,42 +96,17 @@ static const PomodoroStage stage_rotaion_map[] = {
[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;

/// @brief Container for a time period
typedef struct {
uint8_t seconds;
uint8_t minutes;
uint32_t total_seconds;
} TimeDifference;

typedef struct {
PomodoroStage stage;
uint32_t started_at_timestamp;
} 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) {
furi_assert(state);
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) {
Expand All @@ -146,25 +120,32 @@ static void flipp_pomodoro__destroy(FlippPomodoroState* 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) {
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) {
const uint32_t now = furi_hal_rtc_get_timestamp();
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) {
const uint32_t now = furi_hal_rtc_get_timestamp();
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
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() {
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};
return new_state;
}
Expand Down Expand Up @@ -238,23 +219,30 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
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) {
UNUSED(p);
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(Action));
Expand All @@ -279,29 +267,45 @@ int32_t flipp_pomodoro_main(void* p) {


bool running = true;

const int queue_reading_window_tics = 200;

while(running) {
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;
};

if(!action.type) {
// No item in queue
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(state.stage == Work) {
// REGISTER a deed on work stage complete to get an acheivement
DOLPHIN_DEED(DolphinDeedPluginGameWin);
};

flipp_pomodoro__toggle_stage(&state);



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
}

Expand Down
9 changes: 9 additions & 0 deletions flipp_pomodoro_app_i.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

/**
* Index of dependencies for the main app
*/

#include <furi.h>
#include <furi_hal.h>
#include "helpers/time.h"
21 changes: 21 additions & 0 deletions helpers/time.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include <furi.h>
#include <furi_hal.h>
#include "time.h"

const int TIME_SECONDS_IN_MINUTE = 60;
const int TIME_MINUTES_IN_HOUR = 60;

uint32_t time_now() {
return furi_hal_rtc_get_timestamp();
};

TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) {
const uint32_t duration_seconds = end - begin;

uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR;
uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE;

return (TimeDifference){.total_seconds=duration_seconds, .minutes=minutes, .seconds=seconds};
}


25 changes: 25 additions & 0 deletions helpers/time.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <furi.h>
#include <furi_hal.h>

extern const int TIME_SECONDS_IN_MINUTE;
extern const int TIME_MINUTES_IN_HOUR;

/// @brief Container for a time period
typedef struct {
uint8_t seconds;
uint8_t minutes;
uint32_t total_seconds;
} TimeDifference;

/// @brief Time by the moment of calling
/// @return A timestamp(seconds percision)
uint32_t time_now();

/// @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
TimeDifference time_difference_seconds(uint32_t begin, uint32_t end);

0 comments on commit fa63582

Please sign in to comment.