Przeglądaj źródła

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>
David 3 lat temu
rodzic
commit
d5b239595f

+ 1 - 0
applications/main/fap_loader/fap_loader_app.c

@@ -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"));

+ 11 - 3
applications/services/desktop/desktop_settings.h

@@ -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;
-    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;
     PinCode pin_code;
     uint8_t is_locked;
     uint8_t is_locked;
     uint32_t auto_lock_delay_ms;
     uint32_t auto_lock_delay_ms;

+ 18 - 8
applications/services/desktop/scenes/desktop_scene_main.c

@@ -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,
-                    NULL);
+                    FAP_LOADER_APP_NAME,
+                    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);
             }
             }

+ 2 - 0
applications/settings/desktop_settings/desktop_settings_app.c

@@ -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);
 }
 }

+ 2 - 0
applications/settings/desktop_settings/desktop_settings_app.h

@@ -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;

+ 96 - 12
applications/settings/desktop_settings/scenes/desktop_settings_scene_favorite.c

@@ -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 =
-        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(
     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(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 {
         } 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);
         scene_manager_previous_scene(app->scene_manager);
         consumed = true;
         consumed = true;
     }
     }
+
+    furi_string_free(temp_path);
     return consumed;
     return consumed;
 }
 }