Update apps pt3
@@ -19,6 +19,7 @@ typedef struct {
|
||||
} selectedPosition;
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
selectedPosition position;
|
||||
//string with the inputted calculator text
|
||||
char text[20];
|
||||
@@ -201,8 +202,10 @@ void generate_calculator_layout(Canvas* canvas) {
|
||||
};
|
||||
|
||||
void calculator_draw_callback(Canvas* canvas, void* ctx) {
|
||||
const Calculator* calculator_state = acquire_mutex((ValueMutex*)ctx, 25);
|
||||
UNUSED(ctx);
|
||||
furi_assert(ctx);
|
||||
const Calculator* calculator_state = ctx;
|
||||
furi_mutex_acquire(calculator_state->mutex, FuriWaitForever);
|
||||
|
||||
canvas_clear(canvas);
|
||||
|
||||
//show selected button
|
||||
@@ -240,7 +243,7 @@ void calculator_draw_callback(Canvas* canvas, void* ctx) {
|
||||
//draw cursor
|
||||
canvas_draw_box(canvas, stringWidth + 5, 29, 5, 1);
|
||||
|
||||
release_mutex((ValueMutex*)ctx, calculator_state);
|
||||
furi_mutex_release(calculator_state->mutex);
|
||||
}
|
||||
|
||||
void calculator_input_callback(InputEvent* input_event, void* ctx) {
|
||||
@@ -315,8 +318,8 @@ int32_t calculator_app(void* p) {
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
Calculator* calculator_state = malloc(sizeof(Calculator));
|
||||
ValueMutex calculator_state_mutex;
|
||||
if(!init_mutex(&calculator_state_mutex, calculator_state, sizeof(Calculator))) {
|
||||
calculator_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
if(!calculator_state->mutex) {
|
||||
//FURI_LOG_E("calculator", "cannot create mutex\r\n");
|
||||
free(calculator_state);
|
||||
return -1;
|
||||
@@ -324,7 +327,7 @@ int32_t calculator_app(void* p) {
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, calculator_draw_callback, &calculator_state_mutex);
|
||||
view_port_draw_callback_set(view_port, calculator_draw_callback, calculator_state);
|
||||
view_port_input_callback_set(view_port, calculator_input_callback, event_queue);
|
||||
view_port_set_orientation(view_port, ViewPortOrientationVertical);
|
||||
|
||||
@@ -444,10 +447,12 @@ int32_t calculator_app(void* p) {
|
||||
}
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_mutex_free(calculator_state->mutex);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
free(calculator_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
App(
|
||||
appid="Dice",
|
||||
appid="multi_dice",
|
||||
name="Multi-Dice",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="dice_app",
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <gui/elements.h>
|
||||
#include <gui/gui.h>
|
||||
#include <input/input.h>
|
||||
#include <dolphin/dolphin.h>
|
||||
|
||||
#define TAG "Dice Roller"
|
||||
|
||||
@@ -290,10 +289,6 @@ static void dice_render_callback(Canvas* const canvas, void* ctx) {
|
||||
state->diceQty,
|
||||
state->diceType[0],
|
||||
state->rollTime[0]);
|
||||
// if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect)
|
||||
// DOLPHIN_DEED(getRandomDeed());
|
||||
// if(state->diceSelect >= 20 && state->diceRoll == state->diceSelect - 1)
|
||||
// DOLPHIN_DEED(getRandomDeed());
|
||||
if(state->diceQty == 1) {
|
||||
snprintf(state->strings[1], sizeof(state->strings[1]), "%d", state->diceRoll);
|
||||
} else if(state->diceQty == 2) {
|
||||
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
@@ -1,12 +1,12 @@
|
||||
App(
|
||||
appid="Nightstand",
|
||||
appid="nightstand",
|
||||
name="Nightstand Clock",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="clock_app",
|
||||
requires=["gui"],
|
||||
icon="A_Clock_14",
|
||||
stack_size=2 * 1024,
|
||||
fap_icon="ClockIcon.png",
|
||||
fap_icon="clock.png",
|
||||
fap_category="Misc",
|
||||
order=81,
|
||||
)
|
||||
|
||||
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
@@ -11,13 +11,16 @@
|
||||
|
||||
/*
|
||||
This is a modified version of the default clock app intended for use overnight
|
||||
Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off.
|
||||
Up / Down controls the displays brightness. Down at brightness 0 turns the notification LED on and off.
|
||||
*/
|
||||
|
||||
int brightness = 5;
|
||||
bool led = false;
|
||||
NotificationApp* notif = 0;
|
||||
|
||||
int dspBrightnessBarFrames = 0;
|
||||
const int dspBrightnessBarDisplayFrames = 8;
|
||||
|
||||
const NotificationMessage message_red_dim = {
|
||||
.type = NotificationMessageTypeLedRed,
|
||||
.data.led.value = 0xFF / 16,
|
||||
@@ -51,6 +54,7 @@ void set_backlight_brightness(float brightness) {
|
||||
}
|
||||
|
||||
void handle_up() {
|
||||
dspBrightnessBarFrames = dspBrightnessBarDisplayFrames;
|
||||
if(brightness < 100) {
|
||||
led = false;
|
||||
notification_message(notif, &led_off);
|
||||
@@ -60,6 +64,7 @@ void handle_up() {
|
||||
}
|
||||
|
||||
void handle_down() {
|
||||
dspBrightnessBarFrames = dspBrightnessBarDisplayFrames;
|
||||
if(brightness > 0) {
|
||||
brightness -= 5;
|
||||
if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition
|
||||
@@ -83,6 +88,29 @@ static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* even
|
||||
furi_message_queue_put(event_queue, &event, FuriWaitForever);
|
||||
}
|
||||
|
||||
//do you are have stupid?
|
||||
void elements_progress_bar_vertical(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
uint8_t height,
|
||||
float progress) {
|
||||
furi_assert(canvas);
|
||||
furi_assert((progress >= 0) && (progress <= 1.0));
|
||||
uint8_t width = 9;
|
||||
|
||||
uint8_t progress_length = roundf((1.f - progress) * (height - 2));
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_box(canvas, x + 1, y + 1, width - 2, height - 2);
|
||||
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
canvas_draw_box(canvas, x + 1, y + 1, width - 2, progress_length);
|
||||
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_rframe(canvas, x, y, width, height, 3);
|
||||
}
|
||||
|
||||
static void clock_render_callback(Canvas* const canvas, void* ctx) {
|
||||
//canvas_clear(canvas);
|
||||
//canvas_set_color(canvas, ColorBlack);
|
||||
@@ -90,6 +118,11 @@ static void clock_render_callback(Canvas* const canvas, void* ctx) {
|
||||
//avoids a bug with the brightness being reverted after the backlight-off period
|
||||
set_backlight_brightness((float)(brightness / 100.f));
|
||||
|
||||
if(dspBrightnessBarFrames > 0) {
|
||||
elements_progress_bar_vertical(canvas, 119, 1, 62, (float)(brightness / 100.f));
|
||||
dspBrightnessBarFrames--;
|
||||
}
|
||||
|
||||
ClockState* state = ctx;
|
||||
if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) {
|
||||
//FURI_LOG_D(TAG, "Can't obtain mutex, requeue render");
|
||||
@@ -270,7 +303,7 @@ int32_t clock_app(void* p) {
|
||||
|
||||
notif = furi_record_open(RECORD_NOTIFICATION);
|
||||
float tmpBrightness = notif->settings.display_brightness;
|
||||
brightness = tmpBrightness * 100;
|
||||
brightness = tmpBrightness * 100; // Keep current brightness by default
|
||||
|
||||
notification_message(notif, &sequence_display_backlight_enforce_on);
|
||||
notification_message(notif, &led_off);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
typedef struct {
|
||||
FuriMutex* mutex;
|
||||
int mode;
|
||||
} PluginState;
|
||||
|
||||
@@ -39,8 +40,8 @@ int32_t orgasmotron_app(void* p) {
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
PluginState* plugin_state = malloc(sizeof(PluginState));
|
||||
ValueMutex state_mutex;
|
||||
if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) {
|
||||
plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
|
||||
if(!plugin_state->mutex) {
|
||||
FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n");
|
||||
free(plugin_state);
|
||||
return 255;
|
||||
@@ -63,7 +64,7 @@ int32_t orgasmotron_app(void* p) {
|
||||
//while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) {
|
||||
while(processing) {
|
||||
FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
|
||||
PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex);
|
||||
furi_mutex_acquire(plugin_state->mutex, FuriWaitForever);
|
||||
if(event_status == FuriStatusOk) {
|
||||
if(event.key == InputKeyBack && event.type == InputTypeShort) {
|
||||
//Exit Application
|
||||
@@ -132,15 +133,15 @@ int32_t orgasmotron_app(void* p) {
|
||||
delay(50);
|
||||
}
|
||||
}
|
||||
release_mutex(&state_mutex, plugin_state);
|
||||
furi_mutex_release(plugin_state->mutex);
|
||||
}
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_mutex_free(plugin_state->mutex);
|
||||
furi_message_queue_free(event_queue);
|
||||
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
furi_record_close(RECORD_GUI);
|
||||
delete_mutex(&state_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
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.
|
||||
@@ -1,15 +1,11 @@
|
||||
App(
|
||||
appid="Pomodoro_Timer",
|
||||
appid="flipp_pomodoro",
|
||||
name="Pomodoro Timer",
|
||||
apptype=FlipperAppType.EXTERNAL,
|
||||
entry_point="pomodoro_app",
|
||||
entry_point="flipp_pomodoro_app",
|
||||
requires=["gui", "notification", "dolphin"],
|
||||
stack_size=1 * 1024,
|
||||
cdefines=["APP_POMODORO"],
|
||||
requires=[
|
||||
"gui",
|
||||
],
|
||||
order=10,
|
||||
fap_icon="pomodoro_timer.png",
|
||||
fap_category="Tools",
|
||||
fap_icon_assets="icons",
|
||||
fap_icon_assets="images",
|
||||
fap_icon="flipp_pomodoro_10.png",
|
||||
)
|
||||
|
||||
|
After Width: | Height: | Size: 157 B |
@@ -0,0 +1,95 @@
|
||||
#include "flipp_pomodoro_app_i.h"
|
||||
|
||||
enum {
|
||||
CustomEventConsumed = true,
|
||||
CustomEventNotConsumed = false,
|
||||
};
|
||||
|
||||
static bool flipp_pomodoro_app_back_event_callback(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FlippPomodoroApp* app = ctx;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
};
|
||||
|
||||
static void flipp_pomodoro_app_tick_event_callback(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick);
|
||||
};
|
||||
|
||||
static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) {
|
||||
furi_assert(ctx);
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
switch(event) {
|
||||
case FlippPomodoroAppCustomEventStageSkip:
|
||||
flipp_pomodoro__toggle_stage(app->state);
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
|
||||
return CustomEventConsumed;
|
||||
case FlippPomodoroAppCustomEventStageComplete:
|
||||
flipp_pomodoro__toggle_stage(app->state);
|
||||
notification_message(
|
||||
app->notification_app,
|
||||
stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]);
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated);
|
||||
return CustomEventConsumed;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
};
|
||||
|
||||
FlippPomodoroApp* flipp_pomodoro_app_alloc() {
|
||||
FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp));
|
||||
app->state = flipp_pomodoro__new();
|
||||
|
||||
app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app);
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
app->notification_app = furi_record_open(RECORD_NOTIFICATION);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
view_dispatcher_enable_queue(app->view_dispatcher);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, flipp_pomodoro_app_custom_event_callback);
|
||||
view_dispatcher_set_tick_event_callback(
|
||||
app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000);
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, flipp_pomodoro_app_back_event_callback);
|
||||
|
||||
app->timer_view = flipp_pomodoro_view_timer_alloc();
|
||||
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher,
|
||||
FlippPomodoroAppViewTimer,
|
||||
flipp_pomodoro_view_timer_get_view(app->timer_view));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer);
|
||||
|
||||
return app;
|
||||
};
|
||||
|
||||
void flipp_pomodoro_app_free(FlippPomodoroApp* app) {
|
||||
view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
flipp_pomodoro_view_timer_free(app->timer_view);
|
||||
free(app);
|
||||
furi_record_close(RECORD_GUI);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
};
|
||||
|
||||
int32_t flipp_pomodoro_app(void* p) {
|
||||
UNUSED(p);
|
||||
FlippPomodoroApp* app = flipp_pomodoro_app_alloc();
|
||||
|
||||
view_dispatcher_run(app->view_dispatcher);
|
||||
|
||||
flipp_pomodoro_app_free(app);
|
||||
|
||||
return 0;
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include "views/flipp_pomodoro_timer_view.h"
|
||||
|
||||
#include "modules/flipp_pomodoro.h"
|
||||
|
||||
typedef enum {
|
||||
// Reserve first 100 events for button types and indexes, starting from 0
|
||||
FlippPomodoroAppCustomEventStageSkip = 100,
|
||||
FlippPomodoroAppCustomEventStageComplete, // By Expiration
|
||||
FlippPomodoroAppCustomEventTimerTick,
|
||||
FlippPomodoroAppCustomEventStateUpdated,
|
||||
} FlippPomodoroAppCustomEvent;
|
||||
|
||||
typedef struct {
|
||||
SceneManager* scene_manager;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
Gui* gui;
|
||||
NotificationApp* notification_app;
|
||||
FlippPomodoroTimerView* timer_view;
|
||||
FlippPomodoroState* state;
|
||||
} FlippPomodoroApp;
|
||||
|
||||
typedef enum {
|
||||
FlippPomodoroAppViewTimer,
|
||||
} FlippPomodoroAppView;
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#define FURI_DEBUG 1
|
||||
|
||||
/**
|
||||
* Index of dependencies for the main app
|
||||
*/
|
||||
|
||||
// Platform Imports
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_stack.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/elements.h>
|
||||
#include <input/input.h>
|
||||
|
||||
// App resource imports
|
||||
|
||||
#include "helpers/time.h"
|
||||
#include "helpers/notifications.h"
|
||||
#include "modules/flipp_pomodoro.h"
|
||||
#include "flipp_pomodoro_app.h"
|
||||
#include "scenes/flipp_pomodoro_scene.h"
|
||||
#include "views/flipp_pomodoro_timer_view.h"
|
||||
|
||||
// Auto-compiled icons
|
||||
#include "flipp_pomodoro_icons.h"
|
||||
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define TAG "FlippPomodoro"
|
||||
@@ -0,0 +1,49 @@
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
const NotificationSequence work_start_notification = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_vibro_on,
|
||||
|
||||
&message_note_b5,
|
||||
&message_delay_250,
|
||||
|
||||
&message_note_d5,
|
||||
&message_delay_250,
|
||||
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_green_255,
|
||||
&message_delay_1000,
|
||||
&message_green_0,
|
||||
&message_delay_250,
|
||||
&message_green_255,
|
||||
&message_delay_1000,
|
||||
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence rest_start_notification = {
|
||||
&message_display_backlight_on,
|
||||
|
||||
&message_vibro_on,
|
||||
|
||||
&message_note_d5,
|
||||
&message_delay_250,
|
||||
|
||||
&message_note_b5,
|
||||
&message_delay_250,
|
||||
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
|
||||
&message_red_255,
|
||||
&message_delay_1000,
|
||||
&message_red_0,
|
||||
&message_delay_250,
|
||||
&message_red_255,
|
||||
&message_delay_1000,
|
||||
|
||||
NULL,
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "../modules/flipp_pomodoro.h"
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
extern const NotificationSequence work_start_notification;
|
||||
extern const NotificationSequence rest_start_notification;
|
||||
|
||||
/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage.
|
||||
const NotificationSequence* stage_start_notification_sequence_map[] = {
|
||||
[FlippPomodoroStageFocus] = &work_start_notification,
|
||||
[FlippPomodoroStageRest] = &rest_start_notification,
|
||||
[FlippPomodoroStageLongBreak] = &rest_start_notification,
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
#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};
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
#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);
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
1
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1,89 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../helpers/time.h"
|
||||
#include "flipp_pomodoro.h"
|
||||
|
||||
PomodoroStage stages_sequence[] = {
|
||||
FlippPomodoroStageFocus,
|
||||
FlippPomodoroStageRest,
|
||||
|
||||
FlippPomodoroStageFocus,
|
||||
FlippPomodoroStageRest,
|
||||
|
||||
FlippPomodoroStageFocus,
|
||||
FlippPomodoroStageRest,
|
||||
|
||||
FlippPomodoroStageFocus,
|
||||
FlippPomodoroStageLongBreak,
|
||||
};
|
||||
|
||||
char* current_stage_label[] = {
|
||||
[FlippPomodoroStageFocus] = "Continue focus for:",
|
||||
[FlippPomodoroStageRest] = "Keep rest for:",
|
||||
[FlippPomodoroStageLongBreak] = "Long Break for:",
|
||||
};
|
||||
|
||||
char* next_stage_label[] = {
|
||||
[FlippPomodoroStageFocus] = "Focus",
|
||||
[FlippPomodoroStageRest] = "Short Break",
|
||||
[FlippPomodoroStageLongBreak] = "Long Break",
|
||||
};
|
||||
|
||||
PomodoroStage flipp_pomodoro__stage_by_index(int index) {
|
||||
const int one_loop_size = sizeof(stages_sequence);
|
||||
return stages_sequence[index % one_loop_size];
|
||||
}
|
||||
|
||||
void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) {
|
||||
furi_assert(state);
|
||||
state->current_stage_index = state->current_stage_index + 1;
|
||||
state->started_at_timestamp = time_now();
|
||||
};
|
||||
|
||||
PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) {
|
||||
furi_assert(state);
|
||||
return flipp_pomodoro__stage_by_index(state->current_stage_index);
|
||||
};
|
||||
|
||||
char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) {
|
||||
furi_assert(state);
|
||||
return current_stage_label[flipp_pomodoro__get_stage(state)];
|
||||
};
|
||||
|
||||
char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) {
|
||||
furi_assert(state);
|
||||
return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)];
|
||||
};
|
||||
|
||||
uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) {
|
||||
const int32_t stage_duration_seconds_map[] = {
|
||||
[FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE,
|
||||
[FlippPomodoroStageRest] = 5 * TIME_SECONDS_IN_MINUTE,
|
||||
[FlippPomodoroStageLongBreak] = 30 * TIME_SECONDS_IN_MINUTE,
|
||||
};
|
||||
|
||||
return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)];
|
||||
};
|
||||
|
||||
uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) {
|
||||
return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state);
|
||||
};
|
||||
|
||||
TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) {
|
||||
const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state);
|
||||
return time_difference_seconds(time_now(), stage_ends_at);
|
||||
};
|
||||
|
||||
bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) {
|
||||
const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state);
|
||||
const uint8_t seamless_change_span_seconds = 1;
|
||||
return (time_now() - seamless_change_span_seconds) >= expired_by;
|
||||
};
|
||||
|
||||
FlippPomodoroState* flipp_pomodoro__new() {
|
||||
FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState));
|
||||
const uint32_t now = time_now();
|
||||
state->started_at_timestamp = now;
|
||||
state->current_stage_index = 0;
|
||||
return state;
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include "../helpers/time.h"
|
||||
|
||||
/// @brief Options of pomodoro stages
|
||||
typedef enum {
|
||||
FlippPomodoroStageFocus,
|
||||
FlippPomodoroStageRest,
|
||||
FlippPomodoroStageLongBreak,
|
||||
} PomodoroStage;
|
||||
|
||||
/// @brief State of the pomodoro timer
|
||||
typedef struct {
|
||||
PomodoroStage stage;
|
||||
uint8_t current_stage_index;
|
||||
uint32_t started_at_timestamp;
|
||||
} FlippPomodoroState;
|
||||
|
||||
/// @brief Generates initial state
|
||||
/// @returns A new pre-populated state for pomodoro timer
|
||||
FlippPomodoroState* flipp_pomodoro__new();
|
||||
|
||||
/// @brief Extract current stage of pomodoro
|
||||
/// @param state - pointer to the state of pomorodo
|
||||
/// @returns Current stage value
|
||||
PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Destroys state of timer and it's dependencies
|
||||
void flipp_pomodoro__destroy(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Get remaining stage time.
|
||||
/// @param state - pointer to the state of pomorodo
|
||||
/// @returns Time difference to the end of current stage
|
||||
TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Label of currently active stage
|
||||
/// @param state - pointer to the state of pomorodo
|
||||
/// @returns A string that explains current stage
|
||||
char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Label of transition to the next stage
|
||||
/// @param state - pointer to the state of pomorodo.
|
||||
/// @returns string with the label of the "skipp" button
|
||||
char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Check if current stage is expired
|
||||
/// @param state - pointer to the state of pomorodo.
|
||||
/// @returns expriations status - true means stage is expired
|
||||
bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state);
|
||||
|
||||
/// @brief Rotate stage of the timer
|
||||
/// @param state - pointer to the state of pomorodo.
|
||||
void flipp_pomodoro__toggle_stage(FlippPomodoroState* state);
|
||||
@@ -1,164 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
#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;
|
||||
@@ -1,242 +0,0 @@
|
||||
#include "pomodoro_timer.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <gui/elements.h>
|
||||
#include <notification/notification_messages.h>
|
||||
#include <Pomodoro_Timer_icons.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);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#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);
|
||||
|
Before Width: | Height: | Size: 306 B |
@@ -0,0 +1 @@
|
||||
ADD_SCENE(flipp_pomodoro, timer, Timer)
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "flipp_pomodoro_scene.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = {
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
|
||||
bool (*const flipp_pomodoro_scene_on_event_handlers[])(void* ctx, SceneManagerEvent event) = {
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
|
||||
void (*const flipp_pomodoro_scene_on_exit_handlers[])(void* ctx) = {
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers flipp_pomodoro_scene_handlers = {
|
||||
.on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers,
|
||||
.on_event_handlers = flipp_pomodoro_scene_on_event_handlers,
|
||||
.on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers,
|
||||
.scene_num = FlippPomodoroSceneNum,
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id,
|
||||
typedef enum {
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
FlippPomodoroSceneNum,
|
||||
} FlippPomodoroScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers flipp_pomodoro_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_event handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) \
|
||||
bool prefix##_scene_##name##_on_event(void* ctx, SceneManagerEvent event);
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Generate scene on_exit handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* ctx);
|
||||
#include "config/flipp_pomodoro_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <furi.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include "../flipp_pomodoro_app.h"
|
||||
#include "../views/flipp_pomodoro_timer_view.h"
|
||||
|
||||
enum { SceneEventConusmed = true, SceneEventNotConusmed = false };
|
||||
|
||||
uint8_t ExitSignal = 0;
|
||||
|
||||
void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
flipp_pomodoro_view_timer_set_state(
|
||||
flipp_pomodoro_view_timer_get_view(app->timer_view), app->state);
|
||||
};
|
||||
|
||||
void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip);
|
||||
};
|
||||
|
||||
void flipp_pomodoro_scene_timer_on_enter(void* ctx) {
|
||||
furi_assert(ctx);
|
||||
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer);
|
||||
flipp_pomodoro_scene_timer_sync_view_state(app);
|
||||
flipp_pomodoro_view_timer_set_on_right_cb(
|
||||
app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app);
|
||||
};
|
||||
|
||||
void flipp_pomodoro_scene_timer_handle_custom_event(
|
||||
FlippPomodoroApp* app,
|
||||
FlippPomodoroAppCustomEvent custom_event) {
|
||||
if(custom_event == FlippPomodoroAppCustomEventTimerTick &&
|
||||
flipp_pomodoro__is_stage_expired(app->state)) {
|
||||
view_dispatcher_send_custom_event(
|
||||
app->view_dispatcher, FlippPomodoroAppCustomEventStageComplete);
|
||||
}
|
||||
|
||||
if(custom_event == FlippPomodoroAppCustomEventStateUpdated) {
|
||||
flipp_pomodoro_scene_timer_sync_view_state(app);
|
||||
}
|
||||
};
|
||||
|
||||
bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) {
|
||||
furi_assert(ctx);
|
||||
FlippPomodoroApp* app = ctx;
|
||||
|
||||
switch(event.type) {
|
||||
case SceneManagerEventTypeCustom:
|
||||
flipp_pomodoro_scene_timer_handle_custom_event(app, event.event);
|
||||
return SceneEventConusmed;
|
||||
case SceneManagerEventTypeBack:
|
||||
return ExitSignal;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
return SceneEventNotConusmed;
|
||||
};
|
||||
|
||||
void flipp_pomodoro_scene_timer_on_exit(void* ctx) {
|
||||
UNUSED(ctx);
|
||||
};
|
||||
@@ -0,0 +1,195 @@
|
||||
#include "flipp_pomodoro_timer_view.h"
|
||||
#include <furi.h>
|
||||
#include <gui/gui.h>
|
||||
#include <gui/elements.h>
|
||||
#include <gui/view.h>
|
||||
#include "../helpers/debug.h"
|
||||
#include "../flipp_pomodoro_app.h"
|
||||
#include "../modules/flipp_pomodoro.h"
|
||||
|
||||
// Auto-compiled icons
|
||||
#include "flipp_pomodoro_icons.h"
|
||||
|
||||
enum {
|
||||
ViewInputConsumed = true,
|
||||
ViewInputNotConusmed = false,
|
||||
};
|
||||
|
||||
struct FlippPomodoroTimerView {
|
||||
View* view;
|
||||
FlippPomodoroTimerViewInputCb right_cb;
|
||||
void* right_cb_ctx;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
IconAnimation* icon;
|
||||
FlippPomodoroState* state;
|
||||
} FlippPomodoroTimerViewModel;
|
||||
|
||||
static const Icon* stage_background_image[] = {
|
||||
[FlippPomodoroStageFocus] = &A_flipp_pomodoro_focus_64,
|
||||
[FlippPomodoroStageRest] = &A_flipp_pomodoro_rest_64,
|
||||
[FlippPomodoroStageLongBreak] = &A_flipp_pomodoro_rest_64,
|
||||
};
|
||||
|
||||
static void
|
||||
flipp_pomodoro_view_timer_draw_countdown(Canvas* canvas, TimeDifference remaining_time) {
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
const uint8_t right_border_margin = 1;
|
||||
|
||||
const uint8_t countdown_box_height = canvas_height(canvas) * 0.4;
|
||||
const uint8_t countdown_box_width = canvas_width(canvas) * 0.5;
|
||||
const uint8_t countdown_box_x =
|
||||
canvas_width(canvas) - countdown_box_width - right_border_margin;
|
||||
const uint8_t countdown_box_y = 15;
|
||||
|
||||
elements_bold_rounded_frame(
|
||||
canvas, countdown_box_x, countdown_box_y, countdown_box_width, countdown_box_height);
|
||||
|
||||
FuriString* timer_string = furi_string_alloc();
|
||||
furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds);
|
||||
const char* remaining_stage_time_string = furi_string_get_cstr(timer_string);
|
||||
canvas_draw_str_aligned(
|
||||
canvas,
|
||||
countdown_box_x + (countdown_box_width / 2),
|
||||
countdown_box_y + (countdown_box_height / 2),
|
||||
AlignCenter,
|
||||
AlignCenter,
|
||||
remaining_stage_time_string);
|
||||
|
||||
furi_string_free(timer_string);
|
||||
};
|
||||
|
||||
static void draw_str_with_drop_shadow(
|
||||
Canvas* canvas,
|
||||
uint8_t x,
|
||||
uint8_t y,
|
||||
Align horizontal,
|
||||
Align vertical,
|
||||
const char* str) {
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
for(int x_off = -2; x_off <= 2; x_off++) {
|
||||
for(int y_off = -2; y_off <= 2; y_off++) {
|
||||
canvas_draw_str_aligned(canvas, x + x_off, y + y_off, horizontal, vertical, str);
|
||||
}
|
||||
}
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, str);
|
||||
}
|
||||
|
||||
static void
|
||||
flipp_pomodoro_view_timer_draw_current_stage_label(Canvas* canvas, FlippPomodoroState* state) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
draw_str_with_drop_shadow(
|
||||
canvas,
|
||||
canvas_width(canvas),
|
||||
0,
|
||||
AlignRight,
|
||||
AlignTop,
|
||||
flipp_pomodoro__current_stage_label(state));
|
||||
}
|
||||
|
||||
static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) {
|
||||
if(!_model) {
|
||||
return;
|
||||
};
|
||||
|
||||
FlippPomodoroTimerViewModel* model = _model;
|
||||
|
||||
canvas_clear(canvas);
|
||||
if(model->icon) {
|
||||
canvas_draw_icon_animation(canvas, 0, 0, model->icon);
|
||||
}
|
||||
|
||||
flipp_pomodoro_view_timer_draw_countdown(
|
||||
canvas, flipp_pomodoro__stage_remaining_duration(model->state));
|
||||
|
||||
flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state);
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
|
||||
canvas_set_font(canvas, FontSecondary);
|
||||
elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state));
|
||||
};
|
||||
|
||||
bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
furi_assert(event);
|
||||
FlippPomodoroTimerView* timer = ctx;
|
||||
|
||||
const bool should_trigger_right_event_cb = (event->type == InputTypePress) &&
|
||||
(event->key == InputKeyRight) &&
|
||||
(timer->right_cb != NULL);
|
||||
|
||||
if(should_trigger_right_event_cb) {
|
||||
furi_assert(timer->right_cb);
|
||||
furi_assert(timer->right_cb_ctx);
|
||||
timer->right_cb(timer->right_cb_ctx);
|
||||
return ViewInputConsumed;
|
||||
};
|
||||
|
||||
return ViewInputNotConusmed;
|
||||
};
|
||||
|
||||
View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) {
|
||||
furi_assert(timer);
|
||||
return timer->view;
|
||||
};
|
||||
|
||||
void flipp_pomodoro_view_timer_assign_animation(View* view) {
|
||||
with_view_model(
|
||||
view,
|
||||
FlippPomodoroTimerViewModel * model,
|
||||
{
|
||||
furi_assert(model->state);
|
||||
if(model->icon) {
|
||||
icon_animation_free(model->icon);
|
||||
}
|
||||
model->icon = icon_animation_alloc(
|
||||
stage_background_image[flipp_pomodoro__get_stage(model->state)]);
|
||||
view_tie_icon_animation(view, model->icon);
|
||||
icon_animation_start(model->icon);
|
||||
},
|
||||
true);
|
||||
}
|
||||
|
||||
FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() {
|
||||
FlippPomodoroTimerView* timer = malloc(sizeof(FlippPomodoroTimerView));
|
||||
timer->view = view_alloc();
|
||||
|
||||
view_allocate_model(timer->view, ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel));
|
||||
view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer);
|
||||
view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback);
|
||||
view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback);
|
||||
|
||||
return timer;
|
||||
};
|
||||
|
||||
void flipp_pomodoro_view_timer_set_on_right_cb(
|
||||
FlippPomodoroTimerView* timer,
|
||||
FlippPomodoroTimerViewInputCb right_cb,
|
||||
void* right_cb_ctx) {
|
||||
furi_assert(right_cb);
|
||||
furi_assert(right_cb_ctx);
|
||||
timer->right_cb = right_cb;
|
||||
timer->right_cb_ctx = right_cb_ctx;
|
||||
};
|
||||
|
||||
void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) {
|
||||
furi_assert(view);
|
||||
furi_assert(state);
|
||||
with_view_model(
|
||||
view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false);
|
||||
flipp_pomodoro_view_timer_assign_animation(view);
|
||||
};
|
||||
|
||||
void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) {
|
||||
furi_assert(timer);
|
||||
with_view_model(
|
||||
timer->view,
|
||||
FlippPomodoroTimerViewModel * model,
|
||||
{ icon_animation_free(model->icon); },
|
||||
false);
|
||||
view_free(timer->view);
|
||||
|
||||
free(timer);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/view.h>
|
||||
#include "../modules/flipp_pomodoro.h"
|
||||
|
||||
typedef struct FlippPomodoroTimerView FlippPomodoroTimerView;
|
||||
|
||||
typedef void (*FlippPomodoroTimerViewInputCb)(void* ctx);
|
||||
|
||||
FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc();
|
||||
|
||||
View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer);
|
||||
|
||||
void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer);
|
||||
|
||||
void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state);
|
||||
|
||||
void flipp_pomodoro_view_timer_set_on_right_cb(
|
||||
FlippPomodoroTimerView* timer,
|
||||
FlippPomodoroTimerViewInputCb right_cb,
|
||||
void* right_cb_ctx);
|
||||
@@ -1,46 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#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);
|
||||
@@ -1,46 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#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);
|
||||
@@ -1,46 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#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);
|
||||