diff --git a/applications/main/fap_loader/fap_loader_app.c b/applications/main/fap_loader/fap_loader_app.c index d2f6b1f4..6c909aee 100644 --- a/applications/main/fap_loader/fap_loader_app.c +++ b/applications/main/fap_loader/fap_loader_app.c @@ -182,6 +182,7 @@ int32_t fap_loader_app(void* p) { FapLoader* loader; if(p) { loader = fap_loader_alloc((const char*)p); + view_dispatcher_switch_to_view(loader->view_dispatcher, 0); fap_loader_run_selected_app(loader); } else { loader = fap_loader_alloc(EXT_PATH("apps")); diff --git a/applications/services/desktop/desktop_settings.h b/applications/services/desktop/desktop_settings.h index 3b10fbb1..e502c35f 100644 --- a/applications/services/desktop/desktop_settings.h +++ b/applications/services/desktop/desktop_settings.h @@ -8,7 +8,7 @@ #include #include -#define DESKTOP_SETTINGS_VER (5) +#define DESKTOP_SETTINGS_VER (6) #define DESKTOP_SETTINGS_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) #define DESKTOP_SETTINGS_MAGIC (0x17) @@ -34,6 +34,9 @@ #define MAX_PIN_SIZE 10 #define MIN_PIN_SIZE 4 +#define MAX_APP_LENGTH 128 + +#define FAP_LOADER_APP_NAME "Applications" typedef struct { InputKey data[MAX_PIN_SIZE]; @@ -41,8 +44,13 @@ typedef struct { } PinCode; typedef struct { - uint16_t favorite_primary; - uint16_t favorite_secondary; + bool is_external; + char name_or_path[MAX_APP_LENGTH]; +} FavoriteApp; + +typedef struct { + FavoriteApp favorite_primary; + FavoriteApp favorite_secondary; PinCode pin_code; uint8_t is_locked; uint32_t auto_lock_delay_ms; diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index dc9ac04d..654de44d 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -114,29 +114,39 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { case DesktopMainEventOpenFavoritePrimary: DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.favorite_primary < FLIPPER_APPS_COUNT) { + if(desktop->settings.favorite_primary.is_external) { LoaderStatus status = loader_start( - desktop->loader, FLIPPER_APPS[desktop->settings.favorite_primary].name, NULL); + desktop->loader, + FAP_LOADER_APP_NAME, + desktop->settings.favorite_primary.name_or_path); if(status != LoaderStatusOk) { FURI_LOG_E(TAG, "loader_start failed: %d", status); } } else { - FURI_LOG_E(TAG, "Can't find primary favorite application"); + LoaderStatus status = loader_start( + desktop->loader, desktop->settings.favorite_primary.name_or_path, NULL); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } } consumed = true; break; case DesktopMainEventOpenFavoriteSecondary: DESKTOP_SETTINGS_LOAD(&desktop->settings); - if(desktop->settings.favorite_secondary < FLIPPER_APPS_COUNT) { + if(desktop->settings.favorite_secondary.is_external) { LoaderStatus status = loader_start( desktop->loader, - FLIPPER_APPS[desktop->settings.favorite_secondary].name, - NULL); + FAP_LOADER_APP_NAME, + desktop->settings.favorite_secondary.name_or_path); if(status != LoaderStatusOk) { FURI_LOG_E(TAG, "loader_start failed: %d", status); } } else { - FURI_LOG_E(TAG, "Can't find secondary favorite application"); + LoaderStatus status = loader_start( + desktop->loader, desktop->settings.favorite_secondary.name_or_path, NULL); + if(status != LoaderStatusOk) { + FURI_LOG_E(TAG, "loader_start failed: %d", status); + } } consumed = true; break; @@ -166,7 +176,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { } case DesktopMainEventOpenGameMenu: { LoaderStatus status = loader_start( - desktop->loader, "Applications", EXT_PATH("/apps/Games/snake_game.fap")); + desktop->loader, FAP_LOADER_APP_NAME, EXT_PATH("/apps/Games/snake_game.fap")); if(status != LoaderStatusOk) { FURI_LOG_E(TAG, "loader_start failed: %d", status); } diff --git a/applications/settings/desktop_settings/desktop_settings_app.c b/applications/settings/desktop_settings/desktop_settings_app.c index a2ed8b72..afb5d59e 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.c +++ b/applications/settings/desktop_settings/desktop_settings_app.c @@ -22,6 +22,7 @@ DesktopSettingsApp* desktop_settings_app_alloc() { DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); app->gui = furi_record_open(RECORD_GUI); + app->dialogs = furi_record_open(RECORD_DIALOGS); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); view_dispatcher_enable_queue(app->view_dispatcher); @@ -83,6 +84,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); // Records + furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_GUI); free(app); } diff --git a/applications/settings/desktop_settings/desktop_settings_app.h b/applications/settings/desktop_settings/desktop_settings_app.h index 25c5b3ad..fc56c325 100644 --- a/applications/settings/desktop_settings/desktop_settings_app.h +++ b/applications/settings/desktop_settings/desktop_settings_app.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,7 @@ typedef struct { DesktopSettings settings; Gui* gui; + DialogsApp* dialogs; SceneManager* scene_manager; ViewDispatcher* view_dispatcher; VariableItemList* variable_item_list; diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c index d8c579b3..07ba9925 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c @@ -1,6 +1,28 @@ #include "../desktop_settings_app.h" #include "applications.h" #include "desktop_settings_scene.h" +#include +#include +#include + +static bool favorite_fap_selector_item_callback( + FuriString* file_path, + void* context, + uint8_t** icon_ptr, + FuriString* item_name) { + UNUSED(context); + Storage* storage = furi_record_open(RECORD_STORAGE); + bool success = fap_loader_load_name_and_icon(file_path, storage, icon_ptr, item_name); + furi_record_close(RECORD_STORAGE); + return success; +} + +static bool favorite_fap_selector_file_exists(char* file_path) { + Storage* storage = furi_record_open(RECORD_STORAGE); + bool exists = storage_file_exists(storage, file_path); + furi_record_close(RECORD_STORAGE); + return exists; +} static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { DesktopSettingsApp* app = context; @@ -12,6 +34,10 @@ void desktop_settings_scene_favorite_on_enter(void* context) { Submenu* submenu = app->submenu; submenu_reset(submenu); + uint32_t primary_favorite = + scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + uint32_t pre_select_item = 0; + for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { submenu_add_item( submenu, @@ -19,38 +45,96 @@ void desktop_settings_scene_favorite_on_enter(void* context) { i, desktop_settings_scene_favorite_submenu_callback, app); - } - uint32_t primary_favorite = - scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); + if(primary_favorite) { // Select favorite item in submenu + if((app->settings.favorite_primary.is_external && + !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || + (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_primary.name_or_path))) { + pre_select_item = i; + } + } else { + if((app->settings.favorite_secondary.is_external && + !strcmp(FLIPPER_APPS[i].name, FAP_LOADER_APP_NAME)) || + (!strcmp(FLIPPER_APPS[i].name, app->settings.favorite_secondary.name_or_path))) { + pre_select_item = i; + } + } + } submenu_set_header( - app->submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); + submenu, primary_favorite ? "Primary favorite app:" : "Secondary favorite app:"); + submenu_set_selected_item(submenu, pre_select_item); // If set during loop, visual glitch. - 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; + FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); 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; + if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME)) { + if(primary_favorite) { + app->settings.favorite_primary.is_external = false; + strncpy( + app->settings.favorite_primary.name_or_path, + FLIPPER_APPS[event.event].name, + MAX_APP_LENGTH); + } else { + app->settings.favorite_secondary.is_external = false; + strncpy( + app->settings.favorite_secondary.name_or_path, + FLIPPER_APPS[event.event].name, + MAX_APP_LENGTH); + } } else { - app->settings.favorite_secondary = event.event; + const DialogsFileBrowserOptions browser_options = { + .extension = ".fap", + .icon = &I_unknown_10px, + .skip_assets = true, + .hide_ext = true, + .item_loader_callback = favorite_fap_selector_item_callback, + .item_loader_context = app, + }; + + if(primary_favorite) { // Select favorite fap in file browser + if(favorite_fap_selector_file_exists( + app->settings.favorite_primary.name_or_path)) { + furi_string_set_str(temp_path, app->settings.favorite_primary.name_or_path); + } + } else { + if(favorite_fap_selector_file_exists( + app->settings.favorite_secondary.name_or_path)) { + furi_string_set_str(temp_path, app->settings.favorite_secondary.name_or_path); + } + } + + submenu_reset(app->submenu); + if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) { + if(primary_favorite) { + app->settings.favorite_primary.is_external = true; + strncpy( + app->settings.favorite_primary.name_or_path, + furi_string_get_cstr(temp_path), + MAX_APP_LENGTH); + } else { + app->settings.favorite_secondary.is_external = true; + strncpy( + app->settings.favorite_secondary.name_or_path, + furi_string_get_cstr(temp_path), + MAX_APP_LENGTH); + } + } } scene_manager_previous_scene(app->scene_manager); consumed = true; } + + furi_string_free(temp_path); return consumed; }