[FL-2627] Flipper applications: SDK, build and debug system (#1387)

* Added support for running applications from SD card (FAPs - Flipper Application Packages)
* Added plugin_dist target for fbt to build FAPs
* All apps of type FlipperAppType.EXTERNAL and FlipperAppType.PLUGIN are built as FAPs by default
* Updated VSCode configuration for new fbt features - re-deploy stock configuration to use them
* Added debugging support for FAPs with fbt debug & VSCode
* Added public firmware API with automated versioning

Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SG <who.just.the.doctor@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
SG
2022-09-15 02:11:38 +10:00
committed by Aleksandr Kutuzov
parent 0f6f9ad52e
commit b9a766d909
895 changed files with 8862 additions and 1465 deletions

View File

@@ -0,0 +1,211 @@
#include <furi.h>
#include <dialogs/dialogs.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/empty_screen.h>
#include <m-string.h>
#include <furi_hal_version.h>
#include <furi_hal_region.h>
#include <furi_hal_bt.h>
typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMessage* message);
static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
const char* screen_header = "Product: Flipper Zero\n"
"Model: FZ.1\n";
const char* screen_text = "FCC ID: 2A2V6-FZ\n"
"IC: 27624-FZ";
dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop);
dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop);
result = dialog_message_show(dialogs, message);
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
return result;
}
static DialogMessageButton address_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
const char* screen_text = "Flipper Devices Inc\n"
"Suite B #551, 2803\n"
"Philadelphia Pike, Claymont\n"
"DE, USA 19703\n";
dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);
result = dialog_message_show(dialogs, message);
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
return result;
}
static DialogMessageButton compliance_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
const char* screen_text = "For all compliance\n"
"certificates please visit:\n"
"www.flipp.dev/compliance";
dialog_message_set_text(message, screen_text, 0, 0, AlignLeft, AlignTop);
result = dialog_message_show(dialogs, message);
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
return result;
}
static DialogMessageButton icon1_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
dialog_message_set_icon(message, &I_Certification1_103x56, 13, 0);
result = dialog_message_show(dialogs, message);
dialog_message_set_icon(message, NULL, 0, 0);
return result;
}
static DialogMessageButton icon2_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
dialog_message_set_icon(message, &I_Certification2_98x33, 15, 10);
result = dialog_message_show(dialogs, message);
dialog_message_set_icon(message, NULL, 0, 0);
return result;
}
static DialogMessageButton hw_version_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
string_t buffer;
string_init(buffer);
const char* my_name = furi_hal_version_get_name_ptr();
string_cat_printf(
buffer,
"%d.F%dB%dC%d %s:%s %s\n",
furi_hal_version_get_hw_version(),
furi_hal_version_get_hw_target(),
furi_hal_version_get_hw_body(),
furi_hal_version_get_hw_connect(),
furi_hal_version_get_hw_region_name(),
furi_hal_region_get_name(),
my_name ? my_name : "Unknown");
string_cat_printf(buffer, "Serial Number:\n");
const uint8_t* uid = furi_hal_version_uid();
for(size_t i = 0; i < furi_hal_version_uid_size(); i++) {
string_cat_printf(buffer, "%02X", uid[i]);
}
dialog_message_set_header(message, "HW Version Info:", 0, 0, AlignLeft, AlignTop);
dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
result = dialog_message_show(dialogs, message);
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
string_clear(buffer);
return result;
}
static DialogMessageButton fw_version_screen(DialogsApp* dialogs, DialogMessage* message) {
DialogMessageButton result;
string_t buffer;
string_init(buffer);
const Version* ver = furi_hal_version_get_firmware_version();
const BleGlueC2Info* c2_ver = NULL;
#ifdef SRV_BT
c2_ver = ble_glue_get_c2_info();
#endif
if(!ver) {
string_cat_printf(buffer, "No info\n");
} else {
string_cat_printf(
buffer,
"%s [%s]\n%s%s [%s] %s\n[%d] %s",
version_get_version(ver),
version_get_builddate(ver),
version_get_dirty_flag(ver) ? "[!] " : "",
version_get_githash(ver),
version_get_gitbranchnum(ver),
c2_ver ? c2_ver->StackTypeString : "<none>",
version_get_target(ver),
version_get_gitbranch(ver));
}
dialog_message_set_header(message, "FW Version Info:", 0, 0, AlignLeft, AlignTop);
dialog_message_set_text(message, string_get_cstr(buffer), 0, 13, AlignLeft, AlignTop);
result = dialog_message_show(dialogs, message);
dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop);
dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop);
string_clear(buffer);
return result;
}
const AboutDialogScreen about_screens[] = {
product_screen,
compliance_screen,
address_screen,
icon1_screen,
icon2_screen,
hw_version_screen,
fw_version_screen};
const size_t about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen);
int32_t about_settings_app(void* p) {
UNUSED(p);
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessage* message = dialog_message_alloc();
Gui* gui = furi_record_open(RECORD_GUI);
ViewDispatcher* view_dispatcher = view_dispatcher_alloc();
EmptyScreen* empty_screen = empty_screen_alloc();
const uint32_t empty_screen_index = 0;
size_t screen_index = 0;
DialogMessageButton screen_result;
// draw empty screen to prevent menu flickering
view_dispatcher_add_view(
view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen));
view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
view_dispatcher_switch_to_view(view_dispatcher, empty_screen_index);
while(1) {
if(screen_index >= about_screens_count - 1) {
dialog_message_set_buttons(message, "Back", NULL, NULL);
} else {
dialog_message_set_buttons(message, "Back", NULL, "Next");
}
screen_result = about_screens[screen_index](dialogs, message);
if(screen_result == DialogMessageButtonLeft) {
if(screen_index <= 0) {
break;
} else {
screen_index--;
}
} else if(screen_result == DialogMessageButtonRight) {
if(screen_index < about_screens_count) {
screen_index++;
}
} else if(screen_result == DialogMessageButtonBack) {
break;
}
}
dialog_message_free(message);
furi_record_close(RECORD_DIALOGS);
view_dispatcher_remove_view(view_dispatcher, empty_screen_index);
view_dispatcher_free(view_dispatcher);
empty_screen_free(empty_screen);
furi_record_close(RECORD_GUI);
return 0;
}

View File

@@ -0,0 +1,13 @@
App(
appid="about",
name="About",
apptype=FlipperAppType.SETTINGS,
entry_point="about_settings_app",
cdefines=["APP_ABOUT"],
requires=[
"gui",
"dialogs",
],
stack_size=1 * 1024,
order=1000,
)

View File

@@ -0,0 +1,10 @@
App(
appid="settings_apps",
name="Basic settings apps bundle",
apptype=FlipperAppType.METAPACKAGE,
provides=[
"passport",
"system_settings",
"about",
],
)

View File

@@ -0,0 +1,12 @@
App(
appid="bt_settings",
name="Bluetooth",
apptype=FlipperAppType.SETTINGS,
entry_point="bt_settings_app",
stack_size=1 * 1024,
requires=[
"bt",
"gui",
],
order=10,
)

View File

@@ -0,0 +1,85 @@
#include "bt_settings_app.h"
static bool bt_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
BtSettingsApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool bt_settings_back_event_callback(void* context) {
furi_assert(context);
BtSettingsApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
BtSettingsApp* bt_settings_app_alloc() {
BtSettingsApp* app = malloc(sizeof(BtSettingsApp));
// Load settings
bt_settings_load(&app->settings);
app->gui = furi_record_open(RECORD_GUI);
app->bt = furi_record_open(RECORD_BT);
// View Dispatcher and Scene Manager
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&bt_settings_scene_handlers, app);
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, bt_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, bt_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Gui Modules
app->var_item_list = variable_item_list_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
BtSettingsAppViewVarItemList,
variable_item_list_get_view(app->var_item_list));
app->dialog = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BtSettingsAppViewDialog, dialog_ex_get_view(app->dialog));
app->popup = popup_alloc();
view_dispatcher_add_view(
app->view_dispatcher, BtSettingsAppViewPopup, popup_get_view(app->popup));
// Set first scene
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneStart);
return app;
}
void bt_settings_app_free(BtSettingsApp* app) {
furi_assert(app);
// Gui modules
view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewVarItemList);
variable_item_list_free(app->var_item_list);
view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewDialog);
dialog_ex_free(app->dialog);
view_dispatcher_remove_view(app->view_dispatcher, BtSettingsAppViewPopup);
popup_free(app->popup);
// View Dispatcher and Scene Manager
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Records
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_BT);
free(app);
}
extern int32_t bt_settings_app(void* p) {
UNUSED(p);
BtSettingsApp* app = bt_settings_app_alloc();
view_dispatcher_run(app->view_dispatcher);
bt_settings_save(&app->settings);
bt_settings_app_free(app);
return 0;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include <furi.h>
#include <bt/bt_service/bt.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <bt/bt_settings.h>
#include "scenes/bt_settings_scene.h"
enum BtSettingsCustomEvent {
// Keep first 10 events reserved for button types and indexes
BtSettingsCustomEventReserved = 10,
BtSettingsCustomEventForgetDevices,
BtSettingsCustomEventExitView,
};
typedef struct {
BtSettings settings;
Bt* bt;
Gui* gui;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
VariableItemList* var_item_list;
DialogEx* dialog;
Popup* popup;
} BtSettingsApp;
typedef enum {
BtSettingsAppViewVarItemList,
BtSettingsAppViewDialog,
BtSettingsAppViewPopup,
} BtSettingsAppView;

View File

@@ -0,0 +1,30 @@
#include "bt_settings_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const bt_settings_on_enter_handlers[])(void*) = {
#include "bt_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 bt_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "bt_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 bt_settings_on_exit_handlers[])(void* context) = {
#include "bt_settings_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers bt_settings_scene_handlers = {
.on_enter_handlers = bt_settings_on_enter_handlers,
.on_event_handlers = bt_settings_on_event_handlers,
.on_exit_handlers = bt_settings_on_exit_handlers,
.scene_num = BtSettingsAppSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) BtSettingsAppScene##id,
typedef enum {
#include "bt_settings_scene_config.h"
BtSettingsAppSceneNum,
} BtSettingsAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers bt_settings_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "bt_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 "bt_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 "bt_settings_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,3 @@
ADD_SCENE(bt_settings, start, Start)
ADD_SCENE(bt_settings, forget_dev_confirm, ForgetDevConfirm)
ADD_SCENE(bt_settings, forget_dev_success, ForgetDevSuccess)

View File

@@ -0,0 +1,44 @@
#include "../bt_settings_app.h"
#include "furi_hal_bt.h"
void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
BtSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void bt_settings_scene_forget_dev_confirm_on_enter(void* context) {
BtSettingsApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_header(dialog, "Unpair All Devices?", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog, "All previous pairings\nwill be lost!", 64, 22, AlignCenter, AlignTop);
dialog_ex_set_left_button_text(dialog, "Back");
dialog_ex_set_right_button_text(dialog, "Unpair");
dialog_ex_set_context(dialog, app);
dialog_ex_set_result_callback(dialog, bt_settings_scene_forget_dev_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewDialog);
}
bool bt_settings_scene_forget_dev_confirm_on_event(void* context, SceneManagerEvent event) {
BtSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) {
consumed = scene_manager_previous_scene(app->scene_manager);
} else if(event.event == DialogExResultRight) {
bt_forget_bonded_devices(app->bt);
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevSuccess);
consumed = true;
}
}
return consumed;
}
void bt_settings_scene_forget_dev_confirm_on_exit(void* context) {
BtSettingsApp* app = context;
dialog_ex_reset(app->dialog);
}

View File

@@ -0,0 +1,41 @@
#include "../bt_settings_app.h"
#include "furi_hal_bt.h"
void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) {
BtSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, BtSettingsCustomEventExitView);
}
void bt_settings_scene_forget_dev_success_on_enter(void* context) {
BtSettingsApp* app = context;
Popup* popup = app->popup;
popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
popup_set_header(popup, "Done", 14, 15, AlignLeft, AlignTop);
popup_set_timeout(popup, 1500);
popup_set_context(popup, app);
popup_set_callback(popup, bt_settings_app_scene_forget_dev_success_popup_callback);
popup_enable_timeout(popup);
view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewPopup);
}
bool bt_settings_scene_forget_dev_success_on_event(void* context, SceneManagerEvent event) {
BtSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BtSettingsCustomEventExitView) {
if(scene_manager_has_previous_scene(app->scene_manager, BtSettingsAppSceneStart)) {
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, BtSettingsAppSceneStart);
}
}
}
return consumed;
}
void bt_settings_scene_forget_dev_success_on_exit(void* context) {
BtSettingsApp* app = context;
popup_reset(app->popup);
}

View File

@@ -0,0 +1,91 @@
#include "../bt_settings_app.h"
#include "furi_hal_bt.h"
enum BtSetting {
BtSettingOff,
BtSettingOn,
BtSettingNum,
};
enum BtSettingIndex {
BtSettingIndexSwitchBt,
BtSettingIndexForgetDev,
};
const char* const bt_settings_text[BtSettingNum] = {
"OFF",
"ON",
};
static void bt_settings_scene_start_var_list_change_callback(VariableItem* item) {
BtSettingsApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, bt_settings_text[index]);
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
static void bt_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {
furi_assert(context);
BtSettingsApp* app = context;
if(index == BtSettingIndexForgetDev) {
view_dispatcher_send_custom_event(
app->view_dispatcher, BtSettingsCustomEventForgetDevices);
}
}
void bt_settings_scene_start_on_enter(void* context) {
BtSettingsApp* app = context;
VariableItemList* var_item_list = app->var_item_list;
VariableItem* item;
if(furi_hal_bt_is_ble_gatt_gap_supported()) {
item = variable_item_list_add(
var_item_list,
"Bluetooth",
BtSettingNum,
bt_settings_scene_start_var_list_change_callback,
app);
if(app->settings.enabled) {
variable_item_set_current_value_index(item, BtSettingOn);
variable_item_set_current_value_text(item, bt_settings_text[BtSettingOn]);
} else {
variable_item_set_current_value_index(item, BtSettingOff);
variable_item_set_current_value_text(item, bt_settings_text[BtSettingOff]);
}
variable_item_list_add(var_item_list, "Forget All Paired Devices", 1, NULL, NULL);
variable_item_list_set_enter_callback(
var_item_list, bt_settings_scene_start_var_list_enter_callback, app);
} else {
item = variable_item_list_add(var_item_list, "Bluetooth", 1, NULL, NULL);
variable_item_set_current_value_text(item, "Broken");
}
view_dispatcher_switch_to_view(app->view_dispatcher, BtSettingsAppViewVarItemList);
}
bool bt_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
BtSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == BtSettingOn) {
furi_hal_bt_start_advertising();
app->settings.enabled = true;
consumed = true;
} else if(event.event == BtSettingOff) {
app->settings.enabled = false;
furi_hal_bt_stop_advertising();
consumed = true;
} else if(event.event == BtSettingsCustomEventForgetDevices) {
scene_manager_next_scene(app->scene_manager, BtSettingsAppSceneForgetDevConfirm);
consumed = true;
}
}
return consumed;
}
void bt_settings_scene_start_on_exit(void* context) {
BtSettingsApp* app = context;
variable_item_list_reset(app->var_item_list);
}

View File

@@ -0,0 +1,12 @@
App(
appid="desktop_settings",
name="Desktop",
apptype=FlipperAppType.SETTINGS,
entry_point="desktop_settings_app",
requires=[
"desktop",
"gui",
],
stack_size=1 * 1024,
order=50,
)

View File

@@ -0,0 +1,102 @@
#include <furi.h>
#include <gui/modules/popup.h>
#include <gui/scene_manager.h>
#include "desktop_settings_app.h"
#include "scenes/desktop_settings_scene.h"
#include <desktop/views/desktop_view_pin_input.h>
static bool desktop_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
DesktopSettingsApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool desktop_settings_back_event_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
DesktopSettingsApp* desktop_settings_app_alloc() {
DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp));
app->gui = furi_record_open(RECORD_GUI);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app);
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, desktop_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, desktop_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->popup = popup_alloc();
app->submenu = submenu_alloc();
app->variable_item_list = variable_item_list_alloc();
app->pin_input_view = desktop_view_pin_input_alloc();
app->pin_setup_howto_view = desktop_settings_view_pin_setup_howto_alloc();
app->pin_setup_howto2_view = desktop_settings_view_pin_setup_howto2_alloc();
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewMenu, submenu_get_view(app->submenu));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewVarItemList,
variable_item_list_get_view(app->variable_item_list));
view_dispatcher_add_view(
app->view_dispatcher, DesktopSettingsAppViewIdPopup, popup_get_view(app->popup));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewIdPinInput,
desktop_view_pin_input_get_view(app->pin_input_view));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewIdPinSetupHowto,
desktop_settings_view_pin_setup_howto_get_view(app->pin_setup_howto_view));
view_dispatcher_add_view(
app->view_dispatcher,
DesktopSettingsAppViewIdPinSetupHowto2,
desktop_settings_view_pin_setup_howto2_get_view(app->pin_setup_howto2_view));
return app;
}
void desktop_settings_app_free(DesktopSettingsApp* app) {
furi_assert(app);
// Variable item list
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);
view_dispatcher_remove_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);
variable_item_list_free(app->variable_item_list);
submenu_free(app->submenu);
popup_free(app->popup);
desktop_view_pin_input_free(app->pin_input_view);
desktop_settings_view_pin_setup_howto_free(app->pin_setup_howto_view);
desktop_settings_view_pin_setup_howto2_free(app->pin_setup_howto2_view);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Records
furi_record_close(RECORD_GUI);
free(app);
}
extern int32_t desktop_settings_app(void* p) {
DesktopSettingsApp* app = desktop_settings_app_alloc();
LOAD_DESKTOP_SETTINGS(&app->settings);
if(p && (strcmp(p, DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG) == 0)) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
} else {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneStart);
}
view_dispatcher_run(app->view_dispatcher);
desktop_settings_app_free(app);
return 0;
}

View File

@@ -0,0 +1,41 @@
#pragma once
#include <gui/gui.h>
#include <gui/modules/popup.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <gui/modules/submenu.h>
#include <gui/modules/variable_item_list.h>
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#include "views/desktop_settings_view_pin_setup_howto.h"
#include "views/desktop_settings_view_pin_setup_howto2.h"
typedef enum {
DesktopSettingsAppViewMenu,
DesktopSettingsAppViewVarItemList,
DesktopSettingsAppViewIdPopup,
DesktopSettingsAppViewIdPinInput,
DesktopSettingsAppViewIdPinSetupHowto,
DesktopSettingsAppViewIdPinSetupHowto2,
} DesktopSettingsAppView;
typedef struct {
DesktopSettings settings;
Gui* gui;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
VariableItemList* variable_item_list;
Submenu* submenu;
Popup* popup;
DesktopViewPinInput* pin_input_view;
DesktopSettingsViewPinSetupHowto* pin_setup_howto_view;
DesktopSettingsViewPinSetupHowto2* pin_setup_howto2_view;
PinCode pincode_buffer;
bool pincode_buffer_filled;
uint8_t menu_idx;
} DesktopSettingsApp;

View File

@@ -0,0 +1,30 @@
#include "desktop_settings_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const desktop_settings_on_enter_handlers[])(void*) = {
#include "desktop_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 desktop_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "desktop_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 desktop_settings_on_exit_handlers[])(void* context) = {
#include "desktop_settings_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers desktop_settings_scene_handlers = {
.on_enter_handlers = desktop_settings_on_enter_handlers,
.on_event_handlers = desktop_settings_on_event_handlers,
.on_exit_handlers = desktop_settings_on_exit_handlers,
.scene_num = DesktopSettingsAppSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) DesktopSettingsAppScene##id,
typedef enum {
#include "desktop_settings_scene_config.h"
DesktopSettingsAppSceneNum,
} DesktopSettingsAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers desktop_settings_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "desktop_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 "desktop_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 "desktop_settings_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,11 @@
ADD_SCENE(desktop_settings, start, Start)
ADD_SCENE(desktop_settings, favorite, Favorite)
ADD_SCENE(desktop_settings, pin_menu, PinMenu)
ADD_SCENE(desktop_settings, pin_auth, PinAuth)
ADD_SCENE(desktop_settings, pin_error, PinError)
ADD_SCENE(desktop_settings, pin_disable, PinDisable)
ADD_SCENE(desktop_settings, pin_setup, PinSetup)
ADD_SCENE(desktop_settings, pin_setup_howto, PinSetupHowto)
ADD_SCENE(desktop_settings, pin_setup_howto2, PinSetupHowto2)
ADD_SCENE(desktop_settings, pin_setup_done, PinSetupDone)

View File

@@ -0,0 +1,61 @@
#include "../desktop_settings_app.h"
#include "applications.h"
#include "desktop_settings_scene.h"
static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void desktop_settings_scene_favorite_on_enter(void* context) {
DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_reset(submenu);
for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) {
submenu_add_item(
submenu,
FLIPPER_APPS[i].name,
i,
desktop_settings_scene_favorite_submenu_callback,
app);
}
uint32_t primary_favorite =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
submenu_set_header(
app->submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:");
if(primary_favorite) {
submenu_set_selected_item(app->submenu, app->settings.favorite_primary);
} else {
submenu_set_selected_item(app->submenu, app->settings.favorite_secondary);
}
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
}
bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
uint32_t primary_favorite =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite);
if(event.type == SceneManagerEventTypeCustom) {
if(primary_favorite) {
app->settings.favorite_primary = event.event;
} else {
app->settings.favorite_secondary = event.event;
}
scene_manager_previous_scene(app->scene_manager);
consumed = true;
}
return consumed;
}
void desktop_settings_scene_favorite_on_exit(void* context) {
DesktopSettingsApp* app = context;
SAVE_DESKTOP_SETTINGS(&app->settings);
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,7 @@
#pragma once
#define SCENE_STATE_PIN_AUTH_DISABLE (0)
#define SCENE_STATE_PIN_AUTH_CHANGE_PIN (1)
#define SCENE_STATE_PIN_ERROR_MISMATCH (0)
#define SCENE_STATE_PIN_ERROR_WRONG (1)

View File

@@ -0,0 +1,95 @@
#include <stdint.h>
#include <core/check.h>
#include <gui/scene_manager.h>
#include <desktop/helpers/pin_lock.h>
#include "../desktop_settings_app.h"
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#define SCENE_EVENT_EXIT (0U)
#define SCENE_EVENT_PINS_EQUAL (1U)
#define SCENE_EVENT_PINS_DIFFERENT (2U)
static void pin_auth_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
app->pincode_buffer = *pin_code;
if(desktop_pins_are_equal(&app->settings.pin_code, pin_code)) {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
} else {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
}
}
static void pin_auth_back_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_auth_on_enter(void* context) {
DesktopSettingsApp* app = context;
LOAD_DESKTOP_SETTINGS(&app->settings);
furi_assert(app->settings.pin_code.length > 0);
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_auth_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_auth_done_callback);
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Enter your current PIN:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_auth_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_PINS_DIFFERENT:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinError, SCENE_STATE_PIN_ERROR_WRONG);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);
consumed = true;
break;
case SCENE_EVENT_PINS_EQUAL: {
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinAuth);
if(state == SCENE_STATE_PIN_AUTH_CHANGE_PIN) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
} else if(state == SCENE_STATE_PIN_AUTH_DISABLE) {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinDisable);
} else {
furi_assert(0);
}
consumed = true;
break;
}
case SCENE_EVENT_EXIT:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_auth_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@@ -0,0 +1,56 @@
#include <stdint.h>
#include <core/check.h>
#include <gui/scene_manager.h>
#include <gui/modules/popup.h>
#include "../desktop_settings_app.h"
#include <desktop/desktop_settings.h>
#include "desktop_settings_scene.h"
#define SCENE_EVENT_EXIT (0U)
static void pin_disable_back_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_disable_on_enter(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
app->settings.pin_code.length = 0;
memset(app->settings.pin_code.data, '0', sizeof(app->settings.pin_code.data));
SAVE_DESKTOP_SETTINGS(&app->settings);
popup_set_context(app->popup, app);
popup_set_callback(app->popup, pin_disable_back_callback);
popup_set_icon(app->popup, 0, 2, &I_DolphinMafia_115x62);
popup_set_header(app->popup, "PIN\ndeleted!", 95, 9, AlignCenter, AlignCenter);
popup_set_timeout(app->popup, 1500);
popup_enable_timeout(app->popup);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPopup);
}
bool desktop_settings_scene_pin_disable_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_EXIT:
scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_disable_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,77 @@
#include <stdint.h>
#include <core/check.h>
#include <gui/scene_manager.h>
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#include <desktop/helpers/pin_lock.h>
#include "../desktop_settings_app.h"
#define SCENE_EVENT_EXIT (0U)
static void pin_error_back_callback(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
static void pin_error_done_callback(const PinCode* pin_code, void* context) {
UNUSED(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_error_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_pin_lock_error_notify();
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_error_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_error_done_callback);
uint32_t state =
scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppScenePinError);
if(state == SCENE_STATE_PIN_ERROR_MISMATCH) {
desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN mismatch!");
} else if(state == SCENE_STATE_PIN_ERROR_WRONG) {
desktop_view_pin_input_set_label_primary(app->pin_input_view, 35, 8, "Wrong PIN!");
} else {
furi_assert(0);
}
desktop_view_pin_input_set_label_secondary(app->pin_input_view, 0, 8, NULL);
desktop_view_pin_input_set_label_button(app->pin_input_view, "Retry");
desktop_view_pin_input_lock_input(app->pin_input_view);
desktop_view_pin_input_set_pin(app->pin_input_view, &app->pincode_buffer);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_error_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_EXIT:
scene_manager_previous_scene(app->scene_manager);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_error_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_unlock_input(app->pin_input_view);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@@ -0,0 +1,86 @@
#include <gui/scene_manager.h>
#include <applications.h>
#include "../desktop_settings_app.h"
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#define SCENE_EVENT_SET_PIN 0
#define SCENE_EVENT_CHANGE_PIN 1
#define SCENE_EVENT_DISABLE_PIN 2
static void desktop_settings_scene_pin_menu_submenu_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void desktop_settings_scene_pin_menu_on_enter(void* context) {
DesktopSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_reset(submenu);
if(!app->settings.pin_code.length) {
submenu_add_item(
submenu,
"Set Pin",
SCENE_EVENT_SET_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
} else {
submenu_add_item(
submenu,
"Change Pin",
SCENE_EVENT_CHANGE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
submenu_add_item(
submenu,
"Disable",
SCENE_EVENT_DISABLE_PIN,
desktop_settings_scene_pin_menu_submenu_callback,
app);
}
submenu_set_header(app->submenu, "Pin code settings:");
submenu_set_selected_item(app->submenu, app->menu_idx);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu);
}
bool desktop_settings_scene_pin_menu_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_SET_PIN:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto);
consumed = true;
break;
case SCENE_EVENT_CHANGE_PIN:
scene_manager_set_scene_state(
app->scene_manager,
DesktopSettingsAppScenePinAuth,
SCENE_STATE_PIN_AUTH_CHANGE_PIN);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);
consumed = true;
break;
case SCENE_EVENT_DISABLE_PIN:
scene_manager_set_scene_state(
app->scene_manager, DesktopSettingsAppScenePinAuth, SCENE_STATE_PIN_AUTH_DISABLE);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinAuth);
consumed = true;
break;
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_menu_on_exit(void* context) {
DesktopSettingsApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,108 @@
#include <stdint.h>
#include <core/check.h>
#include <gui/scene_manager.h>
#include "../desktop_settings_app.h"
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#include "desktop_settings_scene.h"
#include "desktop_settings_scene_i.h"
#include <desktop/helpers/pin_lock.h>
#define SCENE_EVENT_EXIT (0U)
#define SCENE_EVENT_1ST_PIN_ENTERED (1U)
#define SCENE_EVENT_PINS_EQUAL (2U)
#define SCENE_EVENT_PINS_DIFFERENT (3U)
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
if(!app->pincode_buffer_filled) {
app->pincode_buffer = *pin_code;
app->pincode_buffer_filled = true;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_1ST_PIN_ENTERED);
} else {
app->pincode_buffer_filled = false;
if(desktop_pins_are_equal(&app->pincode_buffer, pin_code)) {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_EQUAL);
} else {
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_PINS_DIFFERENT);
}
}
}
static void pin_setup_back_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_EXIT);
}
void desktop_settings_scene_pin_setup_on_enter(void* context) {
DesktopSettingsApp* app = context;
app->pincode_buffer_filled = false;
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, pin_setup_back_callback);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Enter from 4 to 10 arrows:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_setup_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_1ST_PIN_ENTERED:
desktop_view_pin_input_set_label_button(app->pin_input_view, "OK");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 0, 0, NULL);
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 0, 8, "Confirm your PIN:");
desktop_view_pin_input_reset_pin(app->pin_input_view);
desktop_view_pin_input_unlock_input(app->pin_input_view);
consumed = true;
break;
case SCENE_EVENT_PINS_DIFFERENT:
scene_manager_set_scene_state(
app->scene_manager,
DesktopSettingsAppScenePinError,
SCENE_STATE_PIN_ERROR_MISMATCH);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinError);
consumed = true;
break;
case SCENE_EVENT_PINS_EQUAL:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupHowto2);
consumed = true;
break;
case SCENE_EVENT_EXIT: {
uint32_t scene_found;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@@ -0,0 +1,77 @@
#include <furi.h>
#include <notification/notification.h>
#include <notification/notification_messages.h>
#include <stdint.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include "../desktop_settings_app.h"
#include <desktop/desktop_settings.h>
#include <desktop/views/desktop_view_pin_input.h>
#include "desktop_settings_scene.h"
#define SCENE_EVENT_DONE (0U)
static void pin_setup_done_callback(const PinCode* pin_code, void* context) {
furi_assert(pin_code);
furi_assert(context);
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EVENT_DONE);
}
void desktop_settings_scene_pin_setup_done_on_enter(void* context) {
DesktopSettingsApp* app = context;
app->settings.pin_code = app->pincode_buffer;
SAVE_DESKTOP_SETTINGS(&app->settings);
NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION);
notification_message(notification, &sequence_single_vibro);
furi_record_close(RECORD_NOTIFICATION);
desktop_view_pin_input_set_context(app->pin_input_view, app);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, pin_setup_done_callback);
desktop_view_pin_input_set_pin(app->pin_input_view, &app->settings.pin_code);
desktop_view_pin_input_set_label_button(app->pin_input_view, "Done");
desktop_view_pin_input_set_label_primary(app->pin_input_view, 29, 8, "PIN activated!");
desktop_view_pin_input_set_label_secondary(
app->pin_input_view, 7, 45, "Remember or write it down");
desktop_view_pin_input_lock_input(app->pin_input_view);
desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 24);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinInput);
}
bool desktop_settings_scene_pin_setup_done_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_DONE: {
bool scene_found = false;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void desktop_settings_scene_pin_setup_done_on_exit(void* context) {
furi_assert(context);
DesktopSettingsApp* app = context;
desktop_view_pin_input_set_pin_position(app->pin_input_view, 64, 32);
desktop_view_pin_input_set_back_callback(app->pin_input_view, NULL);
desktop_view_pin_input_set_done_callback(app->pin_input_view, NULL);
}

View File

@@ -0,0 +1,45 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <gui/view_dispatcher.h>
#include "desktop_settings_scene.h"
#include "../desktop_settings_app.h"
#include "../views/desktop_settings_view_pin_setup_howto.h"
#define SCENE_EXIT_EVENT (0U)
static void desktop_settings_scene_pin_lock_done_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_settings_scene_pin_setup_howto_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto_set_callback(
app->pin_setup_howto_view, desktop_settings_scene_pin_lock_done_callback, app);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto);
}
bool desktop_settings_scene_pin_setup_howto_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EXIT_EVENT:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetup);
consumed = true;
break;
default:
furi_assert(0);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_howto_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,67 @@
#include <furi.h>
#include <gui/scene_manager.h>
#include <stdint.h>
#include "desktop_settings_scene.h"
#include "../desktop_settings_app.h"
#include "../views/desktop_settings_view_pin_setup_howto2.h"
#define SCENE_EXIT_EVENT (0U)
#define SCENE_DONE_EVENT (1U)
static void desktop_settings_scene_pin_setup_howto2_done_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_DONE_EVENT);
}
static void desktop_settings_scene_pin_setup_howto2_exit_callback(void* context) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, SCENE_EXIT_EVENT);
}
void desktop_settings_scene_pin_setup_howto2_on_enter(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto2_set_context(app->pin_setup_howto2_view, app);
desktop_settings_view_pin_setup_howto2_set_ok_callback(
app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_done_callback);
desktop_settings_view_pin_setup_howto2_set_cancel_callback(
app->pin_setup_howto2_view, desktop_settings_scene_pin_setup_howto2_exit_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewIdPinSetupHowto2);
}
bool desktop_settings_scene_pin_setup_howto2_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_DONE_EVENT: {
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinSetupDone);
consumed = true;
break;
}
case SCENE_EXIT_EVENT: {
bool scene_found = false;
scene_found = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, DesktopSettingsAppScenePinMenu);
if(!scene_found) {
view_dispatcher_stop(app->view_dispatcher);
}
consumed = true;
break;
}
default:
furi_assert(0);
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_pin_setup_howto2_on_exit(void* context) {
DesktopSettingsApp* app = context;
desktop_settings_view_pin_setup_howto2_set_ok_callback(app->pin_setup_howto2_view, NULL);
desktop_settings_view_pin_setup_howto2_set_cancel_callback(app->pin_setup_howto2_view, NULL);
}

View File

@@ -0,0 +1,100 @@
#include <applications.h>
#include <lib/toolbox/value_index.h>
#include "../desktop_settings_app.h"
#include "desktop_settings_scene.h"
#define SCENE_EVENT_SELECT_FAVORITE_PRIMARY 0
#define SCENE_EVENT_SELECT_FAVORITE_SECONDARY 1
#define SCENE_EVENT_SELECT_PIN_SETUP 2
#define SCENE_EVENT_SELECT_AUTO_LOCK_DELAY 3
#define AUTO_LOCK_DELAY_COUNT 6
const char* const auto_lock_delay_text[AUTO_LOCK_DELAY_COUNT] = {
"OFF",
"30s",
"60s",
"2min",
"5min",
"10min",
};
const uint32_t auto_lock_delay_value[AUTO_LOCK_DELAY_COUNT] =
{0, 30000, 60000, 120000, 300000, 600000};
static void desktop_settings_scene_start_var_list_enter_callback(void* context, uint32_t index) {
DesktopSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
static void desktop_settings_scene_start_auto_lock_delay_changed(VariableItem* item) {
DesktopSettingsApp* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, auto_lock_delay_text[index]);
app->settings.auto_lock_delay_ms = auto_lock_delay_value[index];
}
void desktop_settings_scene_start_on_enter(void* context) {
DesktopSettingsApp* app = context;
VariableItemList* variable_item_list = app->variable_item_list;
VariableItem* item;
uint8_t value_index;
variable_item_list_add(variable_item_list, "Primary Favorite App", 1, NULL, NULL);
variable_item_list_add(variable_item_list, "Secondary Favorite App", 1, NULL, NULL);
variable_item_list_add(variable_item_list, "PIN Setup", 1, NULL, NULL);
item = variable_item_list_add(
variable_item_list,
"Auto Lock Time",
AUTO_LOCK_DELAY_COUNT,
desktop_settings_scene_start_auto_lock_delay_changed,
app);
variable_item_list_set_enter_callback(
variable_item_list, desktop_settings_scene_start_var_list_enter_callback, app);
value_index = value_index_uint32(
app->settings.auto_lock_delay_ms, auto_lock_delay_value, AUTO_LOCK_DELAY_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, auto_lock_delay_text[value_index]);
view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewVarItemList);
}
bool desktop_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
DesktopSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case SCENE_EVENT_SELECT_FAVORITE_PRIMARY:
scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 1);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
consumed = true;
break;
case SCENE_EVENT_SELECT_FAVORITE_SECONDARY:
scene_manager_set_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite, 0);
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneFavorite);
consumed = true;
break;
case SCENE_EVENT_SELECT_PIN_SETUP:
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppScenePinMenu);
consumed = true;
break;
case SCENE_EVENT_SELECT_AUTO_LOCK_DELAY:
consumed = true;
break;
}
}
return consumed;
}
void desktop_settings_scene_start_on_exit(void* context) {
DesktopSettingsApp* app = context;
variable_item_list_reset(app->variable_item_list);
SAVE_DESKTOP_SETTINGS(&app->settings);
}

View File

@@ -0,0 +1,78 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "desktop_settings_view_pin_setup_howto.h"
struct DesktopSettingsViewPinSetupHowto {
View* view;
DesktopSettingsViewPinSetupHowtoDoneCallback callback;
void* context;
};
static void desktop_settings_view_pin_setup_howto_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
furi_assert(model);
canvas_draw_icon(canvas, 16, 18, &I_Pin_attention_dpad_29x29);
elements_button_right(canvas, "Next");
canvas_set_font(canvas, FontPrimary);
elements_multiline_text_aligned(canvas, 64, 0, AlignCenter, AlignTop, "Setting up PIN");
canvas_set_font(canvas, FontSecondary);
elements_multiline_text(canvas, 58, 24, "Prepare to use\narrows as\nPIN symbols");
}
static bool desktop_settings_view_pin_setup_howto_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopSettingsViewPinSetupHowto* instance = context;
bool consumed = false;
if((event->key == InputKeyRight) && (event->type == InputTypeShort)) {
instance->callback(instance->context);
consumed = true;
}
return consumed;
}
void desktop_settings_view_pin_setup_howto_set_callback(
DesktopSettingsViewPinSetupHowto* instance,
DesktopSettingsViewPinSetupHowtoDoneCallback callback,
void* context) {
furi_assert(instance);
furi_assert(callback);
instance->callback = callback;
instance->context = context;
}
DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc() {
DesktopSettingsViewPinSetupHowto* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto));
view->view = view_alloc();
view_allocate_model(view->view, ViewModelTypeLockFree, 1);
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto_draw);
view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto_input);
return view;
}
void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,15 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopSettingsViewPinSetupHowto DesktopSettingsViewPinSetupHowto;
typedef void (*DesktopSettingsViewPinSetupHowtoDoneCallback)(void*);
void desktop_settings_view_pin_setup_howto_set_callback(
DesktopSettingsViewPinSetupHowto* instance,
DesktopSettingsViewPinSetupHowtoDoneCallback callback,
void* context);
DesktopSettingsViewPinSetupHowto* desktop_settings_view_pin_setup_howto_alloc();
void desktop_settings_view_pin_setup_howto_free(DesktopSettingsViewPinSetupHowto* instance);
View* desktop_settings_view_pin_setup_howto_get_view(DesktopSettingsViewPinSetupHowto* instance);

View File

@@ -0,0 +1,100 @@
#include <furi.h>
#include <furi_hal.h>
#include <gui/elements.h>
#include <gui/canvas.h>
#include <toolbox/version.h>
#include <assets_icons.h>
#include <dolphin/helpers/dolphin_state.h>
#include <dolphin/dolphin.h>
#include "desktop_settings_view_pin_setup_howto2.h"
struct DesktopSettingsViewPinSetupHowto2 {
View* view;
DesktopSettingsViewPinSetupHowto2Callback cancel_callback;
DesktopSettingsViewPinSetupHowto2Callback ok_callback;
void* context;
};
static void desktop_settings_view_pin_setup_howto2_draw(Canvas* canvas, void* model) {
furi_assert(canvas);
furi_assert(model);
canvas_set_font(canvas, FontSecondary);
elements_multiline_text_aligned(
canvas,
64,
24,
AlignCenter,
AlignCenter,
"Forgotten PIN can only be\n"
"reset with entire device.\n"
"Read docs How to reset PIN.");
elements_button_right(canvas, "OK");
elements_button_left(canvas, "Cancel");
}
static bool desktop_settings_view_pin_setup_howto2_input(InputEvent* event, void* context) {
furi_assert(event);
furi_assert(context);
DesktopSettingsViewPinSetupHowto2* instance = context;
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyRight) {
instance->ok_callback(instance->context);
consumed = true;
} else if(event->key == InputKeyLeft) {
instance->cancel_callback(instance->context);
consumed = true;
}
}
return consumed;
}
void desktop_settings_view_pin_setup_howto2_set_context(
DesktopSettingsViewPinSetupHowto2* instance,
void* context) {
furi_assert(instance);
instance->context = context;
}
void desktop_settings_view_pin_setup_howto2_set_cancel_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback) {
furi_assert(instance);
instance->cancel_callback = callback;
}
void desktop_settings_view_pin_setup_howto2_set_ok_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback) {
furi_assert(instance);
instance->ok_callback = callback;
}
DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc() {
DesktopSettingsViewPinSetupHowto2* view = malloc(sizeof(DesktopSettingsViewPinSetupHowto2));
view->view = view_alloc();
view_allocate_model(view->view, ViewModelTypeLockFree, 1);
view_set_context(view->view, view);
view_set_draw_callback(view->view, desktop_settings_view_pin_setup_howto2_draw);
view_set_input_callback(view->view, desktop_settings_view_pin_setup_howto2_input);
return view;
}
void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance) {
furi_assert(instance);
view_free(instance->view);
free(instance);
}
View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance) {
furi_assert(instance);
return instance->view;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include <gui/view.h>
typedef struct DesktopSettingsViewPinSetupHowto2 DesktopSettingsViewPinSetupHowto2;
typedef void (*DesktopSettingsViewPinSetupHowto2Callback)(void*);
DesktopSettingsViewPinSetupHowto2* desktop_settings_view_pin_setup_howto2_alloc();
void desktop_settings_view_pin_setup_howto2_free(DesktopSettingsViewPinSetupHowto2* instance);
View* desktop_settings_view_pin_setup_howto2_get_view(DesktopSettingsViewPinSetupHowto2* instance);
void desktop_settings_view_pin_setup_howto2_set_context(
DesktopSettingsViewPinSetupHowto2* instance,
void* context);
void desktop_settings_view_pin_setup_howto2_set_cancel_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback);
void desktop_settings_view_pin_setup_howto2_set_ok_callback(
DesktopSettingsViewPinSetupHowto2* instance,
DesktopSettingsViewPinSetupHowto2Callback callback);

View File

@@ -0,0 +1,13 @@
App(
appid="passport",
name="Passport",
apptype=FlipperAppType.SETTINGS,
entry_point="passport_app",
cdefines=["APP_PASSPORT"],
requires=[
"gui",
"dolphin",
],
stack_size=1 * 1024,
order=60,
)

View File

@@ -0,0 +1,115 @@
#include "assets_icons.h"
#include "dolphin/helpers/dolphin_state.h"
#include <core/check.h>
#include <core/record.h>
#include <furi.h>
#include <gui/gui.h>
#include <furi_hal_version.h>
#include "dolphin/dolphin.h"
#include "math.h"
#define MOODS_TOTAL 3
#define BUTTHURT_MAX 3
static const Icon* const portrait_happy[BUTTHURT_MAX] = {
&I_passport_happy1_46x49,
&I_passport_happy2_46x49,
&I_passport_happy3_46x49};
static const Icon* const portrait_ok[BUTTHURT_MAX] = {
&I_passport_okay1_46x49,
&I_passport_okay2_46x49,
&I_passport_okay3_46x49};
static const Icon* const portrait_bad[BUTTHURT_MAX] = {
&I_passport_bad1_46x49,
&I_passport_bad2_46x49,
&I_passport_bad3_46x49};
static const Icon* const* portraits[MOODS_TOTAL] = {portrait_happy, portrait_ok, portrait_bad};
static void input_callback(InputEvent* input, void* ctx) {
FuriSemaphore* semaphore = ctx;
if((input->type == InputTypeShort) && (input->key == InputKeyBack)) {
furi_semaphore_release(semaphore);
}
}
static void render_callback(Canvas* canvas, void* ctx) {
DolphinStats* stats = ctx;
char level_str[20];
char mood_str[32];
uint8_t mood = 0;
if(stats->butthurt <= 4) {
mood = 0;
snprintf(mood_str, 20, "Mood: Happy");
} else if(stats->butthurt <= 9) {
mood = 1;
snprintf(mood_str, 20, "Mood: Ok");
} else {
mood = 2;
snprintf(mood_str, 20, "Mood: Angry");
}
uint32_t xp_progress = 0;
uint32_t xp_to_levelup = dolphin_state_xp_to_levelup(stats->icounter);
uint32_t xp_for_current_level =
xp_to_levelup + dolphin_state_xp_above_last_levelup(stats->icounter);
if(stats->level == 3) {
xp_progress = 0;
} else {
xp_progress = xp_to_levelup * 64 / xp_for_current_level;
}
// multipass
canvas_draw_icon(canvas, 0, 0, &I_passport_left_6x46);
canvas_draw_icon(canvas, 0, 46, &I_passport_bottom_128x18);
canvas_draw_line(canvas, 6, 0, 125, 0);
canvas_draw_line(canvas, 127, 2, 127, 47);
canvas_draw_dot(canvas, 126, 1);
// portrait
furi_assert((stats->level > 0) && (stats->level <= 3));
canvas_draw_icon(canvas, 9, 5, portraits[mood][stats->level - 1]);
canvas_draw_line(canvas, 58, 16, 123, 16);
canvas_draw_line(canvas, 58, 30, 123, 30);
canvas_draw_line(canvas, 58, 44, 123, 44);
const char* my_name = furi_hal_version_get_name_ptr();
snprintf(level_str, 20, "Level: %hu", stats->level);
canvas_draw_str(canvas, 58, 12, my_name ? my_name : "Unknown");
canvas_draw_str(canvas, 58, 26, mood_str);
canvas_draw_str(canvas, 58, 40, level_str);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, 123 - xp_progress, 47, xp_progress + 1, 6);
canvas_set_color(canvas, ColorBlack);
canvas_draw_line(canvas, 123, 47, 123, 52);
}
int32_t passport_app(void* p) {
UNUSED(p);
FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0);
furi_assert(semaphore);
ViewPort* view_port = view_port_alloc();
Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN);
DolphinStats stats = dolphin_stats(dolphin);
furi_record_close(RECORD_DOLPHIN);
view_port_draw_callback_set(view_port, render_callback, &stats);
view_port_input_callback_set(view_port, input_callback, semaphore);
Gui* gui = furi_record_open(RECORD_GUI);
gui_add_view_port(gui, view_port, GuiLayerFullscreen);
view_port_update(view_port);
furi_check(furi_semaphore_acquire(semaphore, FuriWaitForever) == FuriStatusOk);
gui_remove_view_port(gui, view_port);
view_port_free(view_port);
furi_record_close(RECORD_GUI);
furi_semaphore_free(semaphore);
return 0;
}

View File

@@ -0,0 +1,9 @@
App(
appid="notification_settings",
name="LCD and Notifications",
apptype=FlipperAppType.SETTINGS,
entry_point="notification_settings_app",
requires=["notification"],
stack_size=1 * 1024,
order=20,
)

View File

@@ -0,0 +1,199 @@
#include <furi.h>
#include <notification/notification_app.h>
#include <gui/modules/variable_item_list.h>
#include <gui/view_dispatcher.h>
#include <lib/toolbox/value_index.h>
#define MAX_NOTIFICATION_SETTINGS 4
typedef struct {
NotificationApp* notification;
Gui* gui;
ViewDispatcher* view_dispatcher;
VariableItemList* variable_item_list;
} NotificationAppSettings;
static const NotificationSequence sequence_note_c = {
&message_note_c5,
&message_delay_100,
&message_sound_off,
NULL,
};
#define BACKLIGHT_COUNT 5
const char* const backlight_text[BACKLIGHT_COUNT] = {
"0%",
"25%",
"50%",
"75%",
"100%",
};
const float backlight_value[BACKLIGHT_COUNT] = {
0.0f,
0.25f,
0.5f,
0.75f,
1.0f,
};
#define VOLUME_COUNT 5
const char* const volume_text[VOLUME_COUNT] = {
"0%",
"25%",
"50%",
"75%",
"100%",
};
const float volume_value[VOLUME_COUNT] = {0.0f, 0.25f, 0.5f, 0.75f, 1.0f};
#define DELAY_COUNT 6
const char* const delay_text[DELAY_COUNT] = {
"1s",
"5s",
"15s",
"30s",
"60s",
"120s",
};
const uint32_t delay_value[DELAY_COUNT] = {1000, 5000, 15000, 30000, 60000, 120000};
#define VIBRO_COUNT 2
const char* const vibro_text[VIBRO_COUNT] = {
"OFF",
"ON",
};
const bool vibro_value[VIBRO_COUNT] = {false, true};
static void backlight_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, backlight_text[index]);
app->notification->settings.display_brightness = backlight_value[index];
notification_message(app->notification, &sequence_display_backlight_on);
}
static void screen_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, delay_text[index]);
app->notification->settings.display_off_delay_ms = delay_value[index];
notification_message(app->notification, &sequence_display_backlight_on);
}
const NotificationMessage apply_message = {
.type = NotificationMessageTypeLedBrightnessSettingApply,
};
const NotificationSequence apply_sequence = {
&apply_message,
NULL,
};
static void led_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, backlight_text[index]);
app->notification->settings.led_brightness = backlight_value[index];
notification_message(app->notification, &apply_sequence);
notification_internal_message(app->notification, &apply_sequence);
notification_message(app->notification, &sequence_blink_white_100);
}
static void volume_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, volume_text[index]);
app->notification->settings.speaker_volume = volume_value[index];
notification_message(app->notification, &sequence_note_c);
}
static void vibro_changed(VariableItem* item) {
NotificationAppSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, vibro_text[index]);
app->notification->settings.vibro_on = vibro_value[index];
notification_message(app->notification, &sequence_single_vibro);
}
static uint32_t notification_app_settings_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
static NotificationAppSettings* alloc_settings() {
NotificationAppSettings* app = malloc(sizeof(NotificationAppSettings));
app->notification = furi_record_open(RECORD_NOTIFICATION);
app->gui = furi_record_open(RECORD_GUI);
app->variable_item_list = variable_item_list_alloc();
View* view = variable_item_list_get_view(app->variable_item_list);
view_set_previous_callback(view, notification_app_settings_exit);
VariableItem* item;
uint8_t value_index;
item = variable_item_list_add(
app->variable_item_list, "LCD Backlight", BACKLIGHT_COUNT, backlight_changed, app);
value_index = value_index_float(
app->notification->settings.display_brightness, backlight_value, BACKLIGHT_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, backlight_text[value_index]);
item = variable_item_list_add(
app->variable_item_list, "Backlight Time", DELAY_COUNT, screen_changed, app);
value_index = value_index_uint32(
app->notification->settings.display_off_delay_ms, delay_value, DELAY_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, delay_text[value_index]);
item = variable_item_list_add(
app->variable_item_list, "LED Brightness", BACKLIGHT_COUNT, led_changed, app);
value_index = value_index_float(
app->notification->settings.led_brightness, backlight_value, BACKLIGHT_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, backlight_text[value_index]);
item = variable_item_list_add(
app->variable_item_list, "Volume", VOLUME_COUNT, volume_changed, app);
value_index =
value_index_float(app->notification->settings.speaker_volume, volume_value, VOLUME_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, volume_text[value_index]);
item =
variable_item_list_add(app->variable_item_list, "Vibro", VIBRO_COUNT, vibro_changed, app);
value_index = value_index_bool(app->notification->settings.vibro_on, vibro_value, VIBRO_COUNT);
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, vibro_text[value_index]);
app->view_dispatcher = view_dispatcher_alloc();
view_dispatcher_enable_queue(app->view_dispatcher);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
view_dispatcher_add_view(app->view_dispatcher, 0, view);
view_dispatcher_switch_to_view(app->view_dispatcher, 0);
return app;
}
static void free_settings(NotificationAppSettings* app) {
view_dispatcher_remove_view(app->view_dispatcher, 0);
variable_item_list_free(app->variable_item_list);
view_dispatcher_free(app->view_dispatcher);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_NOTIFICATION);
free(app);
}
int32_t notification_settings_app(void* p) {
UNUSED(p);
NotificationAppSettings* app = alloc_settings();
view_dispatcher_run(app->view_dispatcher);
notification_message_save_settings(app->notification);
free_settings(app);
return 0;
}

View File

@@ -0,0 +1,13 @@
App(
appid="power_settings",
name="Power",
apptype=FlipperAppType.SETTINGS,
entry_point="power_settings_app",
requires=[
"gui",
"power",
],
flags=["InsomniaSafe"],
stack_size=1 * 1024,
order=40,
)

View File

@@ -0,0 +1,86 @@
#include "power_settings_app.h"
static bool power_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
PowerSettingsApp* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool power_settings_back_event_callback(void* context) {
furi_assert(context);
PowerSettingsApp* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static void power_settings_tick_event_callback(void* context) {
furi_assert(context);
PowerSettingsApp* app = context;
scene_manager_handle_tick_event(app->scene_manager);
}
PowerSettingsApp* power_settings_app_alloc(uint32_t first_scene) {
PowerSettingsApp* app = malloc(sizeof(PowerSettingsApp));
// Records
app->gui = furi_record_open(RECORD_GUI);
app->power = furi_record_open(RECORD_POWER);
// View dispatcher
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&power_settings_scene_handlers, app);
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, power_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, power_settings_back_event_callback);
view_dispatcher_set_tick_event_callback(
app->view_dispatcher, power_settings_tick_event_callback, 2000);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
// Views
app->batery_info = battery_info_alloc();
view_dispatcher_add_view(
app->view_dispatcher,
PowerSettingsAppViewBatteryInfo,
battery_info_get_view(app->batery_info));
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, PowerSettingsAppViewSubmenu, submenu_get_view(app->submenu));
app->dialog = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher, PowerSettingsAppViewDialog, dialog_ex_get_view(app->dialog));
// Set first scene
scene_manager_next_scene(app->scene_manager, first_scene);
return app;
}
void power_settings_app_free(PowerSettingsApp* app) {
furi_assert(app);
// Views
view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo);
battery_info_free(app->batery_info);
view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewSubmenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, PowerSettingsAppViewDialog);
dialog_ex_free(app->dialog);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
// Records
furi_record_close(RECORD_POWER);
furi_record_close(RECORD_GUI);
free(app);
}
int32_t power_settings_app(void* p) {
uint32_t first_scene = PowerSettingsAppSceneStart;
if(p && strlen(p) && !strcmp(p, "off")) {
first_scene = PowerSettingsAppScenePowerOff;
}
PowerSettingsApp* app = power_settings_app_alloc(first_scene);
view_dispatcher_run(app->view_dispatcher);
power_settings_app_free(app);
return 0;
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <furi.h>
#include <power/power_service/power.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include "views/battery_info.h"
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include "scenes/power_settings_scene.h"
typedef struct {
Power* power;
Gui* gui;
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
BatteryInfo* batery_info;
Submenu* submenu;
DialogEx* dialog;
PowerInfo info;
} PowerSettingsApp;
typedef enum {
PowerSettingsAppViewBatteryInfo,
PowerSettingsAppViewSubmenu,
PowerSettingsAppViewDialog,
} PowerSettingsAppView;

View File

@@ -0,0 +1,30 @@
#include "power_settings_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const power_settings_on_enter_handlers[])(void*) = {
#include "power_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 power_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "power_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 power_settings_on_exit_handlers[])(void* context) = {
#include "power_settings_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers power_settings_scene_handlers = {
.on_enter_handlers = power_settings_on_enter_handlers,
.on_event_handlers = power_settings_on_event_handlers,
.on_exit_handlers = power_settings_on_exit_handlers,
.scene_num = PowerSettingsAppSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) PowerSettingsAppScene##id,
typedef enum {
#include "power_settings_scene_config.h"
PowerSettingsAppSceneNum,
} PowerSettingsAppScene;
#undef ADD_SCENE
extern const SceneManagerHandlers power_settings_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "power_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 "power_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 "power_settings_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,35 @@
#include "../power_settings_app.h"
static void power_settings_scene_battery_info_update_model(PowerSettingsApp* app) {
power_get_info(app->power, &app->info);
BatteryInfoModel battery_info_data = {
.vbus_voltage = app->info.voltage_vbus,
.gauge_voltage = app->info.voltage_gauge,
.gauge_current = app->info.current_gauge,
.gauge_temperature = app->info.temperature_gauge,
.charge = app->info.charge,
.health = app->info.health,
};
battery_info_set_data(app->batery_info, &battery_info_data);
}
void power_settings_scene_battery_info_on_enter(void* context) {
PowerSettingsApp* app = context;
power_settings_scene_battery_info_update_model(app);
view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewBatteryInfo);
}
bool power_settings_scene_battery_info_on_event(void* context, SceneManagerEvent event) {
PowerSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeTick) {
power_settings_scene_battery_info_update_model(app);
consumed = true;
}
return consumed;
}
void power_settings_scene_battery_info_on_exit(void* context) {
UNUSED(context);
}

View File

@@ -0,0 +1,4 @@
ADD_SCENE(power_settings, start, Start)
ADD_SCENE(power_settings, battery_info, BatteryInfo)
ADD_SCENE(power_settings, reboot, Reboot)
ADD_SCENE(power_settings, power_off, PowerOff)

View File

@@ -0,0 +1,46 @@
#include "../power_settings_app.h"
void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) {
furi_assert(context);
PowerSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void power_settings_scene_power_off_on_enter(void* context) {
PowerSettingsApp* app = context;
DialogEx* dialog = app->dialog;
dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog, " I will be\nwaiting for\n you here...", 78, 16, AlignLeft, AlignTop);
dialog_ex_set_icon(dialog, 21, 13, &I_Cry_dolph_55x52);
dialog_ex_set_left_button_text(dialog, "Back");
dialog_ex_set_right_button_text(dialog, "OFF");
dialog_ex_set_result_callback(dialog, power_settings_scene_power_off_dialog_callback);
dialog_ex_set_context(dialog, app);
view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewDialog);
}
bool power_settings_scene_power_off_on_event(void* context, SceneManagerEvent event) {
PowerSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == DialogExResultLeft) {
if(!scene_manager_previous_scene(app->scene_manager)) {
scene_manager_stop(app->scene_manager);
view_dispatcher_stop(app->view_dispatcher);
}
} else if(event.event == DialogExResultRight) {
power_off(app->power);
}
consumed = true;
}
return consumed;
}
void power_settings_scene_power_off_on_exit(void* context) {
PowerSettingsApp* app = context;
dialog_ex_reset(app->dialog);
}

View File

@@ -0,0 +1,53 @@
#include "../power_settings_app.h"
enum PowerSettingsRebootSubmenuIndex {
PowerSettingsRebootSubmenuIndexDfu,
PowerSettingsRebootSubmenuIndexOs,
};
void power_settings_scene_reboot_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
PowerSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void power_settings_scene_reboot_on_enter(void* context) {
PowerSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_set_header(submenu, "Reboot type");
submenu_add_item(
submenu,
"Firmware upgrade",
PowerSettingsRebootSubmenuIndexDfu,
power_settings_scene_reboot_submenu_callback,
app);
submenu_add_item(
submenu,
"Flipper OS",
PowerSettingsRebootSubmenuIndexOs,
power_settings_scene_reboot_submenu_callback,
app);
view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu);
}
bool power_settings_scene_reboot_on_event(void* context, SceneManagerEvent event) {
UNUSED(context);
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PowerSettingsRebootSubmenuIndexDfu) {
power_reboot(PowerBootModeDfu);
} else if(event.event == PowerSettingsRebootSubmenuIndexOs) {
power_reboot(PowerBootModeNormal);
}
consumed = true;
}
return consumed;
}
void power_settings_scene_reboot_on_exit(void* context) {
PowerSettingsApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,64 @@
#include "../power_settings_app.h"
enum PowerSettingsSubmenuIndex {
PowerSettingsSubmenuIndexBatteryInfo,
PowerSettingsSubmenuIndexReboot,
PowerSettingsSubmenuIndexOff,
};
static void power_settings_scene_start_submenu_callback(void* context, uint32_t index) {
furi_assert(context);
PowerSettingsApp* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void power_settings_scene_start_on_enter(void* context) {
PowerSettingsApp* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"Battery Info",
PowerSettingsSubmenuIndexBatteryInfo,
power_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Reboot",
PowerSettingsSubmenuIndexReboot,
power_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Power OFF",
PowerSettingsSubmenuIndexOff,
power_settings_scene_start_submenu_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, PowerSettingsAppSceneStart));
view_dispatcher_switch_to_view(app->view_dispatcher, PowerSettingsAppViewSubmenu);
}
bool power_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
PowerSettingsApp* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == PowerSettingsSubmenuIndexBatteryInfo) {
scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneBatteryInfo);
} else if(event.event == PowerSettingsSubmenuIndexReboot) {
scene_manager_next_scene(app->scene_manager, PowerSettingsAppSceneReboot);
} else if(event.event == PowerSettingsSubmenuIndexOff) {
scene_manager_next_scene(app->scene_manager, PowerSettingsAppScenePowerOff);
}
scene_manager_set_scene_state(app->scene_manager, PowerSettingsAppSceneStart, event.event);
consumed = true;
}
return consumed;
}
void power_settings_scene_start_on_exit(void* context) {
PowerSettingsApp* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,126 @@
#include "battery_info.h"
#include <furi.h>
#include <gui/elements.h>
struct BatteryInfo {
View* view;
};
static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) {
canvas_draw_frame(canvas, x - 7, y + 7, 30, 13);
canvas_draw_icon(canvas, x, y, icon);
canvas_set_color(canvas, ColorWhite);
canvas_draw_box(canvas, x - 4, y + 16, 24, 6);
canvas_set_color(canvas, ColorBlack);
canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val);
};
static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) {
char emote[20] = {};
char header[20] = {};
char value[20] = {};
int32_t drain_current = data->gauge_current * (-1000);
uint32_t charge_current = data->gauge_current * 1000;
// Draw battery
canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28);
if(charge_current > 0) {
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14);
} else if(drain_current > 100) {
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14);
} else if(data->charge < 10) {
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14);
} else {
canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14);
}
// Draw bubble
elements_bubble(canvas, 53, 0, 71, 39);
// Set text
if(charge_current > 0) {
snprintf(emote, sizeof(emote), "%s", "Yummy!");
snprintf(header, sizeof(header), "%s", "Charging at");
snprintf(
value,
sizeof(value),
"%ld.%ldV %ldmA",
(uint32_t)(data->vbus_voltage),
(uint32_t)(data->vbus_voltage * 10) % 10,
charge_current);
} else if(drain_current > 0) {
snprintf(emote, sizeof(emote), "%s", drain_current > 100 ? "Oh no!" : "Om-nom-nom!");
snprintf(header, sizeof(header), "%s", "Consumption is");
snprintf(
value, sizeof(value), "%ld %s", drain_current, drain_current > 100 ? "mA!" : "mA");
} else if(charge_current != 0 || drain_current != 0) {
snprintf(header, 20, "...");
} else {
snprintf(header, sizeof(header), "Charged!");
}
canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote);
canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header);
canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value);
};
static void battery_info_draw_callback(Canvas* canvas, void* context) {
furi_assert(context);
BatteryInfoModel* model = context;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
draw_battery(canvas, model, 0, 5);
char batt_level[10];
char temperature[10];
char voltage[10];
char health[10];
snprintf(batt_level, sizeof(batt_level), "%ld%%", (uint32_t)model->charge);
snprintf(temperature, sizeof(temperature), "%ld C", (uint32_t)model->gauge_temperature);
snprintf(
voltage,
sizeof(voltage),
"%ld.%01ld V",
(uint32_t)model->gauge_voltage,
(uint32_t)(model->gauge_voltage * 10) % 10);
snprintf(health, sizeof(health), "%d%%", model->health);
draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level);
draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature);
draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage);
draw_stat(canvas, 104, 42, &I_Health_16x16, health);
}
BatteryInfo* battery_info_alloc() {
BatteryInfo* battery_info = malloc(sizeof(BatteryInfo));
battery_info->view = view_alloc();
view_set_context(battery_info->view, battery_info);
view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel));
view_set_draw_callback(battery_info->view, battery_info_draw_callback);
return battery_info;
}
void battery_info_free(BatteryInfo* battery_info) {
furi_assert(battery_info);
view_free(battery_info->view);
free(battery_info);
}
View* battery_info_get_view(BatteryInfo* battery_info) {
furi_assert(battery_info);
return battery_info->view;
}
void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) {
furi_assert(battery_info);
furi_assert(data);
with_view_model(
battery_info->view, (BatteryInfoModel * model) {
memcpy(model, data, sizeof(BatteryInfoModel));
return true;
});
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include <gui/view.h>
typedef struct BatteryInfo BatteryInfo;
typedef struct {
float vbus_voltage;
float gauge_voltage;
float gauge_current;
float gauge_temperature;
uint8_t charge;
uint8_t health;
} BatteryInfoModel;
BatteryInfo* battery_info_alloc();
void battery_info_free(BatteryInfo* battery_info);
View* battery_info_get_view(BatteryInfo* battery_info);
void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data);

View File

@@ -0,0 +1,9 @@
App(
appid="storage_settings",
name="Storage",
apptype=FlipperAppType.SETTINGS,
entry_point="storage_settings_app",
requires=["storage"],
stack_size=2 * 1024,
order=30,
)

View File

@@ -0,0 +1,30 @@
#include "storage_settings_scene.h"
// Generate scene on_enter handlers array
#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
void (*const storage_settings_on_enter_handlers[])(void*) = {
#include "storage_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 storage_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = {
#include "storage_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 storage_settings_on_exit_handlers[])(void* context) = {
#include "storage_settings_scene_config.h"
};
#undef ADD_SCENE
// Initialize scene handlers configuration structure
const SceneManagerHandlers storage_settings_scene_handlers = {
.on_enter_handlers = storage_settings_on_enter_handlers,
.on_event_handlers = storage_settings_on_event_handlers,
.on_exit_handlers = storage_settings_on_exit_handlers,
.scene_num = StorageSettingsSceneNum,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// Generate scene id and total number
#define ADD_SCENE(prefix, name, id) StorageSettings##id,
typedef enum {
#include "storage_settings_scene_config.h"
StorageSettingsSceneNum,
} StorageSettingsScene;
#undef ADD_SCENE
extern const SceneManagerHandlers storage_settings_scene_handlers;
// Generate scene on_enter handlers declaration
#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
#include "storage_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 "storage_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 "storage_settings_scene_config.h"
#undef ADD_SCENE

View File

@@ -0,0 +1,163 @@
#include "../storage_settings.h"
#include <furi_hal.h>
#define BENCH_DATA_SIZE 4096
#define BENCH_COUNT 6
#define BENCH_REPEATS 4
#define BENCH_FILE EXT_PATH("rwfiletest.bin")
static void
storage_settings_scene_benchmark_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
static bool storage_settings_scene_bench_write(
Storage* api,
uint16_t size,
const uint8_t* data,
uint32_t* speed) {
File* file = storage_file_alloc(api);
bool result = true;
if(storage_file_open(file, BENCH_FILE, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
uint32_t ticks;
ticks = furi_get_tick();
for(size_t repeat = 0; repeat < BENCH_REPEATS; repeat++) {
for(size_t i = 0; i < BENCH_DATA_SIZE / size; i++) {
if(storage_file_write(file, (data + i * size), size) != size) {
result = false;
break;
}
}
}
ticks = furi_get_tick() - ticks;
*speed = BENCH_DATA_SIZE * furi_kernel_get_tick_frequency() * BENCH_REPEATS;
*speed /= ticks;
*speed /= 1024;
}
storage_file_close(file);
storage_file_free(file);
return result;
}
static bool
storage_settings_scene_bench_read(Storage* api, uint16_t size, uint8_t* data, uint32_t* speed) {
File* file = storage_file_alloc(api);
bool result = true;
*speed = -1;
if(storage_file_open(file, BENCH_FILE, FSAM_READ, FSOM_OPEN_EXISTING)) {
uint32_t ticks;
ticks = furi_get_tick();
for(size_t repeat = 0; repeat < BENCH_REPEATS; repeat++) {
for(size_t i = 0; i < BENCH_DATA_SIZE / size; i++) {
if(storage_file_read(file, (data + i * size), size) != size) {
result = false;
break;
}
}
}
ticks = furi_get_tick() - ticks;
*speed = BENCH_DATA_SIZE * furi_kernel_get_tick_frequency() * BENCH_REPEATS;
*speed /= ticks;
*speed /= 1024;
}
storage_file_close(file);
storage_file_free(file);
return result;
}
static void storage_settings_scene_benchmark(StorageSettings* app) {
DialogEx* dialog_ex = app->dialog_ex;
uint8_t* bench_data;
dialog_ex_set_header(dialog_ex, "Preparing Data...", 64, 32, AlignCenter, AlignCenter);
bench_data = malloc(BENCH_DATA_SIZE);
for(size_t i = 0; i < BENCH_DATA_SIZE; i++) {
bench_data[i] = (uint8_t)i;
}
uint16_t bench_size[BENCH_COUNT] = {1, 8, 32, 256, 512, 1024};
uint32_t bench_w_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0};
uint32_t bench_r_speed[BENCH_COUNT] = {0, 0, 0, 0, 0, 0};
dialog_ex_set_header(dialog_ex, "Benchmarking...", 64, 32, AlignCenter, AlignCenter);
for(size_t i = 0; i < BENCH_COUNT; i++) {
if(!storage_settings_scene_bench_write(
app->fs_api, bench_size[i], bench_data, &bench_w_speed[i]))
break;
if(i > 0) string_cat_printf(app->text_string, "\n");
string_cat_printf(app->text_string, "%ub : W %luK ", bench_size[i], bench_w_speed[i]);
dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter);
if(!storage_settings_scene_bench_read(
app->fs_api, bench_size[i], bench_data, &bench_r_speed[i]))
break;
string_cat_printf(app->text_string, "R %luK", bench_r_speed[i]);
dialog_ex_set_text(
dialog_ex, string_get_cstr(app->text_string), 0, 32, AlignLeft, AlignCenter);
}
free(bench_data);
}
void storage_settings_scene_benchmark_on_enter(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
FS_Error sd_status = storage_sd_status(app->fs_api);
scene_manager_set_scene_state(app->scene_manager, StorageSettingsBenchmark, sd_status);
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_benchmark_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
if(sd_status != FSE_OK) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
dialog_ex_set_center_button_text(dialog_ex, "Ok");
} else {
storage_settings_scene_benchmark(app);
notification_message(app->notification, &sequence_blink_green_100);
}
}
bool storage_settings_scene_benchmark_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
FS_Error sd_status =
scene_manager_get_scene_state(app->scene_manager, StorageSettingsBenchmark);
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
}
} else if(event.type == SceneManagerEventTypeBack && sd_status != FSE_OK) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_benchmark_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
string_reset(app->text_string);
}

View File

@@ -0,0 +1,9 @@
ADD_SCENE(storage_settings, start, Start)
ADD_SCENE(storage_settings, unmount_confirm, UnmountConfirm)
ADD_SCENE(storage_settings, unmounted, Unmounted)
ADD_SCENE(storage_settings, format_confirm, FormatConfirm)
ADD_SCENE(storage_settings, formatting, Formatting)
ADD_SCENE(storage_settings, sd_info, SDInfo)
ADD_SCENE(storage_settings, internal_info, InternalInfo)
ADD_SCENE(storage_settings, benchmark, Benchmark)
ADD_SCENE(storage_settings, factory_reset, FactoryReset)

View File

@@ -0,0 +1,87 @@
#include "../storage_settings.h"
#include <power/power_service/power.h>
#include <furi_hal.h>
#define STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT 5
static void
storage_settings_scene_factory_reset_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_factory_reset_on_enter(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_factory_reset_dialog_callback);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Erase");
dialog_ex_set_header(dialog_ex, "Confirm Factory Reset", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex,
"Internal storage will be erased\r\nData and setting will be lost!",
64,
32,
AlignCenter,
AlignCenter);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_factory_reset_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
uint32_t counter =
scene_manager_get_scene_state(app->scene_manager, StorageSettingsFactoryReset);
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
scene_manager_set_scene_state(app->scene_manager, StorageSettingsFactoryReset, 0);
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultRight:
counter++;
if(counter < STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT) {
string_printf(
app->text_string,
"%ld presses left",
STORAGE_SETTINGS_SCENE_FACTORY_RESET_CONFIRM_COUNT - counter);
dialog_ex_set_text(
app->dialog_ex,
string_get_cstr(app->text_string),
64,
32,
AlignCenter,
AlignCenter);
scene_manager_set_scene_state(
app->scene_manager, StorageSettingsFactoryReset, counter);
} else {
furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset);
power_reboot(PowerBootModeNormal);
}
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_factory_reset_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
string_reset(app->text_string);
}

View File

@@ -0,0 +1,65 @@
#include "../storage_settings.h"
static void
storage_settings_scene_format_confirm_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_format_confirm_on_enter(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status == FSE_NOT_READY) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
dialog_ex_set_center_button_text(dialog_ex, "Ok");
} else {
dialog_ex_set_header(dialog_ex, "Format SD Card?", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(dialog_ex, "All data will be lost!", 64, 32, AlignCenter, AlignCenter);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Format");
}
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(
dialog_ex, storage_settings_scene_format_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_format_confirm_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultCenter:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultRight:
scene_manager_next_scene(app->scene_manager, StorageSettingsFormatting);
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_format_confirm_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
}

View File

@@ -0,0 +1,79 @@
#include "../storage_settings.h"
static const NotificationMessage message_green_165 = {
.type = NotificationMessageTypeLedGreen,
.data.led.value = 165,
};
static const NotificationSequence sequence_set_formatting_leds = {
&message_red_255,
&message_green_165,
&message_blue_0,
&message_do_not_reset,
NULL,
};
static const NotificationSequence sequence_reset_formatting_leds = {
&message_red_0,
&message_green_0,
NULL,
};
static void
storage_settings_scene_formatting_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_formatting_on_enter(void* context) {
StorageSettings* app = context;
FS_Error error;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_set_header(dialog_ex, "Formatting...", 64, 32, AlignCenter, AlignCenter);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
notification_message_block(app->notification, &sequence_set_formatting_leds);
error = storage_sd_format(app->fs_api);
notification_message(app->notification, &sequence_reset_formatting_leds);
notification_message(app->notification, &sequence_blink_green_100);
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_formatting_dialog_callback);
if(error != FSE_OK) {
dialog_ex_set_header(dialog_ex, "Cannot Format SD Card", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);
} else {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "Format\ncomplete!", 14, 15, AlignLeft, AlignTop);
}
dialog_ex_set_center_button_text(dialog_ex, "OK");
}
bool storage_settings_scene_formatting_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_formatting_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
}

View File

@@ -0,0 +1,62 @@
#include "../storage_settings.h"
#include <furi_hal_version.h>
static void
storage_settings_scene_internal_info_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_internal_info_on_enter(void* context) {
StorageSettings* app = context;
uint64_t total_space;
uint64_t free_space;
FS_Error error =
storage_common_fs_info(app->fs_api, STORAGE_INT_PATH_PREFIX, &total_space, &free_space);
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_internal_info_dialog_callback);
if(error != FSE_OK) {
dialog_ex_set_header(
dialog_ex, "Internal Storage Error", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, storage_error_get_desc(error), 64, 32, AlignCenter, AlignCenter);
} else {
string_printf(
app->text_string,
"Label: %s\nType: LittleFS\n%lu KB total\n%lu KB free",
furi_hal_version_get_name_ptr() ? furi_hal_version_get_name_ptr() : "Unknown",
(uint32_t)(total_space / 1024),
(uint32_t)(free_space / 1024));
dialog_ex_set_text(
dialog_ex, string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop);
}
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_internal_info_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
}
}
return consumed;
}
void storage_settings_scene_internal_info_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
string_reset(app->text_string);
}

View File

@@ -0,0 +1,74 @@
#include "../storage_settings.h"
static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_sd_info_on_enter(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
SDInfo sd_info;
FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info);
scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status);
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_sd_info_dialog_callback);
if(sd_status != FSE_OK) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
dialog_ex_set_center_button_text(dialog_ex, "Ok");
} else {
string_printf(
app->text_string,
"Label: %s\nType: %s\n%lu KB total\n%lu KB free",
sd_info.label,
sd_api_get_fs_type_text(sd_info.fs_type),
sd_info.kb_total,
sd_info.kb_free);
dialog_ex_set_text(
dialog_ex, string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop);
}
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_sd_info_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
FS_Error sd_status = scene_manager_get_scene_state(app->scene_manager, StorageSettingsSDInfo);
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultLeft:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultCenter:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultRight:
scene_manager_next_scene(app->scene_manager, StorageSettingsUnmounted);
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack && sd_status != FSE_OK) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_sd_info_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
string_reset(app->text_string);
}

View File

@@ -0,0 +1,119 @@
#include "../storage_settings.h"
enum StorageSettingsStartSubmenuIndex {
StorageSettingsStartSubmenuIndexInternalInfo,
StorageSettingsStartSubmenuIndexSDInfo,
StorageSettingsStartSubmenuIndexUnmount,
StorageSettingsStartSubmenuIndexFormat,
StorageSettingsStartSubmenuIndexBenchy,
StorageSettingsStartSubmenuIndexFactoryReset
};
static void storage_settings_scene_start_submenu_callback(void* context, uint32_t index) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, index);
}
void storage_settings_scene_start_on_enter(void* context) {
StorageSettings* app = context;
Submenu* submenu = app->submenu;
submenu_add_item(
submenu,
"About Internal Storage",
StorageSettingsStartSubmenuIndexInternalInfo,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"About SD Card",
StorageSettingsStartSubmenuIndexSDInfo,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Unmount SD Card",
StorageSettingsStartSubmenuIndexUnmount,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Format SD Card",
StorageSettingsStartSubmenuIndexFormat,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Benchmark SD Card",
StorageSettingsStartSubmenuIndexBenchy,
storage_settings_scene_start_submenu_callback,
app);
submenu_add_item(
submenu,
"Factory Reset",
StorageSettingsStartSubmenuIndexFactoryReset,
storage_settings_scene_start_submenu_callback,
app);
submenu_set_selected_item(
submenu, scene_manager_get_scene_state(app->scene_manager, StorageSettingsStart));
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewSubmenu);
}
bool storage_settings_scene_start_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case StorageSettingsStartSubmenuIndexSDInfo:
scene_manager_set_scene_state(
app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexSDInfo);
scene_manager_next_scene(app->scene_manager, StorageSettingsSDInfo);
consumed = true;
break;
case StorageSettingsStartSubmenuIndexInternalInfo:
scene_manager_set_scene_state(
app->scene_manager,
StorageSettingsStart,
StorageSettingsStartSubmenuIndexInternalInfo);
scene_manager_next_scene(app->scene_manager, StorageSettingsInternalInfo);
consumed = true;
break;
case StorageSettingsStartSubmenuIndexUnmount:
scene_manager_set_scene_state(
app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexUnmount);
scene_manager_next_scene(app->scene_manager, StorageSettingsUnmountConfirm);
consumed = true;
break;
case StorageSettingsStartSubmenuIndexFormat:
scene_manager_set_scene_state(
app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexFormat);
scene_manager_next_scene(app->scene_manager, StorageSettingsFormatConfirm);
consumed = true;
break;
case StorageSettingsStartSubmenuIndexBenchy:
scene_manager_set_scene_state(
app->scene_manager, StorageSettingsStart, StorageSettingsStartSubmenuIndexBenchy);
scene_manager_next_scene(app->scene_manager, StorageSettingsBenchmark);
consumed = true;
break;
case StorageSettingsStartSubmenuIndexFactoryReset:
scene_manager_set_scene_state(
app->scene_manager,
StorageSettingsStart,
StorageSettingsStartSubmenuIndexFactoryReset);
scene_manager_next_scene(app->scene_manager, StorageSettingsFactoryReset);
consumed = true;
break;
}
}
return consumed;
}
void storage_settings_scene_start_on_exit(void* context) {
StorageSettings* app = context;
submenu_reset(app->submenu);
}

View File

@@ -0,0 +1,66 @@
#include "../storage_settings.h"
static void
storage_settings_scene_unmount_confirm_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_unmount_confirm_on_enter(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status == FSE_NOT_READY) {
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
dialog_ex_set_header(dialog_ex, "SD Card Not Mounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(
dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop);
dialog_ex_set_center_button_text(dialog_ex, "Ok");
} else {
dialog_ex_set_header(dialog_ex, "Unmount SD Card?", 64, 10, AlignCenter, AlignCenter);
dialog_ex_set_text(
dialog_ex, "SD card will be\nunavailable", 64, 32, AlignCenter, AlignCenter);
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
dialog_ex_set_right_button_text(dialog_ex, "Unmount");
}
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(
dialog_ex, storage_settings_scene_unmount_confirm_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_unmount_confirm_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultLeft:
consumed = scene_manager_previous_scene(app->scene_manager);
break;
case DialogExResultRight:
scene_manager_next_scene(app->scene_manager, StorageSettingsUnmounted);
consumed = true;
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_unmount_confirm_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
}

View File

@@ -0,0 +1,57 @@
#include "../storage_settings.h"
static void
storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) {
StorageSettings* app = context;
view_dispatcher_send_custom_event(app->view_dispatcher, result);
}
void storage_settings_scene_unmounted_on_enter(void* context) {
StorageSettings* app = context;
FS_Error error = storage_sd_unmount(app->fs_api);
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_set_center_button_text(dialog_ex, "OK");
dialog_ex_set_icon(dialog_ex, 72, 17, &I_DolphinCommon_56x48);
if(error == FSE_OK) {
dialog_ex_set_header(dialog_ex, "SD Card Unmounted", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, "You can remove\nSD card now.", 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_green_100);
} else {
dialog_ex_set_header(dialog_ex, "Cannot Unmount SD Card", 64, 3, AlignCenter, AlignTop);
dialog_ex_set_text(dialog_ex, storage_error_get_desc(error), 3, 22, AlignLeft, AlignTop);
notification_message(app->notification, &sequence_blink_red_100);
}
dialog_ex_set_context(dialog_ex, app);
dialog_ex_set_result_callback(dialog_ex, storage_settings_scene_unmounted_dialog_callback);
view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx);
}
bool storage_settings_scene_unmounted_on_event(void* context, SceneManagerEvent event) {
StorageSettings* app = context;
bool consumed = false;
if(event.type == SceneManagerEventTypeCustom) {
switch(event.event) {
case DialogExResultCenter:
consumed = scene_manager_search_and_switch_to_previous_scene(
app->scene_manager, StorageSettingsStart);
break;
}
} else if(event.type == SceneManagerEventTypeBack) {
consumed = true;
}
return consumed;
}
void storage_settings_scene_unmounted_on_exit(void* context) {
StorageSettings* app = context;
DialogEx* dialog_ex = app->dialog_ex;
dialog_ex_reset(dialog_ex);
}

View File

@@ -0,0 +1,76 @@
#include "storage_settings.h"
static bool storage_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
StorageSettings* app = context;
return scene_manager_handle_custom_event(app->scene_manager, event);
}
static bool storage_settings_back_event_callback(void* context) {
furi_assert(context);
StorageSettings* app = context;
return scene_manager_handle_back_event(app->scene_manager);
}
static StorageSettings* storage_settings_alloc() {
StorageSettings* app = malloc(sizeof(StorageSettings));
app->gui = furi_record_open(RECORD_GUI);
app->fs_api = furi_record_open(RECORD_STORAGE);
app->notification = furi_record_open(RECORD_NOTIFICATION);
app->view_dispatcher = view_dispatcher_alloc();
app->scene_manager = scene_manager_alloc(&storage_settings_scene_handlers, app);
string_init(app->text_string);
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, storage_settings_custom_event_callback);
view_dispatcher_set_navigation_event_callback(
app->view_dispatcher, storage_settings_back_event_callback);
view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
app->submenu = submenu_alloc();
view_dispatcher_add_view(
app->view_dispatcher, StorageSettingsViewSubmenu, submenu_get_view(app->submenu));
app->dialog_ex = dialog_ex_alloc();
view_dispatcher_add_view(
app->view_dispatcher, StorageSettingsViewDialogEx, dialog_ex_get_view(app->dialog_ex));
scene_manager_next_scene(app->scene_manager, StorageSettingsStart);
return app;
}
static void storage_settings_free(StorageSettings* app) {
view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewSubmenu);
submenu_free(app->submenu);
view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewDialogEx);
dialog_ex_free(app->dialog_ex);
view_dispatcher_free(app->view_dispatcher);
scene_manager_free(app->scene_manager);
furi_record_close(RECORD_GUI);
furi_record_close(RECORD_STORAGE);
furi_record_close(RECORD_NOTIFICATION);
string_clear(app->text_string);
free(app);
}
int32_t storage_settings_app(void* p) {
UNUSED(p);
StorageSettings* app = storage_settings_alloc();
view_dispatcher_run(app->view_dispatcher);
storage_settings_free(app);
return 0;
}

View File

@@ -0,0 +1,47 @@
#pragma once
#include <furi.h>
#include <gui/gui.h>
#include <gui/view.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#include <notification/notification_messages.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#include <storage/storage.h>
#include <storage/storage_sd_api.h>
#include "scenes/storage_settings_scene.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
// records
Gui* gui;
NotificationApp* notification;
Storage* fs_api;
// view managment
SceneManager* scene_manager;
ViewDispatcher* view_dispatcher;
// view modules
Submenu* submenu;
DialogEx* dialog_ex;
// text
string_t text_string;
} StorageSettings;
typedef enum {
StorageSettingsViewSubmenu,
StorageSettingsViewDialogEx,
} StorageSettingsView;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,9 @@
App(
appid="system_settings",
name="System",
apptype=FlipperAppType.SETTINGS,
entry_point="system_settings_app",
requires=["gui"],
stack_size=1 * 1024,
order=70,
)

View File

@@ -0,0 +1,112 @@
#include "system_settings.h"
#include <loader/loader.h>
#include <lib/toolbox/value_index.h>
const char* const log_level_text[] = {
"Default",
"None",
"Error",
"Warning",
"Info",
"Debug",
"Trace",
};
const uint32_t log_level_value[] = {
FuriLogLevelDefault,
FuriLogLevelNone,
FuriLogLevelError,
FuriLogLevelWarn,
FuriLogLevelInfo,
FuriLogLevelDebug,
FuriLogLevelTrace,
};
static void log_level_changed(VariableItem* item) {
// SystemSettings* app = variable_item_get_context(item);
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, log_level_text[index]);
furi_hal_rtc_set_log_level(log_level_value[index]);
}
const char* const debug_text[] = {
"OFF",
"ON",
};
static void debug_changed(VariableItem* item) {
uint8_t index = variable_item_get_current_value_index(item);
variable_item_set_current_value_text(item, debug_text[index]);
if(index) {
furi_hal_rtc_set_flag(FuriHalRtcFlagDebug);
} else {
furi_hal_rtc_reset_flag(FuriHalRtcFlagDebug);
}
loader_update_menu();
}
static uint32_t system_settings_exit(void* context) {
UNUSED(context);
return VIEW_NONE;
}
SystemSettings* system_settings_alloc() {
SystemSettings* app = malloc(sizeof(SystemSettings));
// Load settings
app->gui = furi_record_open(RECORD_GUI);
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_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
VariableItem* item;
uint8_t value_index;
app->var_item_list = variable_item_list_alloc();
item = variable_item_list_add(
app->var_item_list, "Log Level", COUNT_OF(log_level_text), log_level_changed, app);
value_index = value_index_uint32(
furi_hal_rtc_get_log_level(), log_level_value, COUNT_OF(log_level_text));
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, log_level_text[value_index]);
item = variable_item_list_add(
app->var_item_list, "Debug", COUNT_OF(debug_text), debug_changed, app);
value_index = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug) ? 1 : 0;
variable_item_set_current_value_index(item, value_index);
variable_item_set_current_value_text(item, debug_text[value_index]);
view_set_previous_callback(
variable_item_list_get_view(app->var_item_list), system_settings_exit);
view_dispatcher_add_view(
app->view_dispatcher,
SystemSettingsViewVarItemList,
variable_item_list_get_view(app->var_item_list));
view_dispatcher_switch_to_view(app->view_dispatcher, SystemSettingsViewVarItemList);
return app;
}
void system_settings_free(SystemSettings* app) {
furi_assert(app);
// Variable item list
view_dispatcher_remove_view(app->view_dispatcher, SystemSettingsViewVarItemList);
variable_item_list_free(app->var_item_list);
// View dispatcher
view_dispatcher_free(app->view_dispatcher);
// Records
furi_record_close(RECORD_GUI);
free(app);
}
int32_t system_settings_app(void* p) {
UNUSED(p);
SystemSettings* app = system_settings_alloc();
view_dispatcher_run(app->view_dispatcher);
system_settings_free(app);
return 0;
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/variable_item_list.h>
typedef struct {
Gui* gui;
ViewDispatcher* view_dispatcher;
VariableItemList* var_item_list;
} SystemSettings;
typedef enum {
SystemSettingsViewVarItemList,
} SystemSettingsView;