mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 06:58:36 -07:00
Merge remote-tracking branch 'ofw/dev' into mntm-dev
This commit is contained in:
@@ -593,3 +593,7 @@ const NotificationSequence sequence_lcd_contrast_update = {
|
||||
&message_lcd_contrast_update,
|
||||
NULL,
|
||||
};
|
||||
|
||||
const NotificationSequence sequence_empty = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -145,6 +145,9 @@ extern const NotificationSequence sequence_audiovisual_alert;
|
||||
// LCD
|
||||
extern const NotificationSequence sequence_lcd_contrast_update;
|
||||
|
||||
// Wait for notification queue become empty
|
||||
extern const NotificationSequence sequence_empty;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -5,6 +5,7 @@ App(
|
||||
provides=[
|
||||
"passport",
|
||||
"system_settings",
|
||||
"clock_settings",
|
||||
"about",
|
||||
],
|
||||
)
|
||||
|
||||
17
applications/settings/clock_settings/application.fam
Normal file
17
applications/settings/clock_settings/application.fam
Normal file
@@ -0,0 +1,17 @@
|
||||
App(
|
||||
appid="clock_settings",
|
||||
name="Clock & Alarm",
|
||||
apptype=FlipperAppType.SETTINGS,
|
||||
entry_point="clock_settings",
|
||||
requires=["gui"],
|
||||
provides=["clock_settings_start"],
|
||||
stack_size=1 * 1024,
|
||||
order=90,
|
||||
)
|
||||
|
||||
App(
|
||||
appid="clock_settings_start",
|
||||
apptype=FlipperAppType.STARTUP,
|
||||
entry_point="clock_settings_start",
|
||||
order=1000,
|
||||
)
|
||||
71
applications/settings/clock_settings/clock_settings.c
Normal file
71
applications/settings/clock_settings/clock_settings.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "clock_settings.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
static bool clock_settings_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
ClockSettings* app = context;
|
||||
return scene_manager_handle_custom_event(app->scene_manager, event);
|
||||
}
|
||||
|
||||
static bool clock_settings_back_event_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettings* app = context;
|
||||
return scene_manager_handle_back_event(app->scene_manager);
|
||||
}
|
||||
|
||||
ClockSettings* clock_settings_alloc() {
|
||||
ClockSettings* app = malloc(sizeof(ClockSettings));
|
||||
|
||||
app->gui = furi_record_open(RECORD_GUI);
|
||||
|
||||
app->view_dispatcher = view_dispatcher_alloc();
|
||||
app->scene_manager = scene_manager_alloc(&clock_settings_scene_handlers, app);
|
||||
view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
|
||||
|
||||
view_dispatcher_set_custom_event_callback(
|
||||
app->view_dispatcher, clock_settings_custom_event_callback);
|
||||
view_dispatcher_set_navigation_event_callback(
|
||||
app->view_dispatcher, clock_settings_back_event_callback);
|
||||
|
||||
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
app->pwm_view =
|
||||
clock_settings_module_alloc(view_dispatcher_get_event_loop(app->view_dispatcher));
|
||||
view_dispatcher_add_view(
|
||||
app->view_dispatcher, ClockSettingsViewPwm, clock_settings_module_get_view(app->pwm_view));
|
||||
|
||||
scene_manager_next_scene(app->scene_manager, ClockSettingsSceneStart);
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
void clock_settings_free(ClockSettings* app) {
|
||||
furi_assert(app);
|
||||
|
||||
// Views
|
||||
view_dispatcher_remove_view(app->view_dispatcher, ClockSettingsViewPwm);
|
||||
|
||||
clock_settings_module_free(app->pwm_view);
|
||||
|
||||
// View dispatcher
|
||||
view_dispatcher_free(app->view_dispatcher);
|
||||
scene_manager_free(app->scene_manager);
|
||||
|
||||
// Close records
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
free(app);
|
||||
}
|
||||
|
||||
int32_t clock_settings(void* p) {
|
||||
UNUSED(p);
|
||||
ClockSettings* clock_settings = clock_settings_alloc();
|
||||
|
||||
view_dispatcher_run(clock_settings->view_dispatcher);
|
||||
|
||||
clock_settings_free(clock_settings);
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
applications/settings/clock_settings/clock_settings.h
Normal file
31
applications/settings/clock_settings/clock_settings.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "scenes/clock_settings_scene.h"
|
||||
|
||||
#include <furi_hal_clock.h>
|
||||
#include <furi_hal_pwm.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_dispatcher.h>
|
||||
#include <gui/scene_manager.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include <gui/modules/variable_item_list.h>
|
||||
#include <gui/modules/submenu.h>
|
||||
#include "views/clock_settings_module.h"
|
||||
|
||||
typedef struct ClockSettings ClockSettings;
|
||||
|
||||
struct ClockSettings {
|
||||
Gui* gui;
|
||||
ViewDispatcher* view_dispatcher;
|
||||
SceneManager* scene_manager;
|
||||
ClockSettingsModule* pwm_view;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ClockSettingsViewPwm,
|
||||
} ClockSettingsView;
|
||||
|
||||
typedef enum {
|
||||
ClockSettingsCustomEventNone,
|
||||
} ClockSettingsCustomEvent;
|
||||
177
applications/settings/clock_settings/clock_settings_alarm.c
Normal file
177
applications/settings/clock_settings/clock_settings_alarm.c
Normal file
@@ -0,0 +1,177 @@
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
|
||||
#include <gui/gui.h>
|
||||
#include <gui/view_port.h>
|
||||
|
||||
#include <notification/notification.h>
|
||||
#include <notification/notification_messages.h>
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "ClockSettingsAlarm"
|
||||
|
||||
typedef struct {
|
||||
DateTime now;
|
||||
IconAnimation* icon;
|
||||
} ClockSettingsAlramModel;
|
||||
|
||||
const NotificationSequence sequence_alarm = {
|
||||
&message_force_speaker_volume_setting_1f,
|
||||
&message_force_vibro_setting_on,
|
||||
&message_force_display_brightness_setting_1f,
|
||||
&message_vibro_on,
|
||||
|
||||
&message_display_backlight_on,
|
||||
&message_note_c7,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_off,
|
||||
&message_note_c4,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_on,
|
||||
&message_note_c7,
|
||||
&message_delay_250,
|
||||
|
||||
&message_display_backlight_off,
|
||||
&message_note_c4,
|
||||
&message_delay_250,
|
||||
|
||||
&message_sound_off,
|
||||
&message_vibro_off,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void clock_settings_alarm_draw_callback(Canvas* canvas, void* ctx) {
|
||||
ClockSettingsAlramModel* model = ctx;
|
||||
char buffer[64] = {};
|
||||
|
||||
canvas_draw_icon_animation(canvas, 5, 6, model->icon);
|
||||
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
snprintf(buffer, sizeof(buffer), "%02u:%02u", model->now.hour, model->now.minute);
|
||||
canvas_draw_str(canvas, 58, 32, buffer);
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
snprintf(
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
"%02u.%02u.%04u",
|
||||
model->now.day,
|
||||
model->now.month,
|
||||
model->now.year);
|
||||
canvas_draw_str(canvas, 60, 44, buffer);
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_input_callback(InputEvent* input_event, void* ctx) {
|
||||
furi_assert(ctx);
|
||||
FuriMessageQueue* event_queue = ctx;
|
||||
furi_message_queue_put(event_queue, input_event, FuriWaitForever);
|
||||
}
|
||||
|
||||
void clock_settings_alarm_animation_callback(IconAnimation* instance, void* context) {
|
||||
UNUSED(instance);
|
||||
ViewPort* view_port = context;
|
||||
view_port_update(view_port);
|
||||
}
|
||||
|
||||
int32_t clock_settings_alarm(void* p) {
|
||||
UNUSED(p);
|
||||
|
||||
// View Model
|
||||
ClockSettingsAlramModel model;
|
||||
|
||||
furi_hal_rtc_get_datetime(&model.now);
|
||||
model.icon = icon_animation_alloc(&A_Alarm_47x39);
|
||||
|
||||
// Alloc message queue
|
||||
FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
|
||||
|
||||
// Configure view port
|
||||
ViewPort* view_port = view_port_alloc();
|
||||
view_port_draw_callback_set(view_port, clock_settings_alarm_draw_callback, &model);
|
||||
view_port_input_callback_set(view_port, clock_settings_alarm_input_callback, event_queue);
|
||||
|
||||
// Register view port in GUI
|
||||
Gui* gui = furi_record_open(RECORD_GUI);
|
||||
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
|
||||
|
||||
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
|
||||
notification_message(notification, &sequence_alarm);
|
||||
|
||||
icon_animation_set_update_callback(
|
||||
model.icon, clock_settings_alarm_animation_callback, view_port);
|
||||
icon_animation_start(model.icon);
|
||||
|
||||
// Process events
|
||||
InputEvent event;
|
||||
bool running = true;
|
||||
while(running) {
|
||||
if(furi_message_queue_get(event_queue, &event, 2000) == FuriStatusOk) {
|
||||
if(event.type == InputTypePress) {
|
||||
running = false;
|
||||
}
|
||||
} else {
|
||||
notification_message(notification, &sequence_alarm);
|
||||
furi_hal_rtc_get_datetime(&model.now);
|
||||
view_port_update(view_port);
|
||||
}
|
||||
}
|
||||
|
||||
icon_animation_stop(model.icon);
|
||||
|
||||
notification_message_block(notification, &sequence_empty);
|
||||
furi_record_close(RECORD_NOTIFICATION);
|
||||
|
||||
view_port_enabled_set(view_port, false);
|
||||
gui_remove_view_port(gui, view_port);
|
||||
view_port_free(view_port);
|
||||
furi_message_queue_free(event_queue);
|
||||
furi_record_close(RECORD_GUI);
|
||||
|
||||
icon_animation_free(model.icon);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FuriThread* clock_settings_alarm_thread = NULL;
|
||||
|
||||
static void clock_settings_alarm_thread_state_callback(
|
||||
FuriThread* thread,
|
||||
FuriThreadState state,
|
||||
void* context) {
|
||||
furi_assert(clock_settings_alarm_thread == thread);
|
||||
UNUSED(context);
|
||||
|
||||
if(state == FuriThreadStateStopped) {
|
||||
furi_thread_free(thread);
|
||||
clock_settings_alarm_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_start(void* context, uint32_t arg) {
|
||||
UNUSED(context);
|
||||
UNUSED(arg);
|
||||
|
||||
FURI_LOG_I(TAG, "spawning alarm thread");
|
||||
|
||||
if(clock_settings_alarm_thread) return;
|
||||
|
||||
clock_settings_alarm_thread =
|
||||
furi_thread_alloc_ex("ClockAlarm", 1024, clock_settings_alarm, NULL);
|
||||
furi_thread_set_state_callback(
|
||||
clock_settings_alarm_thread, clock_settings_alarm_thread_state_callback);
|
||||
furi_thread_start(clock_settings_alarm_thread);
|
||||
}
|
||||
|
||||
static void clock_settings_alarm_isr(void* context) {
|
||||
UNUSED(context);
|
||||
furi_timer_pending_callback(clock_settings_alarm_start, NULL, 0);
|
||||
}
|
||||
|
||||
void clock_settings_start(void) {
|
||||
#ifndef FURI_RAM_EXEC
|
||||
furi_hal_rtc_set_alarm_callback(clock_settings_alarm_isr, NULL);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "../clock_settings.h"
|
||||
|
||||
// Generate scene on_enter handlers array
|
||||
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
|
||||
void (*const clock_settings_scene_on_enter_handlers[])(void*) = {
|
||||
#include "clock_settings_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 clock_settings_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
|
||||
#include "clock_settings_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 clock_settings_scene_on_exit_handlers[])(void* context) = {
|
||||
#include "clock_settings_scene_config.h"
|
||||
};
|
||||
#undef ADD_SCENE
|
||||
|
||||
// Initialize scene handlers configuration structure
|
||||
const SceneManagerHandlers clock_settings_scene_handlers = {
|
||||
.on_enter_handlers = clock_settings_scene_on_enter_handlers,
|
||||
.on_event_handlers = clock_settings_scene_on_event_handlers,
|
||||
.on_exit_handlers = clock_settings_scene_on_exit_handlers,
|
||||
.scene_num = ClockSettingsSceneNum,
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <gui/scene_manager.h>
|
||||
|
||||
// Generate scene id and total number
|
||||
#define ADD_SCENE(prefix, name, id) ClockSettingsScene##id,
|
||||
typedef enum {
|
||||
#include "clock_settings_scene_config.h"
|
||||
ClockSettingsSceneNum,
|
||||
} ClockSettingsScene;
|
||||
#undef ADD_SCENE
|
||||
|
||||
extern const SceneManagerHandlers clock_settings_scene_handlers;
|
||||
|
||||
// Generate scene on_enter handlers declaration
|
||||
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
|
||||
#include "clock_settings_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* context, SceneManagerEvent event);
|
||||
#include "clock_settings_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* context);
|
||||
#include "clock_settings_scene_config.h"
|
||||
#undef ADD_SCENE
|
||||
@@ -0,0 +1 @@
|
||||
ADD_SCENE(clock_settings, start, Start)
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "../clock_settings.h"
|
||||
#include <furi_hal.h>
|
||||
|
||||
#define TAG "SceneStart"
|
||||
|
||||
typedef enum {
|
||||
SubmenuIndexPwm,
|
||||
SubmenuIndexClockOutput,
|
||||
} SubmenuIndex;
|
||||
|
||||
void clock_settings_scene_start_submenu_callback(void* context, uint32_t index) {
|
||||
ClockSettings* app = context;
|
||||
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, index);
|
||||
}
|
||||
|
||||
void clock_settings_scene_start_on_enter(void* context) {
|
||||
ClockSettings* app = context;
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, ClockSettingsViewPwm);
|
||||
}
|
||||
|
||||
bool clock_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
|
||||
UNUSED(context);
|
||||
UNUSED(event);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void clock_settings_scene_start_on_exit(void* context) {
|
||||
UNUSED(context);
|
||||
}
|
||||
@@ -0,0 +1,438 @@
|
||||
#include "clock_settings_module.h"
|
||||
|
||||
#include <gui/elements.h>
|
||||
#include <assets_icons.h>
|
||||
#include <locale/locale.h>
|
||||
|
||||
#define TAG "ClockSettingsModule"
|
||||
|
||||
struct ClockSettingsModule {
|
||||
FuriEventLoopTimer* timer;
|
||||
View* view;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
DateTime current;
|
||||
DateTime alarm;
|
||||
bool alarm_enabled;
|
||||
bool editing;
|
||||
|
||||
uint8_t row;
|
||||
uint8_t column;
|
||||
} ClockSettingsModuleViewModel;
|
||||
|
||||
typedef enum {
|
||||
EditStateNone,
|
||||
EditStateActive,
|
||||
EditStateActiveEditing,
|
||||
} EditState;
|
||||
|
||||
#define get_state(m, r, c) \
|
||||
((m)->row == (r) && (m)->column == (c) ? \
|
||||
((m)->editing ? EditStateActiveEditing : EditStateActive) : \
|
||||
EditStateNone)
|
||||
|
||||
#define ROW_0_Y (4)
|
||||
#define ROW_0_H (20)
|
||||
|
||||
#define ROW_1_Y (30)
|
||||
#define ROW_1_H (12)
|
||||
|
||||
#define ROW_2_Y (48)
|
||||
#define ROW_2_H (12)
|
||||
|
||||
#define ROW_COUNT 3
|
||||
#define COLUMN_COUNT 3
|
||||
|
||||
static inline void clock_settings_module_cleanup_date(DateTime* dt) {
|
||||
uint8_t day_per_month =
|
||||
datetime_get_days_per_month(datetime_is_leap_year(dt->year), dt->month);
|
||||
if(dt->day > day_per_month) {
|
||||
dt->day = day_per_month;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void clock_settings_module_draw_block(
|
||||
Canvas* canvas,
|
||||
int32_t x,
|
||||
int32_t y,
|
||||
size_t w,
|
||||
size_t h,
|
||||
Font font,
|
||||
EditState state,
|
||||
const char* text) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
if(state != EditStateNone) {
|
||||
if(state == EditStateActiveEditing) {
|
||||
canvas_draw_icon(canvas, x + w / 2 - 2, y - 1 - 3, &I_SmallArrowUp_3x5);
|
||||
canvas_draw_icon(canvas, x + w / 2 - 2, y + h + 1, &I_SmallArrowDown_3x5);
|
||||
}
|
||||
canvas_draw_rbox(canvas, x, y, w, h, 1);
|
||||
canvas_set_color(canvas, ColorWhite);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, x, y, w, h, 1);
|
||||
}
|
||||
|
||||
canvas_set_font(canvas, font);
|
||||
canvas_draw_str_aligned(canvas, x + w / 2, y + h / 2, AlignCenter, AlignCenter, text);
|
||||
if(state != EditStateNone) {
|
||||
canvas_set_color(canvas, ColorBlack);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_time_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_0_Y + 15, "Time");
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.hour);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 32, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 0), buffer);
|
||||
canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7, 2, 2);
|
||||
canvas_draw_box(canvas, 62, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.minute);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 66, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 1), buffer);
|
||||
canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7, 2, 2);
|
||||
canvas_draw_box(canvas, 96, ROW_0_Y + ROW_0_H - 7 - 6, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.second);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 100, ROW_0_Y, 28, ROW_0_H, FontBigNumbers, get_state(model, 0, 2), buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_date_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_1_Y + 9, "Date");
|
||||
// Day
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.day);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 44, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 0), buffer);
|
||||
canvas_draw_box(canvas, 71 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);
|
||||
// Month
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->current.month);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 71, ROW_1_Y, 17, ROW_1_H, FontPrimary, get_state(model, 1, 1), buffer);
|
||||
canvas_draw_box(canvas, 98 - 6, ROW_1_Y + ROW_1_H - 4, 2, 2);
|
||||
// Year
|
||||
snprintf(buffer, sizeof(buffer), "%04u", model->current.year);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 98, ROW_1_Y, 30, ROW_1_H, FontPrimary, get_state(model, 1, 2), buffer);
|
||||
}
|
||||
|
||||
static void
|
||||
clock_settings_module_draw_alarm_callback(Canvas* canvas, ClockSettingsModuleViewModel* model) {
|
||||
char buffer[64];
|
||||
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
canvas_draw_str(canvas, 0, ROW_2_Y + 9, "Alarm");
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->alarm.hour);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 58, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 0), buffer);
|
||||
canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4, 2, 2);
|
||||
canvas_draw_box(canvas, 81 - 4, ROW_2_Y + ROW_2_H - 4 - 4, 2, 2);
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%02u", model->alarm.minute);
|
||||
clock_settings_module_draw_block(
|
||||
canvas, 81, ROW_2_Y, 17, ROW_2_H, FontPrimary, get_state(model, 2, 1), buffer);
|
||||
|
||||
clock_settings_module_draw_block(
|
||||
canvas,
|
||||
106,
|
||||
ROW_2_Y,
|
||||
22,
|
||||
ROW_2_H,
|
||||
FontPrimary,
|
||||
get_state(model, 2, 2),
|
||||
model->alarm_enabled ? "On" : "Off");
|
||||
}
|
||||
|
||||
static void clock_settings_module_draw_callback(Canvas* canvas, void* _model) {
|
||||
ClockSettingsModuleViewModel* model = _model;
|
||||
clock_settings_module_draw_time_callback(canvas, model);
|
||||
clock_settings_module_draw_date_callback(canvas, model);
|
||||
clock_settings_module_draw_alarm_callback(canvas, model);
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_navigation_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->row > 0) model->row--;
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->row < ROW_COUNT - 1) model->row++;
|
||||
} else if(event->key == InputKeyOk) {
|
||||
model->editing = !model->editing;
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(model->column < COLUMN_COUNT - 1) model->column++;
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
if(model->column > 0) model->column--;
|
||||
} else if(event->key == InputKeyBack && model->editing) {
|
||||
model->editing = false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_time_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
model->current.hour++;
|
||||
model->current.hour = model->current.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
model->current.minute++;
|
||||
model->current.minute = model->current.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->current.second++;
|
||||
model->current.second = model->current.second % 60;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.hour > 0) {
|
||||
model->current.hour--;
|
||||
} else {
|
||||
model->current.hour = 23;
|
||||
}
|
||||
model->current.hour = model->current.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.minute > 0) {
|
||||
model->current.minute--;
|
||||
} else {
|
||||
model->current.minute = 59;
|
||||
}
|
||||
model->current.minute = model->current.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.second > 0) {
|
||||
model->current.second--;
|
||||
} else {
|
||||
model->current.second = 59;
|
||||
}
|
||||
model->current.second = model->current.second % 60;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_date_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.day < 31) model->current.day++;
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.month < 12) {
|
||||
model->current.month++;
|
||||
}
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.year < 2099) {
|
||||
model->current.year++;
|
||||
}
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->current.day > 1) {
|
||||
model->current.day--;
|
||||
}
|
||||
} else if(model->column == 1) {
|
||||
if(model->current.month > 1) {
|
||||
model->current.month--;
|
||||
}
|
||||
} else if(model->column == 2) {
|
||||
if(model->current.year > 2000) {
|
||||
model->current.year--;
|
||||
}
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
clock_settings_module_cleanup_date(&model->current);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_alarm_callback(
|
||||
InputEvent* event,
|
||||
ClockSettingsModuleViewModel* model) {
|
||||
if(event->key == InputKeyUp) {
|
||||
if(model->column == 0) {
|
||||
model->alarm.hour++;
|
||||
model->alarm.hour = model->alarm.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
model->alarm.minute++;
|
||||
model->alarm.minute = model->alarm.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->alarm_enabled = !model->alarm_enabled;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else if(event->key == InputKeyDown) {
|
||||
if(model->column == 0) {
|
||||
if(model->alarm.hour > 0) {
|
||||
model->alarm.hour--;
|
||||
} else {
|
||||
model->alarm.hour = 23;
|
||||
}
|
||||
model->alarm.hour = model->alarm.hour % 24;
|
||||
} else if(model->column == 1) {
|
||||
if(model->alarm.minute > 0) {
|
||||
model->alarm.minute--;
|
||||
} else {
|
||||
model->alarm.minute = 59;
|
||||
}
|
||||
model->alarm.minute = model->alarm.minute % 60;
|
||||
} else if(model->column == 2) {
|
||||
model->alarm_enabled = !model->alarm_enabled;
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
return clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool clock_settings_module_input_callback(InputEvent* event, void* context) {
|
||||
furi_assert(context);
|
||||
|
||||
ClockSettingsModule* instance = context;
|
||||
bool consumed = false;
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
ClockSettingsModuleViewModel * model,
|
||||
{
|
||||
if(event->type == InputTypeShort || event->type == InputTypeRepeat) {
|
||||
bool previous_editing = model->editing;
|
||||
if(model->editing) {
|
||||
if(model->row == 0) {
|
||||
consumed = clock_settings_module_input_time_callback(event, model);
|
||||
} else if(model->row == 1) {
|
||||
consumed = clock_settings_module_input_date_callback(event, model);
|
||||
} else if(model->row == 2) {
|
||||
consumed = clock_settings_module_input_alarm_callback(event, model);
|
||||
} else {
|
||||
furi_crash();
|
||||
}
|
||||
} else {
|
||||
consumed = clock_settings_module_input_navigation_callback(event, model);
|
||||
}
|
||||
|
||||
// Switching between navigate/edit
|
||||
if(model->editing != previous_editing) {
|
||||
if(model->row == 2) {
|
||||
if(!model->editing) {
|
||||
// Disable alarm
|
||||
furi_hal_rtc_set_alarm(NULL, false);
|
||||
// Set new alarm
|
||||
furi_hal_rtc_set_alarm(&model->alarm, model->alarm_enabled);
|
||||
// Confirm
|
||||
model->alarm_enabled = furi_hal_rtc_get_alarm(&model->alarm);
|
||||
}
|
||||
} else {
|
||||
if(model->editing) {
|
||||
// stop timer to prevent mess with current date time
|
||||
furi_event_loop_timer_stop(instance->timer);
|
||||
} else {
|
||||
// save date time and restart timer
|
||||
furi_hal_rtc_set_datetime(&model->current);
|
||||
furi_event_loop_timer_start(instance->timer, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
true);
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
static void clock_settings_module_timer_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
|
||||
DateTime dt;
|
||||
furi_hal_rtc_get_datetime(&dt);
|
||||
with_view_model(
|
||||
instance->view, ClockSettingsModuleViewModel * model, { model->current = dt; }, true);
|
||||
}
|
||||
|
||||
static void clock_settings_module_view_enter_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
|
||||
clock_settings_module_timer_callback(context);
|
||||
|
||||
DateTime alarm;
|
||||
bool enabled = furi_hal_rtc_get_alarm(&alarm);
|
||||
|
||||
with_view_model(
|
||||
instance->view,
|
||||
ClockSettingsModuleViewModel * model,
|
||||
{
|
||||
model->alarm = alarm;
|
||||
model->alarm_enabled = enabled;
|
||||
},
|
||||
true);
|
||||
|
||||
furi_event_loop_timer_start(instance->timer, 1000);
|
||||
}
|
||||
|
||||
static void clock_settings_module_view_exit_callback(void* context) {
|
||||
furi_assert(context);
|
||||
ClockSettingsModule* instance = context;
|
||||
furi_event_loop_timer_stop(instance->timer);
|
||||
}
|
||||
|
||||
ClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop) {
|
||||
ClockSettingsModule* instance = malloc(sizeof(ClockSettingsModule));
|
||||
|
||||
instance->timer = furi_event_loop_timer_alloc(
|
||||
event_loop, clock_settings_module_timer_callback, FuriEventLoopTimerTypePeriodic, instance);
|
||||
instance->view = view_alloc();
|
||||
view_set_enter_callback(instance->view, clock_settings_module_view_enter_callback);
|
||||
view_set_exit_callback(instance->view, clock_settings_module_view_exit_callback);
|
||||
view_allocate_model(
|
||||
instance->view, ViewModelTypeLocking, sizeof(ClockSettingsModuleViewModel));
|
||||
with_view_model(
|
||||
instance->view, ClockSettingsModuleViewModel * model, { model->row = 0; }, false);
|
||||
view_set_context(instance->view, instance);
|
||||
view_set_draw_callback(instance->view, clock_settings_module_draw_callback);
|
||||
view_set_input_callback(instance->view, clock_settings_module_input_callback);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void clock_settings_module_free(ClockSettingsModule* instance) {
|
||||
furi_assert(instance);
|
||||
view_free(instance->view);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
View* clock_settings_module_get_view(ClockSettingsModule* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->view;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include <gui/view.h>
|
||||
|
||||
typedef struct ClockSettingsModule ClockSettingsModule;
|
||||
typedef void (*ClockSettingsModuleViewCallback)(
|
||||
uint8_t channel_id,
|
||||
uint32_t freq,
|
||||
uint8_t duty,
|
||||
void* context);
|
||||
|
||||
ClockSettingsModule* clock_settings_module_alloc(FuriEventLoop* event_loop);
|
||||
|
||||
void clock_settings_module_free(ClockSettingsModule* instance);
|
||||
|
||||
View* clock_settings_module_get_view(ClockSettingsModule* instance);
|
||||
|
||||
void clock_settings_module_set(
|
||||
ClockSettingsModule* instance,
|
||||
const DateTime* datetime,
|
||||
bool enabled);
|
||||
|
||||
bool clock_settings_module_get(ClockSettingsModule* instance, DateTime* datetime);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@flipperdevices/fz-sdk",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "Type declarations and documentation for native JS modules available on Flipper Zero",
|
||||
"keywords": [
|
||||
"flipper",
|
||||
@@ -15,14 +15,12 @@
|
||||
"directory": "applications/system/js_app/packages/fz-sdk"
|
||||
},
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"dependencies": {
|
||||
"esbuild": "^0.24.0",
|
||||
"esbuild-plugin-tsc": "^0.4.0",
|
||||
"json5": "^2.2.3",
|
||||
"typedoc": "^0.26.10",
|
||||
"typedoc-material-theme": "^1.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"typedoc-material-theme": "^1.1.0",
|
||||
"prompts": "^2.4.2",
|
||||
"serialport": "^12.0.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user