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:
David 2022-10-13 13:09:37 -05:00 committed by GitHub
parent 55f8beef9f
commit d5b239595f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 23 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(primary_favorite) { if(strcmp(FLIPPER_APPS[event.event].name, FAP_LOADER_APP_NAME)) {
app->settings.favorite_primary = event.event; 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;
} }