mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-20 04:54:45 -07:00
new app
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
- Known Issues: `Chess`
|
||||
- Last Synced/Checked [Unleashed/xMasterX](https://github.com/DarkFlippers/unleashed-firmware), changes in [changelog](https://github.com/RogueMaster/flipperzero-firmware-wPlugins/blob/420/CHANGELOG.md): `2022-10-14 20:25 GMT`
|
||||
- Last Synced/Checked [OFW](https://github.com/flipperdevices/flipperzero-firmware), changes in [commits](https://github.com/flipperdevices/flipperzero-firmware/commits/dev): `2022-10-14 20:25 GMT`
|
||||
- Added: [Pomodoro Timer (By sbrin)](https://github.com/sbrin/flipperzero_pomodoro)
|
||||
|
||||
<details>
|
||||
<summary><B>TO DO / REMOVED</b></summary><br/>
|
||||
|
||||
121
applications/plugins/pomodoro/LICENSE
Normal file
121
applications/plugins/pomodoro/LICENSE
Normal file
@@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
34
applications/plugins/pomodoro/README.md
Normal file
34
applications/plugins/pomodoro/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# flipperzero_pomodoro
|
||||
|
||||
The Pomodoro Technique is a time management method developed by Francesco Cirillo in the late 1980s.[1] It uses a kitchen timer to break work into intervals, typically 25 minutes in length, separated by short breaks. Each interval is known as a pomodoro, from the Italian word for tomato, after the tomato-shaped kitchen timer Cirillo used as a university student.
|
||||
|
||||
Flipper Zero is a portable Tamagotchi-like multi-functional device developed for interaction with access control systems. The device is able to read, copy, and emulate radio-frequency (RFID) tags, radio remotes, and digital access keys.
|
||||
|
||||
## Pomodoro timer application for Flipper Zero
|
||||
|
||||
Three timers available:
|
||||
|
||||
- classic 25 min work, 5 min rest
|
||||
- long 50 min work, 10 min rest
|
||||
- sprint 10 min work, 2 min rest
|
||||
|
||||
With tomato counter
|
||||
|
||||
Plays sound alerts
|
||||
|
||||
Has built-in clocks
|
||||
|
||||
Screenshots:
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
Compatible with firmware v. F81999EA from 14 Oct. 2022
|
||||
14
applications/plugins/pomodoro/application.fam
Normal file
14
applications/plugins/pomodoro/application.fam
Normal file
@@ -0,0 +1,14 @@
|
||||
App(
|
||||
appid="Pomodoro_Timer",
|
||||
name="Pomodoro Timer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="pomodoro_app",
|
||||
stack_size=1 * 1024,
|
||||
cdefines=["APP_POMODORO"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
order=10,
|
||||
fap_icon="pomodoro_timer.png",
|
||||
fap_category="Tools",
|
||||
)
|
||||
BIN
applications/plugins/pomodoro/misc/1.png
Normal file
BIN
applications/plugins/pomodoro/misc/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
applications/plugins/pomodoro/misc/2.png
Normal file
BIN
applications/plugins/pomodoro/misc/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
applications/plugins/pomodoro/misc/3.png
Normal file
BIN
applications/plugins/pomodoro/misc/3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
applications/plugins/pomodoro/misc/4.png
Normal file
BIN
applications/plugins/pomodoro/misc/4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
applications/plugins/pomodoro/misc/5.png
Normal file
BIN
applications/plugins/pomodoro/misc/5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
164
applications/plugins/pomodoro/pomodoro.c
Normal file
164
applications/plugins/pomodoro/pomodoro.c
Normal file
@@ -0,0 +1,164 @@
|
||||
#include "pomodoro.h"
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#define TAG "PomodoroApp"
|
||||
|
||||
enum PomodoroDebugSubmenuIndex {
|
||||
PomodoroSubmenuIndex10,
|
||||
PomodoroSubmenuIndex25,
|
||||
PomodoroSubmenuIndex50,
|
||||
};
|
||||
|
||||
void pomodoro_submenu_callback(void* context, uint32_t index) {
|
||||
furi_assert(context);
|
||||
Pomodoro* app = context;
|
||||
if(index == PomodoroSubmenuIndex10) {
|
||||
app->view_id = PomodoroView10;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView10);
|
||||
}
|
||||
if(index == PomodoroSubmenuIndex25) {
|
||||
app->view_id = PomodoroView25;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView25);
|
||||
}
|
||||
if(index == PomodoroSubmenuIndex50) {
|
||||
app->view_id = PomodoroView50;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroView50);
|
||||
}
|
||||
}
|
||||
|
||||
void pomodoro_dialog_callback(DialogExResult result, void* context) {
|
||||
furi_assert(context);
|
||||
Pomodoro* app = context;
|
||||
if(result == DialogExResultLeft) {
|
||||
view_dispatcher_stop(app->view_dispatcher);
|
||||
} else if(result == DialogExResultRight) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view
|
||||
} else if(result == DialogExResultCenter) {
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, PomodoroViewSubmenu);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t pomodoro_exit_confirm_view(void* context) {
|
||||
UNUSED(context);
|
||||
return PomodoroViewExitConfirm;
|
||||
}
|
||||
|
||||
uint32_t pomodoro_exit(void* context) {
|
||||
UNUSED(context);
|
||||
return VIEW_NONE;
|
||||
}
|
||||
|
||||
Pomodoro* pomodoro_app_alloc() {
|
||||
Pomodoro* app = malloc(sizeof(Pomodoro));
|
||||
|
||||
// Gui
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
// Notifications
|
||||
app->notifications = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
// View dispatcher
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// Submenu view
|
||||
app->submenu = submenu_alloc();
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Classic: 25 work 5 rest",
|
||||
PomodoroSubmenuIndex25,
|
||||
pomodoro_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Long: 50 work 10 rest",
|
||||
PomodoroSubmenuIndex50,
|
||||
pomodoro_submenu_callback,
|
||||
app);
|
||||
submenu_add_item(
|
||||
app->submenu,
|
||||
"Sprint: 10 work 2 rest",
|
||||
PomodoroSubmenuIndex10,
|
||||
pomodoro_submenu_callback,
|
||||
app);
|
||||
view_set_previous_callback(submenu_get_view(app->submenu), pomodoro_exit);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, PomodoroViewSubmenu, submenu_get_view(app->submenu));
|
||||
|
||||
// Dialog view
|
||||
app->dialog = dialog_ex_alloc();
|
||||
dialog_ex_set_result_callback(app->dialog, pomodoro_dialog_callback);
|
||||
dialog_ex_set_context(app->dialog, app);
|
||||
dialog_ex_set_left_button_text(app->dialog, "Exit");
|
||||
dialog_ex_set_right_button_text(app->dialog, "Stay");
|
||||
dialog_ex_set_center_button_text(app->dialog, "Menu");
|
||||
dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, PomodoroViewExitConfirm, dialog_ex_get_view(app->dialog));
|
||||
|
||||
// 25 minutes view
|
||||
app->pomodoro_25 = pomodoro_25_alloc();
|
||||
view_set_previous_callback(pomodoro_25_get_view(app->pomodoro_25), pomodoro_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, PomodoroView25, pomodoro_25_get_view(app->pomodoro_25));
|
||||
|
||||
// 50 minutes view
|
||||
app->pomodoro_50 = pomodoro_50_alloc();
|
||||
view_set_previous_callback(pomodoro_50_get_view(app->pomodoro_50), pomodoro_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, PomodoroView50, pomodoro_50_get_view(app->pomodoro_50));
|
||||
|
||||
// 10 minutes view
|
||||
app->pomodoro_10 = pomodoro_10_alloc();
|
||||
view_set_previous_callback(pomodoro_10_get_view(app->pomodoro_10), pomodoro_exit_confirm_view);
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, PomodoroView10, pomodoro_10_get_view(app->pomodoro_10));
|
||||
|
||||
// TODO switch to menu after Media is done
|
||||
app->view_id = PomodoroViewSubmenu;
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void pomodoro_app_free(Pomodoro* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Reset notification
|
||||
notification_internal_message(app->notifications, &sequence_reset_blue);
|
||||
|
||||
// Free views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewSubmenu);
|
||||
submenu_free(app->submenu);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, PomodoroViewExitConfirm);
|
||||
dialog_ex_free(app->dialog);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, PomodoroView25);
|
||||
pomodoro_25_free(app->pomodoro_25);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, PomodoroView50);
|
||||
pomodoro_50_free(app->pomodoro_50);
|
||||
view_dispatcher_remove_view(app->view_dispatcher, PomodoroView10);
|
||||
pomodoro_10_free(app->pomodoro_10);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
app->gui = NULL;
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
app->notifications = NULL;
|
||||
|
||||
// Free rest
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t pomodoro_app(void* p) {
|
||||
UNUSED(p);
|
||||
// Switch profile to Hid
|
||||
Pomodoro* app = pomodoro_app_alloc();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
pomodoro_app_free(app);
|
||||
|
||||
return 0;
|
||||
}
|
||||
34
applications/plugins/pomodoro/pomodoro.h
Normal file
34
applications/plugins/pomodoro/pomodoro.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <notification/notification.h>
|
||||
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/dialog_ex.h>
|
||||
#include "pomodoro_timer.h"
|
||||
#include "views/pomodoro_10.h"
|
||||
#include "views/pomodoro_25.h"
|
||||
#include "views/pomodoro_50.h"
|
||||
|
||||
typedef struct {
|
||||
Gui* gui;
|
||||
NotificationApp* notifications;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Submenu* submenu;
|
||||
DialogEx* dialog;
|
||||
PomodoroTimer* pomodoro_10;
|
||||
PomodoroTimer* pomodoro_25;
|
||||
PomodoroTimer* pomodoro_50;
|
||||
uint32_t view_id;
|
||||
} Pomodoro;
|
||||
|
||||
typedef enum {
|
||||
PomodoroViewSubmenu,
|
||||
PomodoroView10,
|
||||
PomodoroView25,
|
||||
PomodoroView50,
|
||||
PomodoroViewExitConfirm,
|
||||
} PomodoroView;
|
||||
241
applications/plugins/pomodoro/pomodoro_timer.c
Normal file
241
applications/plugins/pomodoro/pomodoro_timer.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "pomodoro_timer.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
const NotificationSequence sequence_finish = {
|
||||
&message_display_backlight_on,
|
||||
&message_green_255,
|
||||
&message_vibro_on,
|
||||
&message_note_c5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_e5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_g5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_b5,
|
||||
&message_delay_250,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_250,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence sequence_rest = {
|
||||
&message_display_backlight_on,
|
||||
&message_red_255,
|
||||
&message_vibro_on,
|
||||
&message_note_c6,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_b5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_g5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_e5,
|
||||
&message_delay_100,
|
||||
&message_vibro_off,
|
||||
&message_vibro_on,
|
||||
&message_note_c5,
|
||||
&message_delay_250,
|
||||
&message_vibro_off,
|
||||
&message_sound_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event) {
|
||||
with_view_model(
|
||||
pomodoro_timer->view,
|
||||
PomodoroTimerModel * model,
|
||||
{
|
||||
if(event->type == InputTypePress) {
|
||||
if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = true;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
model->reset_pressed = true;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = true;
|
||||
}
|
||||
} else if(event->type == InputTypeRelease) {
|
||||
if(event->key == InputKeyOk) {
|
||||
model->ok_pressed = false;
|
||||
|
||||
// START/STOP TIMER
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
|
||||
// STARTED -> PAUSED
|
||||
if(model->timer_running) {
|
||||
// Update stopped seconds
|
||||
model->timer_stopped_seconds =
|
||||
current_timestamp - model->timer_start_timestamp;
|
||||
} else if(!model->time_passed) {
|
||||
// INITIAL -> STARTED
|
||||
model->timer_start_timestamp = current_timestamp;
|
||||
model->rest_running = false;
|
||||
} else {
|
||||
// PAUSED -> STARTED
|
||||
model->timer_start_timestamp =
|
||||
current_timestamp - model->timer_stopped_seconds;
|
||||
}
|
||||
model->timer_running = !model->timer_running;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
if(!model->timer_running) {
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
model->timer_stopped_seconds = 0;
|
||||
model->timer_start_timestamp = 0;
|
||||
model->time_passed = 0;
|
||||
model->timer_running = false;
|
||||
}
|
||||
model->reset_pressed = false;
|
||||
} else if(event->key == InputKeyBack) {
|
||||
model->back_pressed = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest) {
|
||||
furi_assert(context);
|
||||
PomodoroTimerModel* model = context;
|
||||
FuriHalRtcDateTime curr_dt;
|
||||
furi_hal_rtc_get_datetime(&curr_dt);
|
||||
uint32_t current_timestamp = furi_hal_rtc_datetime_to_timestamp(&curr_dt);
|
||||
|
||||
// Header
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Pomodoro");
|
||||
|
||||
canvas_draw_icon(canvas, 68, 1, &I_Pin_back_arrow_10x8);
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_multiline_text_aligned(canvas, 127, 1, AlignRight, AlignTop, "Hold to exit");
|
||||
|
||||
// Start/Pause/Continue
|
||||
int txt_main_y = 34;
|
||||
canvas_draw_icon(canvas, 63, 23, &I_Space_65x18); // button
|
||||
if(model->ok_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 25, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
if(model->timer_running) {
|
||||
model->time_passed = current_timestamp - model->timer_start_timestamp;
|
||||
elements_multiline_text_aligned(canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Pause");
|
||||
canvas_draw_box(canvas, 71, 27, 2, 8);
|
||||
canvas_draw_box(canvas, 75, 27, 2, 8);
|
||||
} else {
|
||||
if(model->time_passed) {
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Continue");
|
||||
} else {
|
||||
elements_multiline_text_aligned(
|
||||
canvas, 83, txt_main_y, AlignLeft, AlignBottom, "Start");
|
||||
}
|
||||
canvas_draw_icon(canvas, 70, 26, &I_Ok_btn_9x9); // OK icon
|
||||
}
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
// Reset
|
||||
if(!model->timer_running && model->time_passed) {
|
||||
canvas_draw_icon(canvas, 63, 46, &I_Space_65x18);
|
||||
if(model->reset_pressed) {
|
||||
elements_slightly_rounded_box(canvas, 66, 48, 60, 13);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
}
|
||||
canvas_draw_icon(canvas, 72, 50, &I_ButtonLeft_4x7);
|
||||
elements_multiline_text_aligned(canvas, 83, 57, AlignLeft, AlignBottom, "Reset");
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
|
||||
char buffer[64];
|
||||
|
||||
// Time to work
|
||||
int total_time_left = (max_seconds - (uint32_t)model->time_passed);
|
||||
int minutes_left = total_time_left / 60;
|
||||
int seconds_left = total_time_left % 60;
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
|
||||
// Play sound
|
||||
if(total_time_left == 0 && !model->sound_playing) {
|
||||
model->sound_playing = true;
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_finish);
|
||||
}
|
||||
if(total_time_left < 0) {
|
||||
model->timer_running = false;
|
||||
model->time_passed = 0;
|
||||
model->sound_playing = false;
|
||||
|
||||
model->rest_running = true;
|
||||
model->rest_start_timestamp = current_timestamp;
|
||||
seconds_left = 0;
|
||||
model->counter += 1;
|
||||
}
|
||||
if(!model->rest_running) {
|
||||
snprintf(buffer, sizeof(buffer), "%02d:%02d", minutes_left, seconds_left);
|
||||
canvas_draw_str(canvas, 0, 39, buffer);
|
||||
}
|
||||
if(model->timer_running) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 0, 50, AlignLeft, AlignTop, "Time to work");
|
||||
}
|
||||
|
||||
// Time to rest
|
||||
if(model->rest_running && !model->timer_running) {
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
int rest_passed = current_timestamp - model->rest_start_timestamp;
|
||||
int rest_total_time_left = (max_seconds_rest - rest_passed);
|
||||
int rest_minutes_left = rest_total_time_left / 60;
|
||||
int rest_seconds_left = rest_total_time_left % 60;
|
||||
|
||||
// Play sound
|
||||
if(rest_total_time_left == 0 && !model->sound_playing) {
|
||||
model->sound_playing = true;
|
||||
notification_message(furi_record_open(RECORD_NOTIFICATION), &sequence_rest);
|
||||
}
|
||||
if(rest_total_time_left < 0) {
|
||||
rest_seconds_left = 0;
|
||||
model->rest_running = false;
|
||||
model->sound_playing = false;
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "%02d:%02d", rest_minutes_left, rest_seconds_left);
|
||||
canvas_draw_str(canvas, 0, 60, buffer);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
elements_multiline_text_aligned(canvas, 0, 27, AlignLeft, AlignTop, "Have a rest");
|
||||
}
|
||||
|
||||
// Clocks
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%02ld:%02ld:%02ld",
|
||||
((uint32_t)current_timestamp % (60 * 60 * 24)) / (60 * 60),
|
||||
((uint32_t)current_timestamp % (60 * 60)) / 60,
|
||||
(uint32_t)current_timestamp % 60);
|
||||
canvas_draw_str(canvas, 0, 20, buffer);
|
||||
|
||||
// Tomato counter
|
||||
if(model->counter > 5) {
|
||||
model->counter = 1;
|
||||
}
|
||||
for(int i = 0; i < model->counter; ++i) {
|
||||
canvas_draw_disc(canvas, 122 - i * 10, 15, 4);
|
||||
}
|
||||
}
|
||||
33
applications/plugins/pomodoro/pomodoro_timer.h
Normal file
33
applications/plugins/pomodoro/pomodoro_timer.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
|
||||
typedef struct PomodoroTimer PomodoroTimer;
|
||||
|
||||
struct PomodoroTimer {
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct PomodoroTimerModel PomodoroTimerModel;
|
||||
|
||||
struct PomodoroTimerModel {
|
||||
bool ok_pressed;
|
||||
bool reset_pressed;
|
||||
bool back_pressed;
|
||||
bool connected;
|
||||
bool timer_running;
|
||||
bool rest_running;
|
||||
bool sound_playing;
|
||||
uint32_t timer_start_timestamp;
|
||||
uint32_t timer_stopped_seconds;
|
||||
uint32_t time_passed;
|
||||
uint32_t rest_start_timestamp;
|
||||
int counter;
|
||||
};
|
||||
|
||||
void pomodoro_timer_process(PomodoroTimer* pomodoro_timer, InputEvent* event);
|
||||
|
||||
void pomodoro_draw_callback(Canvas* canvas, void* context, int max_seconds, int max_seconds_rest);
|
||||
BIN
applications/plugins/pomodoro/pomodoro_timer.png
Normal file
BIN
applications/plugins/pomodoro/pomodoro_timer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 306 B |
46
applications/plugins/pomodoro/views/pomodoro_10.c
Normal file
46
applications/plugins/pomodoro/views/pomodoro_10.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "../pomodoro_timer.h"
|
||||
#include "pomodoro_10.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
static void pomodoro_10_draw_callback(Canvas* canvas, void* context) {
|
||||
int max_seconds = 60 * 10;
|
||||
int max_seconds_rest = 60 * 2;
|
||||
pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest);
|
||||
}
|
||||
|
||||
static bool pomodoro_10_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
PomodoroTimer* pomodoro_10 = context;
|
||||
|
||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||
return false;
|
||||
} else {
|
||||
pomodoro_timer_process(pomodoro_10, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PomodoroTimer* pomodoro_10_alloc() {
|
||||
PomodoroTimer* pomodoro_10 = malloc(sizeof(PomodoroTimer));
|
||||
pomodoro_10->view = view_alloc();
|
||||
view_set_context(pomodoro_10->view, pomodoro_10);
|
||||
view_allocate_model(pomodoro_10->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel));
|
||||
view_set_draw_callback(pomodoro_10->view, pomodoro_10_draw_callback);
|
||||
view_set_input_callback(pomodoro_10->view, pomodoro_10_input_callback);
|
||||
|
||||
return pomodoro_10;
|
||||
}
|
||||
|
||||
void pomodoro_10_free(PomodoroTimer* pomodoro_10) {
|
||||
furi_assert(pomodoro_10);
|
||||
view_free(pomodoro_10->view);
|
||||
free(pomodoro_10);
|
||||
}
|
||||
|
||||
View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10) {
|
||||
furi_assert(pomodoro_10);
|
||||
return pomodoro_10->view;
|
||||
}
|
||||
10
applications/plugins/pomodoro/views/pomodoro_10.h
Normal file
10
applications/plugins/pomodoro/views/pomodoro_10.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../pomodoro_timer.h"
|
||||
|
||||
PomodoroTimer* pomodoro_10_alloc();
|
||||
|
||||
void pomodoro_10_free(PomodoroTimer* pomodoro_10);
|
||||
|
||||
View* pomodoro_10_get_view(PomodoroTimer* pomodoro_10);
|
||||
46
applications/plugins/pomodoro/views/pomodoro_25.c
Normal file
46
applications/plugins/pomodoro/views/pomodoro_25.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "../pomodoro_timer.h"
|
||||
#include "pomodoro_25.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
static void pomodoro_25_draw_callback(Canvas* canvas, void* context) {
|
||||
int max_seconds = 60 * 25;
|
||||
int max_seconds_rest = 60 * 5;
|
||||
pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest);
|
||||
}
|
||||
|
||||
static bool pomodoro_25_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
PomodoroTimer* pomodoro_25 = context;
|
||||
|
||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||
return false;
|
||||
} else {
|
||||
pomodoro_timer_process(pomodoro_25, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PomodoroTimer* pomodoro_25_alloc() {
|
||||
PomodoroTimer* pomodoro_25 = malloc(sizeof(PomodoroTimer));
|
||||
pomodoro_25->view = view_alloc();
|
||||
view_set_context(pomodoro_25->view, pomodoro_25);
|
||||
view_allocate_model(pomodoro_25->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel));
|
||||
view_set_draw_callback(pomodoro_25->view, pomodoro_25_draw_callback);
|
||||
view_set_input_callback(pomodoro_25->view, pomodoro_25_input_callback);
|
||||
|
||||
return pomodoro_25;
|
||||
}
|
||||
|
||||
void pomodoro_25_free(PomodoroTimer* pomodoro_25) {
|
||||
furi_assert(pomodoro_25);
|
||||
view_free(pomodoro_25->view);
|
||||
free(pomodoro_25);
|
||||
}
|
||||
|
||||
View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25) {
|
||||
furi_assert(pomodoro_25);
|
||||
return pomodoro_25->view;
|
||||
}
|
||||
10
applications/plugins/pomodoro/views/pomodoro_25.h
Normal file
10
applications/plugins/pomodoro/views/pomodoro_25.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../pomodoro_timer.h"
|
||||
|
||||
PomodoroTimer* pomodoro_25_alloc();
|
||||
|
||||
void pomodoro_25_free(PomodoroTimer* pomodoro_25);
|
||||
|
||||
View* pomodoro_25_get_view(PomodoroTimer* pomodoro_25);
|
||||
46
applications/plugins/pomodoro/views/pomodoro_50.c
Normal file
46
applications/plugins/pomodoro/views/pomodoro_50.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "../pomodoro_timer.h"
|
||||
#include "pomodoro_50.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
static void pomodoro_50_draw_callback(Canvas* canvas, void* context) {
|
||||
int max_seconds = 60 * 50;
|
||||
int max_seconds_rest = 60 * 10;
|
||||
pomodoro_draw_callback(canvas, context, max_seconds, max_seconds_rest);
|
||||
}
|
||||
|
||||
static bool pomodoro_50_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
PomodoroTimer* pomodoro_50 = context;
|
||||
|
||||
if(event->type == InputTypeLong && event->key == InputKeyBack) {
|
||||
return false;
|
||||
} else {
|
||||
pomodoro_timer_process(pomodoro_50, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
PomodoroTimer* pomodoro_50_alloc() {
|
||||
PomodoroTimer* pomodoro_50 = malloc(sizeof(PomodoroTimer));
|
||||
pomodoro_50->view = view_alloc();
|
||||
view_set_context(pomodoro_50->view, pomodoro_50);
|
||||
view_allocate_model(pomodoro_50->view, ViewModelTypeLocking, sizeof(PomodoroTimerModel));
|
||||
view_set_draw_callback(pomodoro_50->view, pomodoro_50_draw_callback);
|
||||
view_set_input_callback(pomodoro_50->view, pomodoro_50_input_callback);
|
||||
|
||||
return pomodoro_50;
|
||||
}
|
||||
|
||||
void pomodoro_50_free(PomodoroTimer* pomodoro_50) {
|
||||
furi_assert(pomodoro_50);
|
||||
view_free(pomodoro_50->view);
|
||||
free(pomodoro_50);
|
||||
}
|
||||
|
||||
View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50) {
|
||||
furi_assert(pomodoro_50);
|
||||
return pomodoro_50->view;
|
||||
}
|
||||
10
applications/plugins/pomodoro/views/pomodoro_50.h
Normal file
10
applications/plugins/pomodoro/views/pomodoro_50.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../pomodoro_timer.h"
|
||||
|
||||
PomodoroTimer* pomodoro_50_alloc();
|
||||
|
||||
void pomodoro_50_free(PomodoroTimer* pomodoro_50);
|
||||
|
||||
View* pomodoro_50_get_view(PomodoroTimer* pomodoro_50);
|
||||
Reference in New Issue
Block a user