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(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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user