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; |     FapLoader* loader; | ||||||
|     if(p) { |     if(p) { | ||||||
|         loader = fap_loader_alloc((const char*)p); |         loader = fap_loader_alloc((const char*)p); | ||||||
|  |         view_dispatcher_switch_to_view(loader->view_dispatcher, 0); | ||||||
|         fap_loader_run_selected_app(loader); |         fap_loader_run_selected_app(loader); | ||||||
|     } else { |     } else { | ||||||
|         loader = fap_loader_alloc(EXT_PATH("apps")); |         loader = fap_loader_alloc(EXT_PATH("apps")); | ||||||
|  | |||||||
| @ -8,7 +8,7 @@ | |||||||
| #include <toolbox/saved_struct.h> | #include <toolbox/saved_struct.h> | ||||||
| #include <storage/storage.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_PATH INT_PATH(DESKTOP_SETTINGS_FILE_NAME) | ||||||
| #define DESKTOP_SETTINGS_MAGIC (0x17) | #define DESKTOP_SETTINGS_MAGIC (0x17) | ||||||
| @ -34,6 +34,9 @@ | |||||||
| 
 | 
 | ||||||
| #define MAX_PIN_SIZE 10 | #define MAX_PIN_SIZE 10 | ||||||
| #define MIN_PIN_SIZE 4 | #define MIN_PIN_SIZE 4 | ||||||
|  | #define MAX_APP_LENGTH 128 | ||||||
|  | 
 | ||||||
|  | #define FAP_LOADER_APP_NAME "Applications" | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     InputKey data[MAX_PIN_SIZE]; |     InputKey data[MAX_PIN_SIZE]; | ||||||
| @ -41,8 +44,13 @@ typedef struct { | |||||||
| } PinCode; | } PinCode; | ||||||
| 
 | 
 | ||||||
| typedef struct { | typedef struct { | ||||||
|     uint16_t favorite_primary; |     bool is_external; | ||||||
|     uint16_t favorite_secondary; |     char name_or_path[MAX_APP_LENGTH]; | ||||||
|  | } FavoriteApp; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     FavoriteApp favorite_primary; | ||||||
|  |     FavoriteApp favorite_secondary; | ||||||
|     PinCode pin_code; |     PinCode pin_code; | ||||||
|     uint8_t is_locked; |     uint8_t is_locked; | ||||||
|     uint32_t auto_lock_delay_ms; |     uint32_t auto_lock_delay_ms; | ||||||
|  | |||||||
| @ -114,29 +114,39 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
| 
 | 
 | ||||||
|         case DesktopMainEventOpenFavoritePrimary: |         case DesktopMainEventOpenFavoritePrimary: | ||||||
|             DESKTOP_SETTINGS_LOAD(&desktop->settings); |             DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||||
|             if(desktop->settings.favorite_primary < FLIPPER_APPS_COUNT) { |             if(desktop->settings.favorite_primary.is_external) { | ||||||
|                 LoaderStatus status = loader_start( |                 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) { |                 if(status != LoaderStatusOk) { | ||||||
|                     FURI_LOG_E(TAG, "loader_start failed: %d", status); |                     FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||||
|                 } |                 } | ||||||
|             } else { |             } 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; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|         case DesktopMainEventOpenFavoriteSecondary: |         case DesktopMainEventOpenFavoriteSecondary: | ||||||
|             DESKTOP_SETTINGS_LOAD(&desktop->settings); |             DESKTOP_SETTINGS_LOAD(&desktop->settings); | ||||||
|             if(desktop->settings.favorite_secondary < FLIPPER_APPS_COUNT) { |             if(desktop->settings.favorite_secondary.is_external) { | ||||||
|                 LoaderStatus status = loader_start( |                 LoaderStatus status = loader_start( | ||||||
|                     desktop->loader, |                     desktop->loader, | ||||||
|                     FLIPPER_APPS[desktop->settings.favorite_secondary].name, |                     FAP_LOADER_APP_NAME, | ||||||
|                     NULL); |                     desktop->settings.favorite_secondary.name_or_path); | ||||||
|                 if(status != LoaderStatusOk) { |                 if(status != LoaderStatusOk) { | ||||||
|                     FURI_LOG_E(TAG, "loader_start failed: %d", status); |                     FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||||
|                 } |                 } | ||||||
|             } else { |             } 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; |             consumed = true; | ||||||
|             break; |             break; | ||||||
| @ -166,7 +176,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|         } |         } | ||||||
|         case DesktopMainEventOpenGameMenu: { |         case DesktopMainEventOpenGameMenu: { | ||||||
|             LoaderStatus status = loader_start( |             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) { |             if(status != LoaderStatusOk) { | ||||||
|                 FURI_LOG_E(TAG, "loader_start failed: %d", status); |                 FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||||
|             } |             } | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ DesktopSettingsApp* desktop_settings_app_alloc() { | |||||||
|     DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); |     DesktopSettingsApp* app = malloc(sizeof(DesktopSettingsApp)); | ||||||
| 
 | 
 | ||||||
|     app->gui = furi_record_open(RECORD_GUI); |     app->gui = furi_record_open(RECORD_GUI); | ||||||
|  |     app->dialogs = furi_record_open(RECORD_DIALOGS); | ||||||
|     app->view_dispatcher = view_dispatcher_alloc(); |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|     app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); |     app->scene_manager = scene_manager_alloc(&desktop_settings_scene_handlers, app); | ||||||
|     view_dispatcher_enable_queue(app->view_dispatcher); |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
| @ -83,6 +84,7 @@ void desktop_settings_app_free(DesktopSettingsApp* app) { | |||||||
|     view_dispatcher_free(app->view_dispatcher); |     view_dispatcher_free(app->view_dispatcher); | ||||||
|     scene_manager_free(app->scene_manager); |     scene_manager_free(app->scene_manager); | ||||||
|     // Records
 |     // Records
 | ||||||
|  |     furi_record_close(RECORD_DIALOGS); | ||||||
|     furi_record_close(RECORD_GUI); |     furi_record_close(RECORD_GUI); | ||||||
|     free(app); |     free(app); | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ | |||||||
| #include <gui/scene_manager.h> | #include <gui/scene_manager.h> | ||||||
| #include <gui/modules/submenu.h> | #include <gui/modules/submenu.h> | ||||||
| #include <gui/modules/variable_item_list.h> | #include <gui/modules/variable_item_list.h> | ||||||
|  | #include <dialogs/dialogs.h> | ||||||
| 
 | 
 | ||||||
| #include <desktop/desktop_settings.h> | #include <desktop/desktop_settings.h> | ||||||
| #include <desktop/views/desktop_view_pin_input.h> | #include <desktop/views/desktop_view_pin_input.h> | ||||||
| @ -25,6 +26,7 @@ typedef struct { | |||||||
|     DesktopSettings settings; |     DesktopSettings settings; | ||||||
| 
 | 
 | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
|  |     DialogsApp* dialogs; | ||||||
|     SceneManager* scene_manager; |     SceneManager* scene_manager; | ||||||
|     ViewDispatcher* view_dispatcher; |     ViewDispatcher* view_dispatcher; | ||||||
|     VariableItemList* variable_item_list; |     VariableItemList* variable_item_list; | ||||||
|  | |||||||
| @ -1,6 +1,28 @@ | |||||||
| #include "../desktop_settings_app.h" | #include "../desktop_settings_app.h" | ||||||
| #include "applications.h" | #include "applications.h" | ||||||
| #include "desktop_settings_scene.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) { | static void desktop_settings_scene_favorite_submenu_callback(void* context, uint32_t index) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
| @ -12,6 +34,10 @@ void desktop_settings_scene_favorite_on_enter(void* context) { | |||||||
|     Submenu* submenu = app->submenu; |     Submenu* submenu = app->submenu; | ||||||
|     submenu_reset(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++) { |     for(size_t i = 0; i < FLIPPER_APPS_COUNT; i++) { | ||||||
|         submenu_add_item( |         submenu_add_item( | ||||||
|             submenu, |             submenu, | ||||||
| @ -19,38 +45,96 @@ void desktop_settings_scene_favorite_on_enter(void* context) { | |||||||
|             i, |             i, | ||||||
|             desktop_settings_scene_favorite_submenu_callback, |             desktop_settings_scene_favorite_submenu_callback, | ||||||
|             app); |             app); | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     uint32_t primary_favorite = |         if(primary_favorite) { // Select favorite item in submenu
 | ||||||
|         scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); |             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( |     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); |     view_dispatcher_switch_to_view(app->view_dispatcher, DesktopSettingsAppViewMenu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { | bool desktop_settings_scene_favorite_on_event(void* context, SceneManagerEvent event) { | ||||||
|     DesktopSettingsApp* app = context; |     DesktopSettingsApp* app = context; | ||||||
|     bool consumed = false; |     bool consumed = false; | ||||||
|  |     FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps")); | ||||||
| 
 | 
 | ||||||
|     uint32_t primary_favorite = |     uint32_t primary_favorite = | ||||||
|         scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); |         scene_manager_get_scene_state(app->scene_manager, DesktopSettingsAppSceneFavorite); | ||||||
| 
 | 
 | ||||||
|     if(event.type == SceneManagerEventTypeCustom) { |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME)) { | ||||||
|             if(primary_favorite) { |             if(primary_favorite) { | ||||||
|             app->settings.favorite_primary = event.event; |                 app->settings.favorite_primary.is_external = false; | ||||||
|  |                 strncpy( | ||||||
|  |                     app->settings.favorite_primary.name_or_path, | ||||||
|  |                     FLIPPER_APPS[event.event].name, | ||||||
|  |                     MAX_APP_LENGTH); | ||||||
|             } else { |             } else { | ||||||
|             app->settings.favorite_secondary = event.event; |                 app->settings.favorite_secondary.is_external = false; | ||||||
|  |                 strncpy( | ||||||
|  |                     app->settings.favorite_secondary.name_or_path, | ||||||
|  |                     FLIPPER_APPS[event.event].name, | ||||||
|  |                     MAX_APP_LENGTH); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             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); |         scene_manager_previous_scene(app->scene_manager); | ||||||
|         consumed = true; |         consumed = true; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_free(temp_path); | ||||||
|     return consumed; |     return consumed; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 David
						David