mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Desktop / MNTM Settings: Directories and generic files support for Keybinds / Main Menu (#331)
* feat(Desktop): Directories support for keybinds - Adds *RIGHT* button select in the file browser dialogs and changing the `Open File` action to `Open File/Directory` in `Settings > Desktop > Keybinds Setup`. This adds the ability to open to any directory in the Archive app, in addition to the default behavior of opening a file in it's default app. * line order mixup * Main Menu: Allow adding JS files (or any file) - Normal files and directories are now able to be added to then main menu and are run in their appropriate apps. - e.g. .txt files shown in text viewer, .js files are run in the JS Runner app, and folders are navigated to by the Archive app. All similar to the desktop keybinds functionality. - Icons are also assigned appropriately based on the extensions, though more could probably be added to the `loader_menu_get_ext_icon` function. - Also replaced some of the long arduous is_dir checks and just used the `storage_dir_exists` function since its already there and does the same. * should be checking `ext` for NULL * Move select_right at end of structs for binary compatibility apps may blindly reach into these structs so need to keep the basics in same structure for DialogsFileBrowserOptions this is even in public api and after compilation this would be incompatible with other firmwares even without reaching into private structs * Select menu item / folder for directories too * Move api below too * Keep ofw order here too * Refactor starting archive into desktop, less FuriString passing around * Dont leave main menu when launching archive * Simplify/fix a few things * Handle folders in run_with_default_app() * Update App -> Item naming in MNTM settings * Fix build * Explain pressing right * Update changelog --------- Co-authored-by: WillyJL <me@willyjl.dev>
This commit is contained in:
@@ -8,7 +8,10 @@
|
||||
- UL: Keeloq Comunello add manually support (by @xMasterX)
|
||||
- RFID: Support writing Securakey, Jablotron and FDX-B to EM4305 cards (#434 by @jamisonderek)
|
||||
- BT Remote: Add Rename Option, simplify Bad KB BLE profile (#439 by @aaronjamt & @WillyJL)
|
||||
- MNTM Settings: Add Skip Sliding Animations option for Lockscreen (#436 by @aaronjamt)
|
||||
- MNTM Settings:
|
||||
- Add Main Menu support for directories and generic files (including JS files) (#331 by @956MB & @WillyJL)
|
||||
- Add Skip Sliding Animations option for Lockscreen (#436 by @aaronjamt)
|
||||
- Desktop: Add Keybinds support for directories (#331 by @956MB & @WillyJL)
|
||||
- Input Settings: Add Vibro Trigger option (#429 by @956MB)
|
||||
|
||||
### Updated:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "archive_i.h"
|
||||
#include "helpers/archive_browser.h"
|
||||
|
||||
static bool archive_custom_event_callback(void* context, uint32_t event) {
|
||||
furi_assert(context);
|
||||
@@ -136,11 +137,25 @@ void archive_show_loading_popup(ArchiveApp* context, bool show) {
|
||||
}
|
||||
|
||||
int32_t archive_app(void* p) {
|
||||
UNUSED(p);
|
||||
FuriString* path = (FuriString*)p;
|
||||
|
||||
ArchiveApp* archive = archive_alloc();
|
||||
view_dispatcher_attach_to_gui(
|
||||
archive->view_dispatcher, archive->gui, ViewDispatcherTypeFullscreen);
|
||||
|
||||
// If we are sent a path from context, set it in the browser
|
||||
if(path && !furi_string_empty(path)) {
|
||||
archive_set_tab(archive->browser, ArchiveTabBrowser);
|
||||
furi_string_set(archive->browser->path, path);
|
||||
archive->browser->is_root = false;
|
||||
archive_file_browser_set_path(
|
||||
archive->browser,
|
||||
archive->browser->path,
|
||||
archive_get_tab_ext(ArchiveTabBrowser),
|
||||
false,
|
||||
!momentum_settings.show_hidden_files);
|
||||
}
|
||||
|
||||
scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser);
|
||||
view_dispatcher_run(archive->view_dispatcher);
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ static void archive_long_load_cb(void* context) {
|
||||
browser->view, ArchiveBrowserViewModel * model, { model->folder_loading = true; }, true);
|
||||
}
|
||||
|
||||
static void archive_file_browser_set_path(
|
||||
void archive_file_browser_set_path(
|
||||
ArchiveBrowserView* browser,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
|
||||
@@ -80,6 +80,12 @@ inline bool archive_is_known_app(ArchiveFileTypeEnum type) {
|
||||
return type < ArchiveFileTypeUnknown;
|
||||
}
|
||||
|
||||
void archive_file_browser_set_path(
|
||||
ArchiveBrowserView* browser,
|
||||
FuriString* path,
|
||||
const char* filter_ext,
|
||||
bool skip_assets,
|
||||
bool hide_dot_files);
|
||||
bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx);
|
||||
bool archive_is_file_list_load_required(ArchiveBrowserViewModel* model);
|
||||
void archive_update_offset(ArchiveBrowserView* browser);
|
||||
@@ -104,6 +110,7 @@ void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const ch
|
||||
void archive_show_file_menu(ArchiveBrowserView* browser, bool show, bool manage);
|
||||
void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active);
|
||||
|
||||
void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab);
|
||||
void archive_switch_tab(ArchiveBrowserView* browser, InputKey key);
|
||||
void archive_enter_dir(ArchiveBrowserView* browser, FuriString* name);
|
||||
void archive_leave_dir(ArchiveBrowserView* browser);
|
||||
|
||||
@@ -15,6 +15,8 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
|
||||
file->is_app = is_app;
|
||||
if(is_app) {
|
||||
file->type = archive_get_app_filetype(archive_get_app_type(path));
|
||||
} else if(is_folder) {
|
||||
file->type = ArchiveFileTypeFolder;
|
||||
} else {
|
||||
for(size_t i = 0; i < COUNT_OF(known_ext); i++) {
|
||||
if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue;
|
||||
@@ -53,11 +55,7 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder
|
||||
}
|
||||
}
|
||||
|
||||
if(is_folder) {
|
||||
file->type = ArchiveFileTypeFolder;
|
||||
} else {
|
||||
file->type = ArchiveFileTypeUnknown;
|
||||
}
|
||||
file->type = ArchiveFileTypeUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "../views/archive_browser_view.h"
|
||||
#include "archive/scenes/archive_scene.h"
|
||||
|
||||
#include <desktop/desktop_i.h>
|
||||
|
||||
#define TAG "ArchiveSceneBrowser"
|
||||
|
||||
#define SCENE_STATE_DEFAULT (0)
|
||||
@@ -182,6 +184,12 @@ static void
|
||||
}
|
||||
} else if(selected->type == ArchiveFileTypeApplication) {
|
||||
loader_start_detached_with_gui_error(loader, furi_string_get_cstr(selected->path), NULL);
|
||||
} else if(selected->type == ArchiveFileTypeFolder) {
|
||||
// Folders are handled by archive, so we should only get here with run_with_default_app() outside archive
|
||||
furi_check(browser == NULL, "What you doin?");
|
||||
Desktop* desktop = furi_record_open(RECORD_DESKTOP);
|
||||
desktop_launch_archive(desktop, furi_string_get_cstr(selected->path));
|
||||
furi_record_close(RECORD_DESKTOP);
|
||||
} else {
|
||||
archive_show_file(loader, furi_string_get_cstr(selected->path));
|
||||
}
|
||||
|
||||
@@ -201,22 +201,23 @@ void momentum_app_load_mainmenu_apps(MomentumApp* app) {
|
||||
if(furi_string_start_with(line, "/")) {
|
||||
if(!flipper_application_load_name_and_icon(
|
||||
line, app->storage, &unused_icon, label)) {
|
||||
furi_string_reset(label);
|
||||
const char* end = strrchr(furi_string_get_cstr(line), '/');
|
||||
furi_string_set(label, end ? end + 1 : furi_string_get_cstr(line));
|
||||
}
|
||||
} else {
|
||||
furi_string_reset(label);
|
||||
bool found = false;
|
||||
for(size_t i = 0; !found && i < FLIPPER_APPS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(line), FLIPPER_APPS[i].name)) {
|
||||
furi_string_set(label, FLIPPER_APPS[i].name);
|
||||
found = true;
|
||||
}
|
||||
momentum_app_push_mainmenu_app(app, label, line);
|
||||
continue;
|
||||
}
|
||||
bool found = false;
|
||||
for(size_t i = 0; !found && i < FLIPPER_APPS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(line), FLIPPER_APPS[i].name)) {
|
||||
furi_string_set(label, FLIPPER_APPS[i].name);
|
||||
found = true;
|
||||
}
|
||||
for(size_t i = 0; !found && i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(line), FLIPPER_EXTERNAL_APPS[i].name)) {
|
||||
furi_string_set(label, FLIPPER_EXTERNAL_APPS[i].name);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
for(size_t i = 0; !found && i < FLIPPER_EXTERNAL_APPS_COUNT; i++) {
|
||||
if(!strcmp(furi_string_get_cstr(line), FLIPPER_EXTERNAL_APPS[i].name)) {
|
||||
furi_string_set(label, FLIPPER_EXTERNAL_APPS[i].name);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if(furi_string_empty(label)) {
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
enum VarItemListIndex {
|
||||
VarItemListIndexMenuStyle,
|
||||
VarItemListIndexResetMenu,
|
||||
VarItemListIndexApp,
|
||||
VarItemListIndexAddApp,
|
||||
VarItemListIndexMoveApp,
|
||||
VarItemListIndexRemoveApp,
|
||||
VarItemListIndexItem,
|
||||
VarItemListIndexAddItem,
|
||||
VarItemListIndexMoveItem,
|
||||
VarItemListIndexRemoveItem,
|
||||
};
|
||||
|
||||
void momentum_app_scene_interface_mainmenu_var_item_list_callback(void* context, uint32_t index) {
|
||||
@@ -40,7 +40,7 @@ static void momentum_app_scene_interface_mainmenu_app_changed(VariableItem* item
|
||||
item, *CharList_get(app->mainmenu_app_labels, app->mainmenu_app_index));
|
||||
size_t count = CharList_size(app->mainmenu_app_labels);
|
||||
char label[20];
|
||||
snprintf(label, sizeof(label), "App %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
snprintf(label, sizeof(label), "Item %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
variable_item_set_item_label(item, label);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ static void momentum_app_scene_interface_mainmenu_move_app_changed(VariableItem*
|
||||
CharList_swap_at(app->mainmenu_app_exes, idx, idx - 1);
|
||||
app->mainmenu_app_index--;
|
||||
}
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, VarItemListIndexMoveApp);
|
||||
view_dispatcher_send_custom_event(app->view_dispatcher, VarItemListIndexMoveItem);
|
||||
}
|
||||
variable_item_set_current_value_index(item, 1);
|
||||
}
|
||||
@@ -84,11 +84,11 @@ void momentum_app_scene_interface_mainmenu_on_enter(void* context) {
|
||||
|
||||
size_t count = CharList_size(app->mainmenu_app_labels);
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "App", count, momentum_app_scene_interface_mainmenu_app_changed, app);
|
||||
var_item_list, "Item", count, momentum_app_scene_interface_mainmenu_app_changed, app);
|
||||
if(count) {
|
||||
app->mainmenu_app_index = CLAMP(app->mainmenu_app_index, count - 1, 0U);
|
||||
char label[20];
|
||||
snprintf(label, sizeof(label), "App %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
char label[21];
|
||||
snprintf(label, sizeof(label), "Item %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
variable_item_set_item_label(item, label);
|
||||
variable_item_set_current_value_text(
|
||||
item, *CharList_get(app->mainmenu_app_labels, app->mainmenu_app_index));
|
||||
@@ -98,15 +98,15 @@ void momentum_app_scene_interface_mainmenu_on_enter(void* context) {
|
||||
}
|
||||
variable_item_set_current_value_index(item, app->mainmenu_app_index);
|
||||
|
||||
variable_item_list_add(var_item_list, "Add App", 0, NULL, app);
|
||||
variable_item_list_add(var_item_list, "Add Item", 0, NULL, app);
|
||||
|
||||
item = variable_item_list_add(
|
||||
var_item_list, "Move App", 3, momentum_app_scene_interface_mainmenu_move_app_changed, app);
|
||||
var_item_list, "Move Item", 3, momentum_app_scene_interface_mainmenu_move_app_changed, app);
|
||||
variable_item_set_current_value_text(item, "");
|
||||
variable_item_set_current_value_index(item, 1);
|
||||
variable_item_set_locked(item, count < 2, "Can't move\nwith less\nthan 2 apps!");
|
||||
|
||||
variable_item_list_add(var_item_list, "Remove App", 0, NULL, app);
|
||||
variable_item_list_add(var_item_list, "Remove Item", 0, NULL, app);
|
||||
|
||||
variable_item_list_set_enter_callback(
|
||||
var_item_list, momentum_app_scene_interface_mainmenu_var_item_list_callback, app);
|
||||
@@ -133,7 +133,7 @@ bool momentum_app_scene_interface_mainmenu_on_event(void* context, SceneManagerE
|
||||
case VarItemListIndexResetMenu:
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneInterfaceMainmenuReset);
|
||||
break;
|
||||
case VarItemListIndexRemoveApp:
|
||||
case VarItemListIndexRemoveItem:
|
||||
if(!CharList_size(app->mainmenu_app_labels)) break;
|
||||
if(!CharList_size(app->mainmenu_app_exes)) break;
|
||||
free(*CharList_get(app->mainmenu_app_labels, app->mainmenu_app_index));
|
||||
@@ -143,27 +143,27 @@ bool momentum_app_scene_interface_mainmenu_on_event(void* context, SceneManagerE
|
||||
CharList_remove_v(
|
||||
app->mainmenu_app_exes, app->mainmenu_app_index, app->mainmenu_app_index + 1);
|
||||
/* fall through */
|
||||
case VarItemListIndexMoveApp: {
|
||||
case VarItemListIndexMoveItem: {
|
||||
app->save_mainmenu_apps = true;
|
||||
size_t count = CharList_size(app->mainmenu_app_labels);
|
||||
VariableItem* item = variable_item_list_get(app->var_item_list, VarItemListIndexApp);
|
||||
VariableItem* item = variable_item_list_get(app->var_item_list, VarItemListIndexItem);
|
||||
if(count) {
|
||||
app->mainmenu_app_index = CLAMP(app->mainmenu_app_index, count - 1, 0U);
|
||||
char label[20];
|
||||
snprintf(label, sizeof(label), "App %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
char label[21];
|
||||
snprintf(label, sizeof(label), "Item %u/%u", 1 + app->mainmenu_app_index, count);
|
||||
variable_item_set_item_label(item, label);
|
||||
variable_item_set_current_value_text(
|
||||
item, *CharList_get(app->mainmenu_app_labels, app->mainmenu_app_index));
|
||||
} else {
|
||||
app->mainmenu_app_index = 0;
|
||||
variable_item_set_item_label(item, "App");
|
||||
variable_item_set_item_label(item, "Item");
|
||||
variable_item_set_current_value_text(item, "None");
|
||||
}
|
||||
variable_item_set_current_value_index(item, app->mainmenu_app_index);
|
||||
variable_item_set_values_count(item, count);
|
||||
break;
|
||||
}
|
||||
case VarItemListIndexAddApp:
|
||||
case VarItemListIndexAddItem:
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneInterfaceMainmenuAdd);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
enum SubmenuIndex {
|
||||
SubmenuIndexMainApp,
|
||||
SubmenuIndexExternalApp,
|
||||
SubmenuIndexFileDirectory,
|
||||
};
|
||||
|
||||
static bool fap_selector_item_callback(
|
||||
@@ -26,28 +27,39 @@ static void
|
||||
case SubmenuIndexMainApp:
|
||||
scene_manager_next_scene(app->scene_manager, MomentumAppSceneInterfaceMainmenuAddMain);
|
||||
break;
|
||||
case SubmenuIndexExternalApp: {
|
||||
case SubmenuIndexExternalApp:
|
||||
case SubmenuIndexFileDirectory: {
|
||||
const bool is_file_dir = index == SubmenuIndexFileDirectory;
|
||||
const DialogsFileBrowserOptions browser_options = {
|
||||
.extension = ".fap",
|
||||
.extension = is_file_dir ? "*" : ".fap",
|
||||
.icon = &I_unknown_10px,
|
||||
.skip_assets = true,
|
||||
.hide_ext = true,
|
||||
.hide_ext = !is_file_dir,
|
||||
.item_loader_callback = fap_selector_item_callback,
|
||||
.item_loader_context = app,
|
||||
.base_path = EXT_PATH("apps"),
|
||||
.base_path = is_file_dir ? STORAGE_EXT_PATH_PREFIX : EXT_PATH("apps"),
|
||||
.select_right = is_file_dir,
|
||||
};
|
||||
FuriString* temp_path = furi_string_alloc_set_str(EXT_PATH("apps"));
|
||||
FuriString* temp_path = furi_string_alloc_set_str(browser_options.base_path);
|
||||
|
||||
if(dialog_file_browser_show(app->dialogs, temp_path, temp_path, &browser_options)) {
|
||||
CharList_push_back(app->mainmenu_app_exes, strdup(furi_string_get_cstr(temp_path)));
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint8_t* unused_icon = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
|
||||
flipper_application_load_name_and_icon(temp_path, storage, &unused_icon, temp_path);
|
||||
free(unused_icon);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if(furi_string_start_with_str(temp_path, "[")) {
|
||||
size_t trim = furi_string_search_str(temp_path, "] ", 1);
|
||||
if(trim != FURI_STRING_FAILURE) {
|
||||
furi_string_right(temp_path, trim + 2);
|
||||
if(is_file_dir) {
|
||||
const char* path = furi_string_get_cstr(temp_path);
|
||||
const char* end = strrchr(path, '/');
|
||||
furi_string_set_str(temp_path, end ? end + 1 : path);
|
||||
} else {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
uint8_t* unused_icon = malloc(FAP_MANIFEST_MAX_ICON_SIZE);
|
||||
flipper_application_load_name_and_icon(
|
||||
temp_path, storage, &unused_icon, temp_path);
|
||||
free(unused_icon);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
if(furi_string_start_with_str(temp_path, "[")) {
|
||||
size_t trim = furi_string_search_str(temp_path, "] ", 1);
|
||||
if(trim != FURI_STRING_FAILURE) {
|
||||
furi_string_right(temp_path, trim + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
CharList_push_back(app->mainmenu_app_labels, strdup(furi_string_get_cstr(temp_path)));
|
||||
@@ -68,7 +80,7 @@ void momentum_app_scene_interface_mainmenu_add_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
Submenu* submenu = app->submenu;
|
||||
|
||||
submenu_set_header(submenu, "Add Menu App:");
|
||||
submenu_set_header(submenu, "Add Menu Item:");
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
@@ -84,6 +96,13 @@ void momentum_app_scene_interface_mainmenu_add_on_enter(void* context) {
|
||||
momentum_app_scene_interface_mainmenu_add_submenu_callback,
|
||||
app);
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"File / Directory (right btn)",
|
||||
SubmenuIndexFileDirectory,
|
||||
momentum_app_scene_interface_mainmenu_add_submenu_callback,
|
||||
app);
|
||||
|
||||
view_dispatcher_switch_to_view(app->view_dispatcher, MomentumAppViewSubmenu);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ void momentum_app_scene_interface_mainmenu_reset_on_enter(void* context) {
|
||||
MomentumApp* app = context;
|
||||
DialogEx* dialog_ex = app->dialog_ex;
|
||||
|
||||
dialog_ex_set_header(dialog_ex, "Reset Menu Apps?", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_header(dialog_ex, "Reset Menu Items?", 64, 10, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_text(dialog_ex, "Your edits will be lost!", 64, 32, AlignCenter, AlignCenter);
|
||||
dialog_ex_set_left_button_text(dialog_ex, "Cancel");
|
||||
dialog_ex_set_right_button_text(dialog_ex, "Reset");
|
||||
|
||||
@@ -379,6 +379,8 @@ static Desktop* desktop_alloc(void) {
|
||||
|
||||
furi_record_create(RECORD_DESKTOP, desktop);
|
||||
|
||||
desktop->archive_dir = furi_string_alloc();
|
||||
|
||||
return desktop;
|
||||
}
|
||||
|
||||
@@ -494,6 +496,15 @@ void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled) {
|
||||
desktop->in_transition = false;
|
||||
}
|
||||
|
||||
void desktop_launch_archive(Desktop* desktop, const char* open_dir) {
|
||||
if(open_dir) {
|
||||
furi_string_set(desktop->archive_dir, open_dir);
|
||||
} else {
|
||||
furi_string_reset(desktop->archive_dir);
|
||||
}
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventOpenArchive);
|
||||
}
|
||||
|
||||
/*
|
||||
* Public API
|
||||
*/
|
||||
|
||||
@@ -89,9 +89,12 @@ struct Desktop {
|
||||
|
||||
FuriPubSub* ascii_events_pubsub;
|
||||
FuriPubSubSubscription* ascii_events_subscription;
|
||||
|
||||
FuriString* archive_dir;
|
||||
};
|
||||
|
||||
void desktop_lock(Desktop* desktop, bool pin_lock);
|
||||
void desktop_unlock(Desktop* desktop);
|
||||
int32_t desktop_shutdown(void* context);
|
||||
void desktop_set_stealth_mode_state(Desktop* desktop, bool enabled);
|
||||
void desktop_launch_archive(Desktop* desktop, const char* open_dir);
|
||||
|
||||
@@ -203,7 +203,7 @@ void desktop_run_keybind(Desktop* desktop, InputType _type, InputKey _key) {
|
||||
} else if(furi_string_equal(keybind, "Apps Menu")) {
|
||||
loader_start_detached_with_gui_error(desktop->loader, LOADER_APPLICATIONS_NAME, NULL);
|
||||
} else if(furi_string_equal(keybind, "Archive")) {
|
||||
view_dispatcher_send_custom_event(desktop->view_dispatcher, DesktopMainEventOpenArchive);
|
||||
desktop_launch_archive(desktop, NULL);
|
||||
} else if(furi_string_equal(keybind, "Clock")) {
|
||||
loader_start_detached_with_gui_error(
|
||||
desktop->loader, EXT_PATH("apps/Tools/nightstand.fap"), "");
|
||||
@@ -218,11 +218,11 @@ void desktop_run_keybind(Desktop* desktop, InputType _type, InputKey _key) {
|
||||
} else if(furi_string_equal(keybind, "Wipe Device")) {
|
||||
loader_start_detached_with_gui_error(desktop->loader, "Storage", "Wipe Device");
|
||||
} else {
|
||||
if(storage_common_exists(desktop->storage, furi_string_get_cstr(keybind))) {
|
||||
run_with_default_app(furi_string_get_cstr(keybind));
|
||||
const char* str = furi_string_get_cstr(keybind);
|
||||
if(storage_common_exists(desktop->storage, str)) {
|
||||
run_with_default_app(str);
|
||||
} else {
|
||||
loader_start_detached_with_gui_error(
|
||||
desktop->loader, furi_string_get_cstr(keybind), NULL);
|
||||
loader_start_detached_with_gui_error(desktop->loader, str, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,8 +34,10 @@ static void desktop_scene_main_interact_animation_callback(void* context) {
|
||||
}
|
||||
|
||||
#ifdef APP_ARCHIVE
|
||||
static void
|
||||
desktop_switch_to_app(Desktop* desktop, const FlipperInternalApplication* flipper_app) {
|
||||
static void desktop_switch_to_app(
|
||||
Desktop* desktop,
|
||||
const FlipperInternalApplication* flipper_app,
|
||||
void* context) {
|
||||
furi_assert(desktop);
|
||||
furi_assert(flipper_app);
|
||||
furi_assert(flipper_app->app);
|
||||
@@ -56,6 +58,7 @@ static void
|
||||
furi_thread_set_name(desktop->scene_thread, flipper_app->name);
|
||||
furi_thread_set_stack_size(desktop->scene_thread, flipper_app->stack_size);
|
||||
furi_thread_set_callback(desktop->scene_thread, flipper_app->app);
|
||||
furi_thread_set_context(desktop->scene_thread, context);
|
||||
|
||||
furi_thread_start(desktop->scene_thread);
|
||||
}
|
||||
@@ -114,7 +117,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) {
|
||||
|
||||
case DesktopMainEventOpenArchive:
|
||||
#ifdef APP_ARCHIVE
|
||||
desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE);
|
||||
desktop_switch_to_app(desktop, &FLIPPER_ARCHIVE, desktop->archive_dir);
|
||||
#endif
|
||||
consumed = true;
|
||||
break;
|
||||
|
||||
@@ -16,6 +16,7 @@ void dialog_file_browser_set_basic_options(
|
||||
options->hide_ext = true;
|
||||
options->item_loader_callback = NULL;
|
||||
options->item_loader_context = NULL;
|
||||
options->select_right = false;
|
||||
}
|
||||
|
||||
static DialogsApp* dialogs_app_alloc(void) {
|
||||
|
||||
@@ -26,6 +26,7 @@ typedef struct DialogsApp DialogsApp;
|
||||
* @param hide_ext true - hide extensions for files
|
||||
* @param item_loader_callback callback function for providing custom icon & entry name
|
||||
* @param hide_ext callback context
|
||||
* @param select_right true - select with right key, allows selecting directories
|
||||
*/
|
||||
typedef struct {
|
||||
const char* extension;
|
||||
@@ -36,6 +37,8 @@ typedef struct {
|
||||
bool hide_ext;
|
||||
FileBrowserLoadItemCallback item_loader_callback;
|
||||
void* item_loader_context;
|
||||
|
||||
bool select_right;
|
||||
} DialogsFileBrowserOptions;
|
||||
|
||||
/**
|
||||
|
||||
@@ -43,6 +43,7 @@ bool dialog_file_browser_show(
|
||||
.item_callback = options ? options->item_loader_callback : NULL,
|
||||
.item_callback_context = options ? options->item_loader_context : NULL,
|
||||
.base_path = furi_string_get_cstr(base_path),
|
||||
.select_right = options ? options->select_right : false,
|
||||
}};
|
||||
|
||||
DialogsAppReturn return_data;
|
||||
|
||||
@@ -18,6 +18,8 @@ typedef struct {
|
||||
FileBrowserLoadItemCallback item_callback;
|
||||
void* item_callback_context;
|
||||
const char* base_path;
|
||||
|
||||
bool select_right;
|
||||
} DialogsAppMessageDataFileBrowser;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -46,6 +46,7 @@ bool dialogs_app_process_module_file_browser(const DialogsAppMessageDataFileBrow
|
||||
data->file_icon,
|
||||
data->hide_ext);
|
||||
file_browser_set_item_callback(file_browser, data->item_callback, data->item_callback_context);
|
||||
file_browser_set_select_right(file_browser, data->select_right);
|
||||
file_browser_start(file_browser, data->preselected_filename);
|
||||
|
||||
view_holder_set_view(view_holder, file_browser_get_view(file_browser));
|
||||
|
||||
@@ -127,6 +127,8 @@ struct FileBrowser {
|
||||
|
||||
FuriString* result_path;
|
||||
FuriTimer* scroll_timer;
|
||||
|
||||
bool select_right;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@@ -234,10 +236,11 @@ void file_browser_configure(
|
||||
furi_check(browser);
|
||||
|
||||
browser->ext_filter = extension;
|
||||
browser->skip_assets = skip_assets;
|
||||
browser->hide_ext = hide_ext;
|
||||
browser->base_path = base_path;
|
||||
browser->skip_assets = skip_assets;
|
||||
browser->hide_dot_files = hide_dot_files;
|
||||
browser->hide_ext = hide_ext;
|
||||
browser->select_right = false;
|
||||
|
||||
with_view_model(
|
||||
browser->view,
|
||||
@@ -296,6 +299,11 @@ void file_browser_set_item_callback(
|
||||
browser->item_callback = callback;
|
||||
}
|
||||
|
||||
void file_browser_set_select_right(FileBrowser* browser, bool select_right) {
|
||||
furi_check(browser);
|
||||
browser->select_right = select_right;
|
||||
}
|
||||
|
||||
static bool browser_is_item_in_array(FileBrowserModel* model, uint32_t idx) {
|
||||
size_t array_size = items_array_size(model->items);
|
||||
|
||||
@@ -793,6 +801,31 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) {
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->key == InputKeyRight) {
|
||||
if(event->type == InputTypeShort && browser->select_right) {
|
||||
BrowserItem_t* selected_item = NULL;
|
||||
with_view_model(
|
||||
browser->view,
|
||||
FileBrowserModel * model,
|
||||
{
|
||||
if(browser_is_item_in_array(model, model->item_idx)) {
|
||||
selected_item =
|
||||
items_array_get(model->items, model->item_idx - model->array_offset);
|
||||
}
|
||||
},
|
||||
false);
|
||||
|
||||
if(selected_item) {
|
||||
if(selected_item->type == BrowserItemTypeFile ||
|
||||
selected_item->type == BrowserItemTypeFolder) {
|
||||
furi_string_set(browser->result_path, selected_item->path);
|
||||
if(browser->callback) {
|
||||
browser->callback(browser->context);
|
||||
}
|
||||
}
|
||||
}
|
||||
consumed = true;
|
||||
}
|
||||
} else if(event->key == InputKeyLeft) {
|
||||
if(event->type == InputTypeShort) {
|
||||
bool is_root = false;
|
||||
|
||||
@@ -48,6 +48,8 @@ void file_browser_set_item_callback(
|
||||
FileBrowserLoadItemCallback callback,
|
||||
void* context);
|
||||
|
||||
void file_browser_set_select_right(FileBrowser* browser, bool select_right);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <applications.h>
|
||||
#include <archive/helpers/archive_favorites.h>
|
||||
#include <toolbox/run_parallel.h>
|
||||
#include <archive/helpers/archive_helpers_ext.h>
|
||||
|
||||
#include "loader.h"
|
||||
#include "loader_i.h"
|
||||
@@ -130,7 +131,12 @@ static void loader_menu_apps_callback(void* context, uint32_t index) {
|
||||
LoaderMenuApp* app = context;
|
||||
const MenuApp* menu_app = MenuAppList_get(app->apps_list, index);
|
||||
const char* name = menu_app->path ? menu_app->path : menu_app->name;
|
||||
loader_menu_start(name);
|
||||
|
||||
if(menu_app->path && !strstr(menu_app->path, ".fap")) {
|
||||
run_with_default_app(menu_app->path);
|
||||
} else {
|
||||
loader_menu_start(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void loader_menu_last_callback(void* context, uint32_t index) {
|
||||
@@ -225,6 +231,14 @@ static void loader_menu_add_app_entry(
|
||||
app);
|
||||
}
|
||||
|
||||
static const Icon* loader_menu_get_ext_icon(Storage* storage, const char* path) {
|
||||
if(storage_dir_exists(storage, path)) return &I_dir_10px;
|
||||
const char* ext = strrchr(path, '.');
|
||||
if(ext && strcasecmp(ext, ".js") == 0) return &I_js_script_10px;
|
||||
|
||||
return &I_file_10px;
|
||||
}
|
||||
|
||||
bool loader_menu_load_fap_meta(
|
||||
Storage* storage,
|
||||
FuriString* path,
|
||||
@@ -254,11 +268,9 @@ static void loader_menu_find_add_app(LoaderMenuApp* app, Storage* storage, FuriS
|
||||
if(furi_string_start_with(line, "/")) {
|
||||
path = strdup(furi_string_get_cstr(line));
|
||||
if(!loader_menu_load_fap_meta(storage, line, line, &icon)) {
|
||||
free((void*)path);
|
||||
path = NULL;
|
||||
} else {
|
||||
name = strdup(furi_string_get_cstr(line));
|
||||
icon = loader_menu_get_ext_icon(storage, path);
|
||||
}
|
||||
name = strdup(furi_string_get_cstr(line));
|
||||
} else {
|
||||
for(size_t i = 0; !name && i < FLIPPER_APPS_COUNT; i++) {
|
||||
if(furi_string_equal(line, FLIPPER_APPS[i].name)) {
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
DesktopSettingsAppKeybindActionTypeMainApp,
|
||||
DesktopSettingsAppKeybindActionTypeExternalApp,
|
||||
DesktopSettingsAppKeybindActionTypeOpenFile,
|
||||
DesktopSettingsAppKeybindActionTypeOpenFileOrDirectory,
|
||||
DesktopSettingsAppKeybindActionTypeMoreActions,
|
||||
DesktopSettingsAppKeybindActionTypeRemoveKeybind,
|
||||
} DesktopSettingsAppKeybindActionType;
|
||||
|
||||
@@ -31,7 +31,7 @@ static void
|
||||
scene_manager_next_scene(app->scene_manager, DesktopSettingsAppSceneKeybindsAction);
|
||||
break;
|
||||
case DesktopSettingsAppKeybindActionTypeExternalApp:
|
||||
case DesktopSettingsAppKeybindActionTypeOpenFile: {
|
||||
case DesktopSettingsAppKeybindActionTypeOpenFileOrDirectory: {
|
||||
const char* base_path;
|
||||
const char* extension;
|
||||
bool hide_ext;
|
||||
@@ -53,9 +53,10 @@ static void
|
||||
.item_loader_callback = keybinds_fap_selector_item_callback,
|
||||
.item_loader_context = app,
|
||||
.base_path = base_path,
|
||||
.select_right = true,
|
||||
};
|
||||
FuriString* temp_path = furi_string_alloc_set_str(base_path);
|
||||
if(storage_file_exists(furi_record_open(RECORD_STORAGE), furi_string_get_cstr(keybind))) {
|
||||
if(storage_common_exists(furi_record_open(RECORD_STORAGE), furi_string_get_cstr(keybind))) {
|
||||
furi_string_set(temp_path, keybind);
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
@@ -98,8 +99,8 @@ void desktop_settings_scene_keybinds_action_type_on_enter(void* context) {
|
||||
|
||||
submenu_add_item(
|
||||
submenu,
|
||||
"Open File",
|
||||
DesktopSettingsAppKeybindActionTypeOpenFile,
|
||||
"File / Directory (right btn)",
|
||||
DesktopSettingsAppKeybindActionTypeOpenFileOrDirectory,
|
||||
desktop_settings_scene_keybinds_action_type_submenu_callback,
|
||||
app);
|
||||
|
||||
@@ -131,12 +132,15 @@ void desktop_settings_scene_keybinds_action_type_on_enter(void* context) {
|
||||
}
|
||||
}
|
||||
|
||||
if(storage_file_exists(furi_record_open(RECORD_STORAGE), furi_string_get_cstr(keybind))) {
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
if(storage_file_exists(storage, furi_string_get_cstr(keybind))) {
|
||||
if(furi_string_end_with_str(keybind, ".fap")) {
|
||||
selected = DesktopSettingsAppKeybindActionTypeExternalApp;
|
||||
} else {
|
||||
selected = DesktopSettingsAppKeybindActionTypeOpenFile;
|
||||
selected = DesktopSettingsAppKeybindActionTypeOpenFileOrDirectory;
|
||||
}
|
||||
} else if(storage_dir_exists(storage, furi_string_get_cstr(keybind))) {
|
||||
selected = DesktopSettingsAppKeybindActionTypeOpenFileOrDirectory;
|
||||
}
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
|
||||
|
||||
@@ -1146,6 +1146,7 @@ Function,+,file_browser_free,void,FileBrowser*
|
||||
Function,+,file_browser_get_view,View*,FileBrowser*
|
||||
Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*"
|
||||
Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*"
|
||||
Function,+,file_browser_set_select_right,void,"FileBrowser*, _Bool"
|
||||
Function,+,file_browser_start,void,"FileBrowser*, FuriString*"
|
||||
Function,+,file_browser_stop,void,FileBrowser*
|
||||
Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool"
|
||||
|
||||
|
Reference in New Issue
Block a user