#include #include #include #include #include #include #include "loader.h" #include "loader_menu.h" #include "loader_extmainapp.h" #define TAG "LoaderMenu" struct LoaderMenu { FuriThread* thread; bool settings; void (*closed_cb)(void*); void* context; }; static int32_t loader_menu_thread(void* p); LoaderMenu* loader_menu_alloc(void (*closed_cb)(void*), void* context, bool settings) { LoaderMenu* loader_menu = malloc(sizeof(LoaderMenu)); loader_menu->closed_cb = closed_cb; loader_menu->context = context; loader_menu->settings = settings; loader_menu->thread = furi_thread_alloc_ex(TAG, 1024, loader_menu_thread, loader_menu); furi_thread_start(loader_menu->thread); return loader_menu; } void loader_menu_free(LoaderMenu* loader_menu) { furi_assert(loader_menu); furi_thread_join(loader_menu->thread); furi_thread_free(loader_menu->thread); free(loader_menu); } typedef enum { LoaderMenuViewPrimary, LoaderMenuViewSettings, } LoaderMenuView; typedef struct { Gui* gui; ViewDispatcher* view_dispatcher; Menu* primary_menu; Submenu* settings_menu; bool settings; } LoaderMenuApp; static void loader_menu_start(const char* name) { Loader* loader = furi_record_open(RECORD_LOADER); loader_start_with_gui_error(loader, name, NULL); furi_record_close(RECORD_LOADER); } static void loader_menu_callback(void* context, uint32_t index) { UNUSED(context); 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) { UNUSED(index); UNUSED(context); const char* name = LOADER_APPLICATIONS_NAME; loader_menu_start(name); } static void loader_menu_settings_menu_callback(void* context, uint32_t index) { UNUSED(context); const char* name = FLIPPER_SETTINGS_APPS[index].name; loader_menu_start(name); } static void loader_menu_switch_to_settings(void* context, uint32_t index) { UNUSED(index); LoaderMenuApp* app = context; view_dispatcher_switch_to_view(app->view_dispatcher, LoaderMenuViewSettings); } static uint32_t loader_menu_switch_to_primary(void* context) { UNUSED(context); return LoaderMenuViewPrimary; } static uint32_t loader_menu_exit(void* context) { UNUSED(context); return VIEW_NONE; } static void loader_menu_build_menu(LoaderMenuApp* app, LoaderMenu* menu) { menu_add_item( app->primary_menu, LOADER_APPLICATIONS_NAME, &A_Plugins_14, 0, loader_menu_applications_callback, (void*)menu); for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { menu_add_item( app->primary_menu, FLIPPER_APPS[i].name, FLIPPER_APPS[i].icon, (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) { for(size_t i = 0; i < FLIPPER_SETTINGS_APPS_COUNT; i++) { submenu_add_item( app->settings_menu, FLIPPER_SETTINGS_APPS[i].name, i, loader_menu_settings_menu_callback, loader_menu); } } static LoaderMenuApp* loader_menu_app_alloc(LoaderMenu* loader_menu) { LoaderMenuApp* app = malloc(sizeof(LoaderMenuApp)); app->gui = furi_record_open(RECORD_GUI); app->view_dispatcher = view_dispatcher_alloc(); app->settings = loader_menu->settings; // Primary menu if(!app->settings) { app->primary_menu = menu_alloc(); loader_menu_build_menu(app, loader_menu); View* primary_view = menu_get_view(app->primary_menu); view_set_context(primary_view, app->primary_menu); view_set_previous_callback(primary_view, loader_menu_exit); view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewPrimary, primary_view); } // Settings menu app->settings_menu = submenu_alloc(); loader_menu_build_submenu(app, loader_menu); View* settings_view = submenu_get_view(app->settings_menu); view_set_context(settings_view, app->settings_menu); view_set_previous_callback( settings_view, app->settings ? loader_menu_exit : loader_menu_switch_to_primary); view_dispatcher_add_view(app->view_dispatcher, LoaderMenuViewSettings, settings_view); view_dispatcher_enable_queue(app->view_dispatcher); view_dispatcher_switch_to_view( app->view_dispatcher, app->settings ? LoaderMenuViewSettings : LoaderMenuViewPrimary); return app; } static void loader_menu_app_free(LoaderMenuApp* app) { if(!app->settings) { view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewPrimary); menu_free(app->primary_menu); } view_dispatcher_remove_view(app->view_dispatcher, LoaderMenuViewSettings); submenu_free(app->settings_menu); view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_GUI); free(app); } static int32_t loader_menu_thread(void* p) { LoaderMenu* loader_menu = p; furi_assert(loader_menu); LoaderMenuApp* app = loader_menu_app_alloc(loader_menu); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); view_dispatcher_run(app->view_dispatcher); if(loader_menu->closed_cb) { loader_menu->closed_cb(loader_menu->context); } loader_menu_app_free(app); return 0; }