diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 07102eb0e..2e4649002 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -3,11 +3,14 @@ #include #include #include +#include #include #include #include #include +#include +#include #define TAG "Loader" #define LOADER_MAGIC_THREAD_VALUE 0xDEADBEEF @@ -102,6 +105,11 @@ FuriPubSub* loader_get_pubsub(Loader* loader) { return loader->pubsub; } +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader) { + furi_assert(loader); + return &loader->ext_main_apps; +} + // callbacks static void loader_menu_closed_callback(void* context) { @@ -136,6 +144,28 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con // implementation +bool loader_menu_load_fap_meta( + Storage* storage, + FuriString* path, + FuriString* name, + const Icon** icon) { + *icon = NULL; + uint8_t* icon_buf = malloc(CUSTOM_ICON_MAX_SIZE); + if(!flipper_application_load_name_and_icon(path, storage, &icon_buf, name)) { + free(icon_buf); + icon_buf = NULL; + return false; + } + *icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN((*icon)->frame_count, 1); + FURI_CONST_ASSIGN((*icon)->frame_rate, 0); + FURI_CONST_ASSIGN((*icon)->width, 10); + FURI_CONST_ASSIGN((*icon)->height, 10); + FURI_CONST_ASSIGN_PTR((*icon)->frames, malloc(sizeof(const uint8_t*))); + FURI_CONST_ASSIGN_PTR((*icon)->frames[0], icon_buf); + return true; +} + static Loader* loader_alloc() { Loader* loader = malloc(sizeof(Loader)); loader->pubsub = furi_pubsub_alloc(); @@ -146,6 +176,33 @@ static Loader* loader_alloc() { loader->app.thread = NULL; loader->app.insomniac = false; loader->app.fap = NULL; + ExtMainAppList_init(loader->ext_main_apps); + + if(furi_hal_is_normal_boot()) { + Storage* storage = furi_record_open(RECORD_STORAGE); + FuriString* path = furi_string_alloc(); + FuriString* name = furi_string_alloc(); + Stream* stream = file_stream_alloc(storage); + if(file_stream_open(stream, XTREME_APPS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + while(stream_read_line(stream, path)) { + furi_string_replace_all(path, "\r", ""); + furi_string_replace_all(path, "\n", ""); + const Icon* icon; + if(!loader_menu_load_fap_meta(storage, path, name, &icon)) continue; + ExtMainAppList_push_back( + loader->ext_main_apps, + (ExtMainApp){ + .name = strdup(furi_string_get_cstr(name)), + .path = strdup(furi_string_get_cstr(path)), + .icon = icon}); + } + } + file_stream_close(stream); + stream_free(stream); + furi_string_free(name); + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + } return loader; } diff --git a/applications/services/loader/loader_extmainapp.h b/applications/services/loader/loader_extmainapp.h new file mode 100644 index 000000000..4eeb263c7 --- /dev/null +++ b/applications/services/loader/loader_extmainapp.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +typedef struct { + const char* name; + const char* path; + const Icon* icon; +} ExtMainApp; + +LIST_DEF(ExtMainAppList, ExtMainApp, M_POD_OPLIST) + +ExtMainAppList_t* loader_get_ext_main_apps(Loader* loader); diff --git a/applications/services/loader/loader_i.h b/applications/services/loader/loader_i.h index cdf01c33c..cbe01c7f3 100644 --- a/applications/services/loader/loader_i.h +++ b/applications/services/loader/loader_i.h @@ -4,6 +4,7 @@ #include #include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" #include "loader_applications.h" typedef struct { @@ -19,6 +20,7 @@ struct Loader { LoaderMenu* loader_menu; LoaderApplications* loader_applications; LoaderAppData app; + ExtMainAppList_t ext_main_apps; }; typedef enum { diff --git a/applications/services/loader/loader_menu.c b/applications/services/loader/loader_menu.c index 45d3d4c92..de063083c 100644 --- a/applications/services/loader/loader_menu.c +++ b/applications/services/loader/loader_menu.c @@ -7,6 +7,7 @@ #include "loader.h" #include "loader_menu.h" +#include "loader_extmainapp.h" #define TAG "LoaderMenu" @@ -57,8 +58,8 @@ static void loader_menu_start(const char* name) { static void loader_menu_callback(void* context, uint32_t index) { UNUSED(context); - const char* name = FLIPPER_APPS[index].name; - loader_menu_start(name); + const char* name_or_path = (const char*)index; + loader_menu_start(name_or_path); } static void loader_menu_applications_callback(void* context, uint32_t index) { @@ -103,12 +104,25 @@ static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, - i, + (uint32_t)FLIPPER_APPS[i].name, loader_menu_callback, (void*)menu); } menu_add_item( app->primary_menu, "Settings", &A_Settings_14, 0, loader_menu_switch_to_settings, app); + Loader* loader = furi_record_open(RECORD_LOADER); + ExtMainAppList_t* ext_main_apps = loader_get_ext_main_apps(loader); + for(size_t i = 0; i < ExtMainAppList_size(*ext_main_apps); i++) { + const ExtMainApp* ext_main_app = ExtMainAppList_get(*ext_main_apps, i); + menu_add_item( + app->primary_menu, + ext_main_app->name, + ext_main_app->icon, + (uint32_t)ext_main_app->path, + loader_menu_callback, + (void*)menu); + } + furi_record_close(RECORD_LOADER); }; static void loader_menu_build_submenu(LoaderMenuApp* app, LoaderMenu* loader_menu) {