mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-10-10 08:42:32 +02:00
Merge remote-tracking branch 'OFW/dev' into dev
This commit is contained in:
@@ -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) {
|
||||
|
@@ -4,12 +4,14 @@
|
||||
|
||||
typedef enum {
|
||||
ArchiveAppTypeU2f,
|
||||
ArchiveAppTypeSetting,
|
||||
ArchiveAppTypeUnknown,
|
||||
ArchiveAppsTotal,
|
||||
} ArchiveAppTypeEnum;
|
||||
|
||||
static const ArchiveFileTypeEnum app_file_types[] = {
|
||||
[ArchiveAppTypeU2f] = ArchiveFileTypeU2f,
|
||||
[ArchiveAppTypeSetting] = ArchiveFileTypeSetting,
|
||||
[ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown,
|
||||
};
|
||||
|
||||
|
@@ -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[] = {
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -23,6 +23,7 @@ typedef enum {
|
||||
ArchiveFileTypeFolder,
|
||||
ArchiveFileTypeUnknown,
|
||||
ArchiveFileTypeAppOrJs,
|
||||
ArchiveFileTypeSetting,
|
||||
ArchiveFileTypeLoading,
|
||||
} ArchiveFileTypeEnum;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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 {
|
||||
|
BIN
assets/icons/Archive/settings_10px.png
Normal file
BIN
assets/icons/Archive/settings_10px.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 B |
@@ -76,6 +76,7 @@ FIRMWARE_APPS = {
|
||||
"radio_device_cc1101_ext",
|
||||
"unit_tests",
|
||||
"js_app",
|
||||
"archive",
|
||||
],
|
||||
}
|
||||
|
||||
|
103
lib/toolbox/settings_helpers/submenu_based.c
Normal file
103
lib/toolbox/settings_helpers/submenu_based.c
Normal 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);
|
||||
}
|
89
lib/toolbox/settings_helpers/submenu_based.h
Normal file
89
lib/toolbox/settings_helpers/submenu_based.h
Normal 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
|
@@ -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*
|
||||
|
|
@@ -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*
|
||||
|
|
Reference in New Issue
Block a user