pomodoro_timer.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include "pomodoro_timer.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <gui/elements.h>
  5. #include <notification/notification_messages.h>
  6. #include <pomodoro_timer_icons.h>
  7. const NotificationSequence sequence_finish = {
  8. &message_display_backlight_on,
  9. &message_green_255,
  10. &message_vibro_on,
  11. &message_note_c5,
  12. &message_delay_100,
  13. &message_vibro_off,
  14. &message_vibro_on,
  15. &message_note_e5,
  16. &message_delay_100,
  17. &message_vibro_off,
  18. &message_vibro_on,
  19. &message_note_g5,
  20. &message_delay_100,
  21. &message_vibro_off,
  22. &message_vibro_on,
  23. &message_note_b5,
  24. &message_delay_250,
  25. &message_vibro_off,
  26. &message_vibro_on,
  27. &message_note_c6,
  28. &message_delay_250,
  29. &message_vibro_off,
  30. &message_sound_off,
  31. NULL,
  32. };
  33. const NotificationSequence sequence_rest = {
  34. &message_display_backlight_on,
  35. &message_red_255,
  36. &message_vibro_on,
  37. &message_note_c6,
  38. &message_delay_100,
  39. &message_vibro_off,
  40. &message_vibro_on,
  41. &message_note_b5,
  42. &message_delay_100,
  43. &message_vibro_off,
  44. &message_vibro_on,
  45. &message_note_g5,
  46. &message_delay_100,
  47. &message_vibro_off,
  48. &message_vibro_on,
  49. &message_note_e5,
  50. &message_delay_100,
  51. &message_vibro_off,
  52. &message_vibro_on,
  53. &message_note_c5,
  54. &message_delay_250,
  55. &message_vibro_off,
  56. &message_sound_off,
  57. NULL,
  58. };
  59. void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event) {
  60. with_view_model(
  61. pomodoro_timer->view,
  62. PomodoroTimerModel * model,
  63. {
  64. if(event->type == InputTypePress) {
  65. if(event->key == InputKeyOk) {
  66. model->ok_pressed = true;
  67. } else if(event->key == InputKeyLeft) {
  68. model->reset_pressed = true;
  69. } else if(event->key == InputKeyBack) {
  70. model->back_pressed = true;
  71. }
  72. } else if(event->type == InputTypeRelease) {
  73. if(event->key == InputKeyOk) {
  74. model->ok_pressed = false;
  75. // START/STOP TIMER
  76. DateTime curr_dt;
  77. furi_hal_rtc_get_datetime(&curr_dt);
  78. uint32_t current_timestamp = datetime_datetime_to_timestamp(&curr_dt);
  79. // STARTED -> PAUSED
  80. if(model->timer_running) {
  81. // Update stopped seconds
  82. model->timer_stopped_seconds =
  83. current_timestamp - model->timer_start_timestamp;
  84. } else if(!model->time_passed) {
  85. // INITIAL -> STARTED
  86. model->timer_start_timestamp = current_timestamp;
  87. model->rest_running = false;
  88. } else {
  89. // PAUSED -> STARTED
  90. model->timer_start_timestamp =
  91. current_timestamp - model->timer_stopped_seconds;
  92. }
  93. model->timer_running = !model->timer_running;
  94. } else if(event->key == InputKeyLeft) {
  95. if(!model->timer_running) {
  96. furi_record_close(RECORD_NOTIFICATION);
  97. model->timer_stopped_seconds = 0;
  98. model->timer_start_timestamp = 0;
  99. model->time_passed = 0;
  100. model->timer_running = false;
  101. }
  102. model->reset_pressed = false;
  103. } else if(event->key == InputKeyBack) {
  104. model->back_pressed = false;
  105. }
  106. }
  107. },
  108. true);
  109. }
  110. void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest) {
  111. furi_assert(context);
  112. PomodoroTimerModel* model = context;
  113. DateTime curr_dt;
  114. furi_hal_rtc_get_datetime(&curr_dt);
  115. uint32_t current_timestamp = datetime_datetime_to_timestamp(&curr_dt);
  116. // Header
  117. canvas_set_font(canvas, FontPrimary);
  118. elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Pomodoro");
  119. canvas_draw_icon(canvas, 68, 1, &I_Pin_back_arrow_10x8);
  120. canvas_set_font(canvas, FontSecondary);
  121. elements_multiline_text_aligned(canvas, 127, 1, AlignRight, AlignTop, "Hold to exit");
  122. // Start/Pause/Continue
  123. int txt_main_y = 34;
  124. canvas_draw_icon(canvas, 63, 23, &I_Space_65x18); // button
  125. if(model->ok_pressed) {
  126. elements_slightly_rounded_box(canvas, 66, 25, 60, 13);
  127. canvas_set_color(canvas, ColorWhite);
  128. }
  129. if(model->timer_running) {
  130. model->time_passed = current_timestamp - model->timer_start_timestamp;
  131. elements_multiline_text_aligned(canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Pause");
  132. canvas_draw_box(canvas, 71, 27, 2, 8);
  133. canvas_draw_box(canvas, 75, 27, 2, 8);
  134. } else {
  135. if(model->time_passed) {
  136. elements_multiline_text_aligned(
  137. canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Continue");
  138. } else {
  139. elements_multiline_text_aligned(
  140. canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Start");
  141. }
  142. canvas_draw_icon(canvas, 70, 26, &I_Ok_btn_9x9); // OK icon
  143. }
  144. canvas_set_color(canvas, ColorBlack);
  145. // Reset
  146. if(!model->timer_running && model->time_passed) {
  147. canvas_draw_icon(canvas, 63, 46, &I_Space_65x18);
  148. if(model->reset_pressed) {
  149. elements_slightly_rounded_box(canvas, 66, 48, 60, 13);
  150. canvas_set_color(canvas, ColorWhite);
  151. }
  152. canvas_draw_icon(canvas, 72, 50, &I_ButtonLeft_4x7);
  153. elements_multiline_text_aligned(canvas, 83, 57, AlignLeft, AlignBottom, "Reset");
  154. canvas_set_color(canvas, ColorBlack);
  155. }
  156. char buffer[64];
  157. // Time to work
  158. int total_time_left = (max_seconds - (uint32_t)model->time_passed);
  159. int minutes_left = total_time_left / 60;
  160. int seconds_left = total_time_left % 60;
  161. canvas_set_font(canvas, FontBigNumbers);
  162. // Play sound
  163. if(total_time_left == 0 && !model->sound_playing) {
  164. model->sound_playing = true;
  165. notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish);
  166. }
  167. if(total_time_left < 0) {
  168. model->timer_running = false;
  169. model->time_passed = 0;
  170. model->sound_playing = false;
  171. model->rest_running = true;
  172. model->rest_start_timestamp = current_timestamp;
  173. seconds_left = 0;
  174. model->counter += 1;
  175. }
  176. if(!model->rest_running) {
  177. snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes_left, seconds_left);
  178. canvas_draw_str(canvas, 0, 39, buffer);
  179. }
  180. if(model->timer_running) {
  181. canvas_set_font(canvas, FontPrimary);
  182. elements_multiline_text_aligned(canvas, 0, 50, AlignLeft, AlignTop, "Time to work");
  183. }
  184. // Time to rest
  185. if(model->rest_running && !model->timer_running) {
  186. canvas_set_font(canvas, FontBigNumbers);
  187. int rest_passed = current_timestamp - model->rest_start_timestamp;
  188. int rest_total_time_left = (max_seconds_rest - rest_passed);
  189. int rest_minutes_left = rest_total_time_left / 60;
  190. int rest_seconds_left = rest_total_time_left % 60;
  191. // Play sound
  192. if(rest_total_time_left == 0 && !model->sound_playing) {
  193. model->sound_playing = true;
  194. notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_rest);
  195. }
  196. if(rest_total_time_left < 0) {
  197. rest_seconds_left = 0;
  198. model->rest_running = false;
  199. model->sound_playing = false;
  200. }
  201. snprintf(buffer, sizeof(buffer), "%02d:%02d", rest_minutes_left, rest_seconds_left);
  202. canvas_draw_str(canvas, 0, 60, buffer);
  203. canvas_set_font(canvas, FontPrimary);
  204. elements_multiline_text_aligned(canvas, 0, 27, AlignLeft, AlignTop, "Have a rest");
  205. }
  206. // Clocks
  207. canvas_set_font(canvas, FontSecondary);
  208. snprintf(
  209. buffer,
  210. sizeof(buffer),
  211. "%02ld:%02ld:%02ld",
  212. ((uint32_t)current_timestamp % (60 * 60 * 24)) / (60 * 60),
  213. ((uint32_t)current_timestamp % (60 * 60)) / 60,
  214. (uint32_t)current_timestamp % 60);
  215. canvas_draw_str(canvas, 0, 20, buffer);
  216. // Tomato counter
  217. if(model->counter > 5) {
  218. model->counter = 1;
  219. }
  220. for(int i = 0; i < model->counter; ++i) {
  221. canvas_draw_disc(canvas, 122 - i * 10, 15, 4);
  222. }
  223. }
  224. void pomodoro_timer_update(PomodoroTimer* pomodoro_timer) {
  225. with_view_model(
  226. pomodoro_timer->view, PomodoroTimerModel * model, { UNUSED(model); }, true);
  227. }