Desktop: Set external apps as favorites (#1816)
* MVP * Display app name and icon in browser * Pre-select favorites in submenu and browser * Show animation while external favorite is loading * A little birdie told me... (Missing record_close) * DesktopSettings: reset submenu before running dialog Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									55f8beef9f
								
							
						
					
					
						commit
						d5b239595f
					
				| @ -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")); | ||||
|  | ||||
| @ -8,7 +8,7 @@ | ||||
| #include <toolbox/saved_struct.h> | ||||
| #include <storage/storage.h> | ||||
| 
 | ||||
| #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; | ||||
|  | ||||
| @ -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); | ||||
|             } | ||||
|  | ||||
| @ -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); | ||||
| } | ||||
|  | ||||
| @ -6,6 +6,7 @@ | ||||
| #include <gui/scene_manager.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <gui/modules/variable_item_list.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| 
 | ||||
| #include <desktop/desktop_settings.h> | ||||
| #include <desktop/views/desktop_view_pin_input.h> | ||||
| @ -25,6 +26,7 @@ typedef struct { | ||||
|     DesktopSettings settings; | ||||
| 
 | ||||
|     Gui* gui; | ||||
|     DialogsApp* dialogs; | ||||
|     SceneManager* scene_manager; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     VariableItemList* variable_item_list; | ||||
|  | ||||
| @ -1,6 +1,28 @@ | ||||
| #include "../desktop_settings_app.h" | ||||
| #include "applications.h" | ||||
| #include "desktop_settings_scene.h" | ||||
| #include <storage/storage.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| #include <fap_loader/fap_loader_app.h> | ||||
| 
 | ||||
| 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; | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 David
						David