Merge remote-tracking branch 'OFW/dev' into dev

This commit is contained in:
MX
2025-04-07 18:01:37 +03:00
20 changed files with 412 additions and 144 deletions

View File

@@ -1,8 +1,9 @@
#include "archive_apps.h"
#include "archive_browser.h"
static const char* known_apps[] = {
static const char* const known_apps[] = {
[ArchiveAppTypeU2f] = "u2f",
[ArchiveAppTypeSetting] = "setting",
};
ArchiveAppTypeEnum archive_get_app_type(const char* path) {
@@ -36,6 +37,8 @@ bool archive_app_is_available(void* context, const char* path) {
furi_record_close(RECORD_STORAGE);
return file_exists;
} else if(app == ArchiveAppTypeSetting) {
return true;
} else {
return false;
}
@@ -53,6 +56,9 @@ bool archive_app_read_dir(void* context, const char* path) {
if(app == ArchiveAppTypeU2f) {
archive_add_app_item(browser, "/app:u2f/U2F Token");
return true;
} else if(app == ArchiveAppTypeSetting) {
archive_add_app_item(browser, path);
return true;
} else {
return false;
}
@@ -75,6 +81,8 @@ void archive_app_delete_file(void* context, const char* path) {
if(archive_is_favorite("/app:u2f/U2F Token")) {
archive_favorites_delete("/app:u2f/U2F Token");
}
} else if(app == ArchiveAppTypeSetting) {
// can't delete a setting!
}
if(res) {

View File

@@ -4,12 +4,14 @@
typedef enum {
ArchiveAppTypeU2f,
ArchiveAppTypeSetting,
ArchiveAppTypeUnknown,
ArchiveAppsTotal,
} ArchiveAppTypeEnum;
static const ArchiveFileTypeEnum app_file_types[] = {
[ArchiveAppTypeU2f] = ArchiveFileTypeU2f,
[ArchiveAppTypeSetting] = ArchiveFileTypeSetting,
[ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown,
};

View File

@@ -7,7 +7,7 @@
#define TAB_DEFAULT ArchiveTabFavorites // Start tab
#define FILE_LIST_BUF_LEN 50
static const char* tab_default_paths[] = {
static const char* const tab_default_paths[] = {
[ArchiveTabFavorites] = "/app:favorites",
[ArchiveTabIButton] = EXT_PATH("ibutton"),
[ArchiveTabNFC] = EXT_PATH("nfc"),
@@ -22,7 +22,7 @@ static const char* tab_default_paths[] = {
[ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX,
};
static const char* known_ext[] = {
static const char* const known_ext[] = {
[ArchiveFileTypeIButton] = ".ibtn",
[ArchiveFileTypeNFC] = ".nfc",
[ArchiveFileTypeSubGhz] = ".sub",
@@ -37,6 +37,7 @@ static const char* known_ext[] = {
[ArchiveFileTypeFolder] = "?",
[ArchiveFileTypeUnknown] = "*",
[ArchiveFileTypeAppOrJs] = ".fap|.js",
[ArchiveFileTypeSetting] = "?",
};
static const ArchiveFileTypeEnum known_type[] = {

View File

@@ -1,9 +1,10 @@
#include "archive_favorites.h"
#include "archive_files.h"
#include "archive_apps.h"
#include "archive_browser.h"
#include <dialogs/dialogs.h>
#define ARCHIVE_FAV_FILE_BUF_LEN 32
static bool archive_favorites_read_line(File* file, FuriString* str_result) {
@@ -337,3 +338,46 @@ void archive_favorites_save(void* context) {
storage_file_free(file);
furi_record_close(RECORD_STORAGE);
}
void archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting) {
DialogMessage* message = dialog_message_alloc();
FuriString* setting_path = furi_string_alloc_set_str(app_name);
if(setting) {
furi_string_push_back(setting_path, '/');
furi_string_cat_str(setting_path, setting);
}
const char* setting_path_str = furi_string_get_cstr(setting_path);
bool is_favorite = archive_is_favorite("/app:setting/%s", setting_path_str);
dialog_message_set_header(
message,
is_favorite ? "Unpin This Setting?" : "Pin This Setting?",
64,
0,
AlignCenter,
AlignTop);
dialog_message_set_text(
message,
is_favorite ? "It will no longer be\naccessible from the\nFavorites menu" :
"It will be accessible from the\nFavorites menu",
64,
32,
AlignCenter,
AlignCenter);
dialog_message_set_buttons(
message, is_favorite ? "Unpin" : "Go back", NULL, is_favorite ? "Keep pinned" : "Pin");
DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
DialogMessageButton button = dialog_message_show(dialogs, message);
furi_record_close(RECORD_DIALOGS);
if(is_favorite && button == DialogMessageButtonLeft) {
archive_favorites_delete("/app:setting/%s", setting_path_str);
} else if(!is_favorite && button == DialogMessageButtonRight) {
archive_file_append(ARCHIVE_FAV_PATH, "/app:setting/%s\n", setting_path_str);
}
furi_string_free(setting_path);
dialog_message_free(message);
}

View File

@@ -12,3 +12,13 @@ bool archive_is_favorite(const char* format, ...) _ATTRIBUTE((__format__(__print
bool archive_favorites_rename(const char* src, const char* dst);
void archive_add_to_favorites(const char* file_path);
void archive_favorites_save(void* context);
/**
* Intended to be called by settings apps to handle long presses, as well as
* internally from within the archive
*
* @param app_name name of the referring application
* @param setting name of the setting, which will be both displayed to the user
* and passed to the application as an argument upon recall
*/
void archive_favorites_handle_setting_pin_unpin(const char* app_name, const char* setting);

View File

@@ -23,6 +23,7 @@ typedef enum {
ArchiveFileTypeFolder,
ArchiveFileTypeUnknown,
ArchiveFileTypeAppOrJs,
ArchiveFileTypeSetting,
ArchiveFileTypeLoading,
} ArchiveFileTypeEnum;

View File

@@ -54,20 +54,37 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec
UNUSED(browser);
Loader* loader = furi_record_open(RECORD_LOADER);
const char* app_name = archive_get_flipper_app_name(selected->type);
if(app_name) {
if(selected->is_app) {
char* param = strrchr(furi_string_get_cstr(selected->path), '/');
if(param != NULL) {
param++;
}
loader_start_with_gui_error(loader, app_name, param);
if(selected->type == ArchiveFileTypeSetting) {
FuriString* app_name = furi_string_alloc_set(selected->path);
furi_string_right(app_name, furi_string_search_char(app_name, '/', 1) + 1);
size_t slash = furi_string_search_char(app_name, '/', 1);
if(slash != FURI_STRING_FAILURE) {
furi_string_left(app_name, slash);
FuriString* app_args =
furi_string_alloc_set_str(furi_string_get_cstr(app_name) + slash + 1);
loader_start_with_gui_error(
loader, furi_string_get_cstr(app_name), furi_string_get_cstr(app_args));
furi_string_free(app_args);
} else {
loader_start_with_gui_error(loader, app_name, furi_string_get_cstr(selected->path));
loader_start_with_gui_error(loader, furi_string_get_cstr(app_name), NULL);
}
furi_string_free(app_name);
} else {
loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL);
const char* app_name = archive_get_flipper_app_name(selected->type);
if(app_name) {
if(selected->is_app) {
char* param = strrchr(furi_string_get_cstr(selected->path), '/');
if(param != NULL) {
param++;
}
loader_start_with_gui_error(loader, app_name, param);
} else {
loader_start_with_gui_error(
loader, app_name, furi_string_get_cstr(selected->path));
}
} else {
loader_start_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL);
}
}
furi_record_close(RECORD_LOADER);

View File

@@ -33,6 +33,7 @@ static const Icon* ArchiveItemIcons[] = {
[ArchiveFileTypeBadUsb] = &I_badusb_10px,
[ArchiveFileTypeU2f] = &I_u2f_10px,
[ArchiveFileTypeApplication] = &I_Apps_10px,
[ArchiveFileTypeSetting] = &I_settings_10px,
[ArchiveFileTypeUpdateManifest] = &I_update_10px,
[ArchiveFileTypeFolder] = &I_dir_10px,
[ArchiveFileTypeUnknown] = &I_unknown_10px,

View File

@@ -13,8 +13,12 @@ struct Submenu {
typedef struct {
FuriString* label;
uint32_t index;
SubmenuItemCallback callback;
union {
SubmenuItemCallback callback;
SubmenuItemCallbackEx callback_ex;
};
void* callback_context;
bool has_extended_events;
bool locked;
FuriString* locked_message;
} SubmenuItem;
@@ -70,7 +74,7 @@ typedef struct {
static void submenu_process_up(Submenu* submenu);
static void submenu_process_down(Submenu* submenu);
static void submenu_process_ok(Submenu* submenu);
static void submenu_process_ok(Submenu* submenu, InputType input_type);
static size_t submenu_items_on_screen(bool header, bool vertical) {
size_t res = (vertical) ? 8 : 4;
@@ -196,6 +200,9 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
with_view_model(
submenu->view, SubmenuModel * model, { model->locked_message_visible = false; }, true);
consumed = true;
} else if(event->key == InputKeyOk) {
consumed = true;
submenu_process_ok(submenu, event->type);
} else if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyUp:
@@ -206,10 +213,6 @@ static bool submenu_view_input_callback(InputEvent* event, void* context) {
consumed = true;
submenu_process_down(submenu);
break;
case InputKeyOk:
consumed = true;
submenu_process_ok(submenu);
break;
default:
break;
}
@@ -310,6 +313,7 @@ void submenu_add_lockable_item(
item->index = index;
item->callback = callback;
item->callback_context = callback_context;
item->has_extended_events = false;
item->locked = locked;
if(locked) {
furi_string_set_str(item->locked_message, locked_message);
@@ -318,6 +322,30 @@ void submenu_add_lockable_item(
true);
}
void submenu_add_item_ex(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallbackEx callback,
void* callback_context) {
SubmenuItem* item = NULL;
furi_check(label);
furi_check(submenu);
with_view_model(
submenu->view,
SubmenuModel * model,
{
item = SubmenuItemArray_push_new(model->items);
furi_string_set_str(item->label, label);
item->index = index;
item->callback_ex = callback;
item->callback_context = callback_context;
item->has_extended_events = true;
},
true);
}
void submenu_change_item_label(Submenu* submenu, uint32_t index, const char* label) {
furi_check(submenu);
furi_check(label);
@@ -465,7 +493,7 @@ void submenu_process_down(Submenu* submenu) {
true);
}
void submenu_process_ok(Submenu* submenu) {
void submenu_process_ok(Submenu* submenu, InputType input_type) {
SubmenuItem* item = NULL;
with_view_model(
@@ -483,8 +511,15 @@ void submenu_process_ok(Submenu* submenu) {
},
true);
if(item && !item->locked && item->callback) {
if(!item) return;
if(item->locked) {
return;
}
if(!item->has_extended_events && input_type == InputTypeShort && item->callback) {
item->callback(item->callback_context, item->index);
} else if(item->has_extended_events && item->callback_ex) {
item->callback_ex(item->callback_context, input_type, item->index);
}
}

View File

@@ -14,6 +14,7 @@ extern "C" {
/** Submenu anonymous structure */
typedef struct Submenu Submenu;
typedef void (*SubmenuItemCallback)(void* context, uint32_t index);
typedef void (*SubmenuItemCallbackEx)(void* context, InputType input_type, uint32_t index);
/** Allocate and initialize submenu
*
@@ -73,6 +74,22 @@ void submenu_add_lockable_item(
bool locked,
const char* locked_message);
/** Add item to submenu with extended press events
*
* @param submenu Submenu instance
* @param label menu item label
* @param index menu item index, used for callback, may be
* the same with other items
* @param callback menu item extended callback
* @param callback_context menu item callback context
*/
void submenu_add_item_ex(
Submenu* submenu,
const char* label,
uint32_t index,
SubmenuItemCallbackEx callback,
void* callback_context);
/** Change label of an existing item
*
* @param submenu Submenu instance

View File

@@ -4,6 +4,7 @@
#include <gui/modules/submenu.h>
#include <assets_icons.h>
#include <applications.h>
#include <archive/helpers/archive_favorites.h>
#include "loader.h"
#include "loader_menu.h"
@@ -71,10 +72,16 @@ static void loader_menu_applications_callback(void* context, uint32_t index) {
loader_menu_start(name);
}
static void loader_menu_settings_menu_callback(void* context, uint32_t index) {
static void
loader_menu_settings_menu_callback(void* context, InputType input_type, uint32_t index) {
UNUSED(context);
const char* name = FLIPPER_SETTINGS_APPS[index].name;
loader_menu_start(name);
if(input_type == InputTypeShort) {
const char* name = FLIPPER_SETTINGS_APPS[index].name;
loader_menu_start(name);
} else if(input_type == InputTypeLong) {
const char* name = FLIPPER_SETTINGS_APPS[index].name;
archive_favorites_handle_setting_pin_unpin(name, NULL);
}
}
static void loader_menu_switch_to_settings(void* context, uint32_t index) {
@@ -130,7 +137,7 @@ static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) {
static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) {
for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) {
submenu_add_item(
submenu_add_item_ex(
app->settings_menu,
FLIPPER_SETTINGS_APPS[i].name,
i,

View File

@@ -1,131 +1,20 @@
#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);
FS_Error sd_status = storage_sd_status(app->fs_api);
if(sd_status != FSE_OK) {
submenu_add_item(
submenu,
"Mount SD Card",
StorageSettingsStartSubmenuIndexUnmount,
storage_settings_scene_start_submenu_callback,
app);
} else {
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);
app->helper_descriptor->options[STORAGE_SETTINGS_MOUNT_INDEX].name =
(sd_status != FSE_OK) ? "Mount SD Card" : "Unmount SD Card";
submenu_settings_helpers_scene_enter(app->settings_helper);
}
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, StorageSettingsBenchmarkConfirm);
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;
return submenu_settings_helpers_scene_event(app->settings_helper, event);
}
void storage_settings_scene_start_on_exit(void* context) {
StorageSettings* app = context;
submenu_reset(app->submenu);
submenu_settings_helpers_scene_exit(app->settings_helper);
}

View File

@@ -1,5 +1,19 @@
#include "storage_settings.h"
const SubmenuSettingsHelperDescriptor descriptor_template = {
.app_name = "Storage",
.options_cnt = 6,
.options =
{
{.name = "About Internal Storage", .scene_id = StorageSettingsSDInfo},
{.name = "About SD Card", .scene_id = StorageSettingsInternalInfo},
{.name = "Unmount SD Card", .scene_id = StorageSettingsUnmountConfirm},
{.name = "Format SD Card", .scene_id = StorageSettingsFormatConfirm},
{.name = "Benchmark SD Card", .scene_id = StorageSettingsBenchmarkConfirm},
{.name = "Factory Reset", .scene_id = StorageSettingsFactoryReset},
},
};
static bool storage_settings_custom_event_callback(void* context, uint32_t event) {
furi_assert(context);
StorageSettings* app = context;
@@ -40,12 +54,27 @@ static StorageSettings* storage_settings_alloc(void) {
view_dispatcher_add_view(
app->view_dispatcher, StorageSettingsViewDialogEx, dialog_ex_get_view(app->dialog_ex));
scene_manager_next_scene(app->scene_manager, StorageSettingsStart);
size_t descriptor_size =
sizeof(SubmenuSettingsHelperDescriptor) +
(descriptor_template.options_cnt * sizeof(SubmenuSettingsHelperOption));
app->helper_descriptor = malloc(descriptor_size);
memcpy(app->helper_descriptor, &descriptor_template, descriptor_size);
app->settings_helper = submenu_settings_helpers_alloc(app->helper_descriptor);
submenu_settings_helpers_assign_objects(
app->settings_helper,
app->view_dispatcher,
app->scene_manager,
app->submenu,
StorageSettingsViewSubmenu,
StorageSettingsStart);
return app;
}
static void storage_settings_free(StorageSettings* app) {
submenu_settings_helpers_free(app->settings_helper);
free(app->helper_descriptor);
view_dispatcher_remove_view(app->view_dispatcher, StorageSettingsViewSubmenu);
submenu_free(app->submenu);
@@ -68,6 +97,10 @@ int32_t storage_settings_app(void* p) {
UNUSED(p);
StorageSettings* app = storage_settings_alloc();
if(!submenu_settings_helpers_app_start(app->settings_helper, p)) {
scene_manager_next_scene(app->scene_manager, StorageSettingsStart);
}
view_dispatcher_run(app->view_dispatcher);
storage_settings_free(app);

View File

@@ -16,6 +16,10 @@
#include "scenes/storage_settings_scene.h"
#include <settings_helpers/submenu_based.h>
#define STORAGE_SETTINGS_MOUNT_INDEX 2
#ifdef __cplusplus
extern "C" {
#endif
@@ -36,6 +40,10 @@ typedef struct {
// text
FuriString* text_string;
// helpers
SubmenuSettingsHelperDescriptor* helper_descriptor;
SubmenuSettingsHelper* settings_helper;
} StorageSettings;
typedef enum {

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View File

@@ -76,6 +76,7 @@ FIRMWARE_APPS = {
"radio_device_cc1101_ext",
"unit_tests",
"js_app",
"archive",
],
}

View File

@@ -0,0 +1,103 @@
#include "submenu_based.h"
#include <archive/helpers/archive_favorites.h>
struct SubmenuSettingsHelper {
const SubmenuSettingsHelperDescriptor* descriptor;
ViewDispatcher* view_dispatcher;
SceneManager* scene_manager;
Submenu* submenu;
uint32_t submenu_view_id;
uint32_t main_scene_id;
};
SubmenuSettingsHelper*
submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor) {
furi_check(descriptor);
SubmenuSettingsHelper* helper = malloc(sizeof(SubmenuSettingsHelper));
helper->descriptor = descriptor;
return helper;
}
void submenu_settings_helpers_assign_objects(
SubmenuSettingsHelper* helper,
ViewDispatcher* view_dispatcher,
SceneManager* scene_manager,
Submenu* submenu,
uint32_t submenu_view_id,
uint32_t main_scene_id) {
furi_check(helper);
furi_check(view_dispatcher);
furi_check(scene_manager);
furi_check(submenu);
helper->view_dispatcher = view_dispatcher;
helper->scene_manager = scene_manager;
helper->submenu = submenu;
helper->submenu_view_id = submenu_view_id;
helper->main_scene_id = main_scene_id;
}
void submenu_settings_helpers_free(SubmenuSettingsHelper* helper) {
free(helper);
}
bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg) {
furi_check(helper);
if(!arg) return false;
const char* option = arg;
for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {
if(strcmp(helper->descriptor->options[i].name, option) == 0) {
scene_manager_next_scene(
helper->scene_manager, helper->descriptor->options[i].scene_id);
return true;
}
}
return false;
}
static void
submenu_settings_helpers_callback(void* context, InputType input_type, uint32_t index) {
SubmenuSettingsHelper* helper = context;
if(input_type == InputTypeShort) {
view_dispatcher_send_custom_event(helper->view_dispatcher, index);
} else if(input_type == InputTypeLong) {
archive_favorites_handle_setting_pin_unpin(
helper->descriptor->app_name, helper->descriptor->options[index].name);
}
}
void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper) {
furi_check(helper);
for(size_t i = 0; i < helper->descriptor->options_cnt; i++) {
submenu_add_item_ex(
helper->submenu,
helper->descriptor->options[i].name,
i,
submenu_settings_helpers_callback,
helper);
}
submenu_set_selected_item(
helper->submenu,
scene_manager_get_scene_state(helper->scene_manager, helper->main_scene_id));
view_dispatcher_switch_to_view(helper->view_dispatcher, helper->submenu_view_id);
}
bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event) {
furi_check(helper);
if(event.type == SceneManagerEventTypeCustom) {
scene_manager_next_scene(
helper->scene_manager, helper->descriptor->options[event.event].scene_id);
scene_manager_set_scene_state(helper->scene_manager, helper->main_scene_id, event.event);
return true;
}
return false;
}
void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper) {
furi_check(helper);
submenu_reset(helper->submenu);
}

View File

@@ -0,0 +1,89 @@
#include <gui/modules/submenu.h>
#include <gui/view_dispatcher.h>
#include <gui/scene_manager.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*SubmenuSettingsHelpherCallback)(void* context, uint32_t index);
typedef struct {
const char* name;
uint32_t scene_id;
} SubmenuSettingsHelperOption;
typedef struct {
const char* app_name;
size_t options_cnt;
SubmenuSettingsHelperOption options[];
} SubmenuSettingsHelperDescriptor;
typedef struct SubmenuSettingsHelper SubmenuSettingsHelper;
/**
* @brief Allocates a submenu-based settings helper
* @param descriptor settings descriptor
*/
SubmenuSettingsHelper*
submenu_settings_helpers_alloc(const SubmenuSettingsHelperDescriptor* descriptor);
/**
* @brief Assigns dynamic objects to the submenu-based settings helper
* @param helper helper object
* @param view_dispatcher ViewDispatcher
* @param scene_manager SceneManager
* @param submenu Submenu
* @param submenu_view_id Submenu view id in the ViewDispatcher
* @param main_scene_id Main scene id in the SceneManager
*/
void submenu_settings_helpers_assign_objects(
SubmenuSettingsHelper* helper,
ViewDispatcher* view_dispatcher,
SceneManager* scene_manager,
Submenu* submenu,
uint32_t submenu_view_id,
uint32_t main_scene_id);
/**
* @brief Frees a submenu-based settings helper
* @param helper helper object
*/
void submenu_settings_helpers_free(SubmenuSettingsHelper* helper);
/**
* @brief App start callback for the submenu-based settings helper
*
* If an argument containing one of the options was provided, launches the
* corresponding scene.
*
* @param helper helper object
* @param arg app argument, may be NULL
* @returns true if a setting name was provided in the argument, false if normal
* app operation shall commence
*/
bool submenu_settings_helpers_app_start(SubmenuSettingsHelper* helper, void* arg);
/**
* @brief Main scene enter callback for the submenu-based settings helper
* @param helper helper object
*/
void submenu_settings_helpers_scene_enter(SubmenuSettingsHelper* helper);
/**
* @brief Main scene event callback for the submenu-based settings helper
* @param helper helper object
* @param event event data
* @returns true if the event was consumed, false otherwise
*/
bool submenu_settings_helpers_scene_event(SubmenuSettingsHelper* helper, SceneManagerEvent event);
/**
* @brief Main scene exit callback for the submenu-based settings helper
* @param helper helper object
*/
void submenu_settings_helpers_scene_exit(SubmenuSettingsHelper* helper);
#ifdef __cplusplus
}
#endif

View File

@@ -2728,6 +2728,7 @@ Function,-,strverscmp,int,"const char*, const char*"
Function,-,strxfrm,size_t,"char*, const char*, size_t"
Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t"
Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*"
Function,+,submenu_add_item_ex,void,"Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*"
Function,+,submenu_alloc,Submenu*,
Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*"
Function,+,submenu_free,void,Submenu*
1 entry status name type params
2728 Function - strxfrm size_t char*, const char*, size_t
2729 Function - strxfrm_l size_t char*, const char*, size_t, locale_t
2730 Function + submenu_add_item void Submenu*, const char*, uint32_t, SubmenuItemCallback, void*
2731 Function + submenu_add_item_ex void Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*
2732 Function + submenu_alloc Submenu*
2733 Function + submenu_change_item_label void Submenu*, uint32_t, const char*
2734 Function + submenu_free void Submenu*

View File

@@ -3664,6 +3664,7 @@ Function,+,subghz_worker_start,void,SubGhzWorker*
Function,+,subghz_worker_stop,void,SubGhzWorker*
Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*"
Function,+,submenu_add_lockable_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*, _Bool, const char*"
Function,+,submenu_add_item_ex,void,"Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*"
Function,+,submenu_alloc,Submenu*,
Function,+,submenu_change_item_label,void,"Submenu*, uint32_t, const char*"
Function,+,submenu_free,void,Submenu*
1 entry status name type params
3664 Function + subghz_worker_stop void SubGhzWorker*
3665 Function + submenu_add_item void Submenu*, const char*, uint32_t, SubmenuItemCallback, void*
3666 Function + submenu_add_lockable_item void Submenu*, const char*, uint32_t, SubmenuItemCallback, void*, _Bool, const char*
3667 Function + submenu_add_item_ex void Submenu*, const char*, uint32_t, SubmenuItemCallbackEx, void*
3668 Function + submenu_alloc Submenu*
3669 Function + submenu_change_item_label void Submenu*, uint32_t, const char*
3670 Function + submenu_free void Submenu*