From 41cf421234fcd9974754b6219477c2b8a5b1dd46 Mon Sep 17 00:00:00 2001 From: Nikolay Minaylov Date: Thu, 9 Jun 2022 10:09:52 +0300 Subject: [PATCH] [FL-2565] Archive: switch to browser worker #1295 --- applications/archive/archive.c | 3 + applications/archive/archive_i.h | 1 + applications/archive/helpers/archive_apps.c | 8 +- .../archive/helpers/archive_browser.c | 277 ++++++++++-------- .../archive/helpers/archive_browser.h | 14 +- .../archive/helpers/archive_favorites.c | 5 +- applications/archive/helpers/archive_files.c | 153 +--------- applications/archive/helpers/archive_files.h | 19 +- .../archive/scenes/archive_scene_browser.c | 40 ++- .../archive/scenes/archive_scene_delete.c | 15 +- .../archive/scenes/archive_scene_rename.c | 41 ++- .../archive/views/archive_browser_view.c | 52 +++- .../archive/views/archive_browser_view.h | 17 +- applications/bad_usb/views/bad_usb_view.c | 2 +- applications/debug_tools/usb_test.c | 4 +- .../gui/modules/file_browser_worker.c | 58 +++- .../gui/modules/file_browser_worker.h | 8 + lib/toolbox/path.c | 11 + lib/toolbox/path.h | 9 + 19 files changed, 377 insertions(+), 360 deletions(-) diff --git a/applications/archive/archive.c b/applications/archive/archive.c index 5a38700b..99b9bb02 100644 --- a/applications/archive/archive.c +++ b/applications/archive/archive.c @@ -1,4 +1,5 @@ #include "archive_i.h" +#include "m-string.h" bool archive_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -17,6 +18,7 @@ ArchiveApp* archive_alloc() { archive->gui = furi_record_open("gui"); archive->text_input = text_input_alloc(); + string_init(archive->fav_move_str); archive->view_dispatcher = view_dispatcher_alloc(); archive->scene_manager = scene_manager_alloc(&archive_scene_handlers, archive); @@ -56,6 +58,7 @@ void archive_free(ArchiveApp* archive) { view_dispatcher_free(archive->view_dispatcher); scene_manager_free(archive->scene_manager); browser_free(archive->browser); + string_clear(archive->fav_move_str); text_input_free(archive->text_input); diff --git a/applications/archive/archive_i.h b/applications/archive/archive_i.h index 0822a801..865e837d 100644 --- a/applications/archive/archive_i.h +++ b/applications/archive/archive_i.h @@ -28,6 +28,7 @@ struct ArchiveApp { TextInput* text_input; Widget* widget; FuriPubSubSubscription* loader_stop_subscription; + string_t fav_move_str; char text_store[MAX_NAME_LEN]; char file_extension[MAX_EXT_LEN + 1]; }; diff --git a/applications/archive/helpers/archive_apps.c b/applications/archive/helpers/archive_apps.c index f5edbbb3..3393993d 100644 --- a/applications/archive/helpers/archive_apps.c +++ b/applications/archive/helpers/archive_apps.c @@ -7,8 +7,14 @@ static const char* known_apps[] = { }; ArchiveAppTypeEnum archive_get_app_type(const char* path) { + const char* app_name = strchr(path, ':'); + if(app_name == NULL) { + return ArchiveAppTypeUnknown; + } + app_name++; + for(size_t i = 0; i < COUNT_OF(known_apps); i++) { - if(strncmp(path, known_apps[i], strlen(known_apps[i])) == 0) { + if(strncmp(app_name, known_apps[i], strlen(known_apps[i])) == 0) { return i; } } diff --git a/applications/archive/helpers/archive_browser.c b/applications/archive/helpers/archive_browser.c index cb3d3ca8..b2f39c07 100644 --- a/applications/archive/helpers/archive_browser.c +++ b/applications/archive/helpers/archive_browser.c @@ -1,8 +1,91 @@ +#include "archive/views/archive_browser_view.h" #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include "furi/common_defines.h" +#include "furi/log.h" +#include "gui/modules/file_browser_worker.h" +#include "m-string.h" #include +static void + archive_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + int32_t load_offset = 0; + browser->is_root = is_root; + + if((item_cnt == 0) && (archive_is_home(browser))) { + archive_switch_tab(browser, browser->last_tab_switch_dir); + } else if(!string_start_with_str_p(browser->path, "/app:")) { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_reset(model->files); + model->item_cnt = item_cnt; + model->item_idx = (file_idx > 0) ? file_idx : 0; + load_offset = + CLAMP(model->item_idx - FILE_LIST_BUF_LEN / 2, (int32_t)model->item_cnt, 0); + model->array_offset = 0; + model->list_offset = 0; + model->list_loading = true; + model->folder_loading = false; + return false; + }); + archive_update_offset(browser); + + file_browser_worker_load(browser->worker, load_offset, FILE_LIST_BUF_LEN); + } +} + +static void archive_list_load_cb(void* context, uint32_t list_load_offset) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_reset(model->files); + model->array_offset = list_load_offset; + return false; + }); +} + +static void archive_list_item_cb(void* context, string_t item_path, bool is_folder, bool is_last) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + if(!is_last) { + archive_add_file_item(browser, is_folder, string_get_cstr(item_path)); + } else { + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->list_loading = false; + return true; + }); + } +} + +static void archive_long_load_cb(void* context) { + furi_assert(context); + ArchiveBrowserView* browser = (ArchiveBrowserView*)context; + + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + model->folder_loading = true; + return true; + }); +} + +void archive_file_browser_set_callbacks(ArchiveBrowserView* browser) { + furi_assert(browser); + + file_browser_worker_set_callback_context(browser->worker, browser); + file_browser_worker_set_folder_callback(browser->worker, archive_folder_open_cb); + file_browser_worker_set_list_callback(browser->worker, archive_list_load_cb); + file_browser_worker_set_item_callback(browser->worker, archive_list_item_cb); + file_browser_worker_set_long_load_callback(browser->worker, archive_long_load_cb); +} + bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) { size_t array_size = files_array_size(model->files); @@ -39,9 +122,9 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { furi_assert(browser); furi_assert(target); - archive_get_filenames(browser, string_get_cstr(browser->path)); + archive_get_items(browser, string_get_cstr(browser->path)); - if(!archive_file_get_array_size(browser) && !archive_get_depth(browser)) { + if(!archive_file_get_array_size(browser) && archive_is_home(browser)) { archive_switch_tab(browser, TAB_RIGHT); } else { with_view_model( @@ -49,7 +132,7 @@ void archive_update_focus(ArchiveBrowserView* browser, const char* target) { uint16_t idx = 0; while(idx < files_array_size(model->files)) { ArchiveFile_t* current = files_array_get(model->files, idx); - if(!string_search(current->name, target)) { + if(!string_search(current->path, target)) { model->item_idx = idx + model->array_offset; break; } @@ -102,7 +185,7 @@ void archive_file_array_rm_selected(ArchiveBrowserView* browser) { return false; }); - if((items_cnt == 0) && (archive_get_depth(browser) == 0)) { + if((items_cnt == 0) && (archive_is_home(browser))) { archive_switch_tab(browser, TAB_RIGHT); } @@ -145,7 +228,7 @@ void archive_file_array_rm_all(ArchiveBrowserView* browser) { }); } -bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { +void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { furi_assert(browser); int32_t offset_new = 0; @@ -160,22 +243,17 @@ bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) { } else { offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1; } - offset_new = CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); + if(offset_new > 0) { + offset_new = + CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0); + } else { + offset_new = 0; + } } return false; }); - bool res = archive_dir_read_items( - browser, string_get_cstr(browser->path), offset_new, FILE_LIST_BUF_LEN); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->array_offset = offset_new; - model->list_loading = false; - return true; - }); - - return res; + file_browser_worker_load(browser->worker, offset_new, FILE_LIST_BUF_LEN); } ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) { @@ -218,26 +296,20 @@ ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) { return tab_id; } -uint8_t archive_get_depth(ArchiveBrowserView* browser) { +bool archive_is_home(ArchiveBrowserView* browser) { furi_assert(browser); - uint8_t depth; - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - depth = idx_last_array_size(model->idx_last); - return false; - }); + if(browser->is_root) { + return true; + } - return depth; -} - -const char* archive_get_path(ArchiveBrowserView* browser) { - return string_get_cstr(browser->path); + const char* default_path = archive_get_default_path(archive_get_tab(browser)); + return (string_cmp_str(browser->path, default_path) == 0); } const char* archive_get_name(ArchiveBrowserView* browser) { ArchiveFile_t* selected = archive_get_current_file(browser); - return string_get_cstr(selected->name); + return string_get_cstr(selected->path); } void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { @@ -249,37 +321,15 @@ void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { return false; }); } -void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) { - UNUSED(tab); // FIXME? - furi_assert(browser); - - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - model->last_tab = model->tab_idx; - return false; - }); -} void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { furi_assert(browser); furi_assert(name); ArchiveFile_t item; - - string_t full_name; - - string_init_set(full_name, browser->path); - string_cat_printf(full_name, "/%s", name); - - char* app_name = strchr(string_get_cstr(full_name), ':'); - if(app_name == NULL) { - string_clear(full_name); - return; - } - ArchiveFile_t_init(&item); - string_init_set_str(item.name, name); - archive_set_file_type(&item, NULL, app_name + 1, true); + string_set_str(item.path, name); + archive_set_file_type(&item, name, false, true); with_view_model( browser->view, (ArchiveBrowserViewModel * model) { @@ -288,29 +338,24 @@ void archive_add_app_item(ArchiveBrowserView* browser, const char* name) { return false; }); ArchiveFile_t_clear(&item); - string_clear(full_name); } -void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) { +void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name) { furi_assert(browser); - furi_assert(file_info); furi_assert(name); ArchiveFile_t item; - if(archive_filter_by_extension( - file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { - ArchiveFile_t_init(&item); - string_init_set_str(item.name, name); - archive_set_file_type(&item, file_info, archive_get_path(browser), false); + ArchiveFile_t_init(&item); + string_init_set_str(item.path, name); + archive_set_file_type(&item, string_get_cstr(browser->path), is_folder, false); - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - files_array_push_back(model->files, item); - return false; - }); - ArchiveFile_t_clear(&item); - } + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + files_array_push_back(model->files, item); + return false; + }); + ArchiveFile_t_clear(&item); } void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { @@ -323,7 +368,7 @@ void archive_show_file_menu(ArchiveBrowserView* browser, bool show) { model->menu_idx = 0; ArchiveFile_t* selected = files_array_get(model->files, model->item_idx - model->array_offset); - selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name)); + selected->fav = archive_is_favorite("%s", string_get_cstr(selected->path)); } } else { model->menu = false; @@ -344,36 +389,40 @@ void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) { }); } -void archive_switch_dir(ArchiveBrowserView* browser, const char* path) { - furi_assert(browser); - furi_assert(path); - - string_set(browser->path, path); - archive_get_filenames(browser, string_get_cstr(browser->path)); - archive_update_offset(browser); -} - void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_assert(browser); ArchiveTabEnum tab = archive_get_tab(browser); + browser->last_tab_switch_dir = key; + if(key == InputKeyLeft) { tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else if(key == InputKeyRight) { + } else { tab = (tab + 1) % ArchiveTabTotal; } + browser->is_root = true; archive_set_tab(browser, tab); - const char* path = archive_get_default_path(tab); + string_set_str(browser->path, archive_get_default_path(tab)); bool tab_empty = true; if(tab == ArchiveTabFavorites) { - if(archive_favorites_count(browser) > 0) tab_empty = false; - } else if(strncmp(path, "/app:", 5) == 0) { - if(archive_app_is_available(browser, path)) tab_empty = false; + if(archive_favorites_count(browser) > 0) { + tab_empty = false; + } + } else if(string_start_with_str_p(browser->path, "/app:")) { + char* app_name = strchr(string_get_cstr(browser->path), ':'); + if(app_name != NULL) { + if(archive_app_is_available(browser, string_get_cstr(browser->path))) { + tab_empty = false; + } + } } else { - uint32_t files_cnt = archive_dir_count_items(browser, archive_get_default_path(tab)); - if(files_cnt > 0) tab_empty = false; + ArchiveTabEnum tab = archive_get_tab(browser); + bool skip_assets = (strcmp(archive_get_tab_ext(tab), "*") == 0) ? false : true; + file_browser_worker_set_config( + browser->worker, browser->path, archive_get_tab_ext(tab), skip_assets); + tab_empty = false; // Empty check will be performed later } if((tab_empty) && (tab != ArchiveTabBrowser)) { @@ -381,60 +430,46 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { } else { with_view_model( browser->view, (ArchiveBrowserViewModel * model) { - if(model->last_tab != model->tab_idx) { - model->item_idx = 0; - model->array_offset = 0; - idx_last_array_reset(model->idx_last); - } + model->item_idx = 0; + model->array_offset = 0; return false; }); - archive_switch_dir(browser, archive_get_default_path(tab)); + archive_get_items(browser, string_get_cstr(browser->path)); + archive_update_offset(browser); } - archive_set_last_tab(browser, tab); } -void archive_enter_dir(ArchiveBrowserView* browser, string_t name) { +void archive_enter_dir(ArchiveBrowserView* browser, string_t path) { furi_assert(browser); - furi_assert(name); + furi_assert(path); - if(string_size(name) >= (MAX_NAME_LEN - 1)) { - return; - } + int32_t idx_temp = 0; - if(string_cmp(browser->path, name) != 0) { - with_view_model( - browser->view, (ArchiveBrowserViewModel * model) { - idx_last_array_push_back(model->idx_last, model->item_idx); - model->array_offset = 0; - model->item_idx = 0; - return false; - }); + with_view_model( + browser->view, (ArchiveBrowserViewModel * model) { + idx_temp = model->item_idx; + return false; + }); - string_set(browser->path, name); - } - - archive_dir_count_items(browser, string_get_cstr(name)); - archive_switch_dir(browser, string_get_cstr(browser->path)); + string_set(browser->path, path); + file_browser_worker_folder_enter(browser->worker, path, idx_temp); } void archive_leave_dir(ArchiveBrowserView* browser) { furi_assert(browser); - const char* path = archive_get_path(browser); - char* last_char_ptr = strrchr(path, '/'); + file_browser_worker_folder_exit(browser->worker); +} - if(last_char_ptr) { - size_t pos = last_char_ptr - path; - string_left(browser->path, pos); - } +void archive_refresh_dir(ArchiveBrowserView* browser) { + furi_assert(browser); - archive_dir_count_items(browser, path); + int32_t idx_temp = 0; with_view_model( browser->view, (ArchiveBrowserViewModel * model) { - idx_last_array_pop_back(&model->item_idx, model->idx_last); + idx_temp = model->item_idx; return false; }); - - archive_switch_dir(browser, path); + file_browser_worker_folder_refresh(browser->worker, idx_temp); } diff --git a/applications/archive/helpers/archive_browser.h b/applications/archive/helpers/archive_browser.h index 3395541e..c4283123 100644 --- a/applications/archive/helpers/archive_browser.h +++ b/applications/archive/helpers/archive_browser.h @@ -2,11 +2,12 @@ #include "../archive_i.h" -#define TAB_RIGHT InputKeyRight //default tab swith direction +#define TAB_RIGHT InputKeyRight // Default tab swith direction +#define TAB_DEFAULT ArchiveTabFavorites // Start tab #define FILE_LIST_BUF_LEN 100 static const char* tab_default_paths[] = { - [ArchiveTabFavorites] = "/any/favorites", + [ArchiveTabFavorites] = "/app:favorites", [ArchiveTabIButton] = "/any/ibutton", [ArchiveTabNFC] = "/any/nfc", [ArchiveTabSubGhz] = "/any/subghz", @@ -62,7 +63,7 @@ bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx); void archive_update_offset(ArchiveBrowserView* browser); void archive_update_focus(ArchiveBrowserView* browser, const char* target); -bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir); +void archive_file_array_load(ArchiveBrowserView* browser, int8_t dir); size_t archive_file_get_array_size(ArchiveBrowserView* browser); void archive_file_array_rm_selected(ArchiveBrowserView* browser); void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir); @@ -73,15 +74,16 @@ void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count); ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser); ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx); ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser); -uint8_t archive_get_depth(ArchiveBrowserView* browser); -const char* archive_get_path(ArchiveBrowserView* browser); +bool archive_is_home(ArchiveBrowserView* browser); const char* archive_get_name(ArchiveBrowserView* browser); void archive_add_app_item(ArchiveBrowserView* browser, const char* name); -void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name); +void archive_add_file_item(ArchiveBrowserView* browser, bool is_folder, const char* name); void archive_show_file_menu(ArchiveBrowserView* browser, bool show); void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active); void archive_switch_tab(ArchiveBrowserView* browser, InputKey key); void archive_enter_dir(ArchiveBrowserView* browser, string_t name); void archive_leave_dir(ArchiveBrowserView* browser); +void archive_refresh_dir(ArchiveBrowserView* browser); +void archive_file_browser_set_callbacks(ArchiveBrowserView* browser); diff --git a/applications/archive/helpers/archive_favorites.c b/applications/archive/helpers/archive_favorites.c index ac5b9d29..af7927e9 100644 --- a/applications/archive/helpers/archive_favorites.c +++ b/applications/archive/helpers/archive_favorites.c @@ -168,7 +168,8 @@ bool archive_favorites_read(void* context) { if(file_exists) { storage_common_stat(fs_api, string_get_cstr(buffer), &file_info); storage_file_close(fav_item_file); - archive_add_file_item(browser, &file_info, string_get_cstr(buffer)); + archive_add_file_item( + browser, (file_info.flags & FSF_DIRECTORY), string_get_cstr(buffer)); file_count++; } else { storage_file_close(fav_item_file); @@ -337,7 +338,7 @@ void archive_favorites_save(void* context) { for(size_t i = 0; i < archive_file_get_array_size(browser); i++) { ArchiveFile_t* item = archive_get_file_at(browser, i); - archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->name)); + archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(item->path)); } storage_common_remove(fs_api, ARCHIVE_FAV_PATH); diff --git a/applications/archive/helpers/archive_files.c b/applications/archive/helpers/archive_files.c index 3b1e8944..7ff54fed 100644 --- a/applications/archive/helpers/archive_files.c +++ b/applications/archive/helpers/archive_files.c @@ -6,59 +6,18 @@ #define ASSETS_DIR "assets" -bool archive_filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name) { - furi_assert(file_info); - furi_assert(tab_ext); - furi_assert(name); - - bool result = false; - - if(strcmp(tab_ext, "*") == 0) { - result = true; - } else if(strstr(name, tab_ext) != NULL) { - result = true; - } else if(file_info->flags & FSF_DIRECTORY) { - if(strstr(name, ASSETS_DIR) != NULL) { - result = false; // Skip assets folder in all tabs except browser - } else { - result = true; - } - } - - return result; -} - -void archive_trim_file_path(char* name, bool ext) { - char* slash = strrchr(name, '/') + 1; - if(strlen(slash)) strlcpy(name, slash, strlen(slash) + 1); - if(ext) { - char* dot = strrchr(name, '.'); - if(strlen(dot)) *dot = '\0'; - } -} - -void archive_get_file_extension(char* name, char* ext) { - char* dot = strrchr(name, '.'); - if(dot == NULL) - *ext = '\0'; - else - strncpy(ext, dot, MAX_EXT_LEN); -} - -void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app) { +void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app) { furi_assert(file); file->is_app = is_app; if(is_app) { file->type = archive_get_app_filetype(archive_get_app_type(path)); } else { - furi_assert(file_info); - for(size_t i = 0; i < COUNT_OF(known_ext); i++) { if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; - if(string_search_str(file->name, known_ext[i], 0) != STRING_FAILURE) { + if(string_search_str(file->path, known_ext[i], 0) != STRING_FAILURE) { if(i == ArchiveFileTypeBadUsb) { - if(string_search_str(file->name, archive_get_default_path(ArchiveTabBadUsb)) == + if(string_search_str(file->path, archive_get_default_path(ArchiveTabBadUsb)) == 0) { file->type = i; return; // *.txt file is a BadUSB script only if it is in BadUSB folder @@ -70,7 +29,7 @@ void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* } } - if(file_info->flags & FSF_DIRECTORY) { + if(is_folder) { file->type = ArchiveFileTypeFolder; } else { file->type = ArchiveFileTypeUnknown; @@ -78,120 +37,20 @@ void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* } } -bool archive_get_filenames(void* context, const char* path) { +bool archive_get_items(void* context, const char* path) { furi_assert(context); - bool res; + bool res = false; ArchiveBrowserView* browser = context; if(archive_get_tab(browser) == ArchiveTabFavorites) { res = archive_favorites_read(browser); } else if(strncmp(path, "/app:", 5) == 0) { res = archive_app_read_dir(browser, path); - } else { - res = archive_file_array_load(browser, 0); } return res; } -uint32_t archive_dir_count_items(void* context, const char* path) { - furi_assert(context); - ArchiveBrowserView* browser = context; - - FileInfo file_info; - Storage* fs_api = furi_record_open("storage"); - File* directory = storage_file_alloc(fs_api); - char name[MAX_NAME_LEN]; - - if(!storage_dir_open(directory, path)) { - storage_dir_close(directory); - storage_file_free(directory); - return 0; - } - - uint32_t files_found = 0; - while(1) { - if(!storage_dir_read(directory, &file_info, name, MAX_NAME_LEN)) { - break; - } - if((storage_file_get_error(directory) == FSE_OK) && (name[0])) { - if(archive_filter_by_extension( - &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { - files_found++; - } - } - } - storage_dir_close(directory); - storage_file_free(directory); - - furi_record_close("storage"); - - archive_set_item_count(browser, files_found); - - return files_found; -} - -uint32_t archive_dir_read_items(void* context, const char* path, uint32_t offset, uint32_t count) { - furi_assert(context); - - ArchiveBrowserView* browser = context; - FileInfo file_info; - Storage* fs_api = furi_record_open("storage"); - File* directory = storage_file_alloc(fs_api); - char name[MAX_NAME_LEN]; - snprintf(name, MAX_NAME_LEN, "%s/", path); - size_t path_len = strlen(name); - - if(!storage_dir_open(directory, path)) { - storage_dir_close(directory); - storage_file_free(directory); - return false; - } - - // Skip items before offset - uint32_t items_cnt = 0; - while(items_cnt < offset) { - if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - if(archive_filter_by_extension( - &file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) { - items_cnt++; - } - } else { - break; - } - } - if(items_cnt != offset) { - storage_dir_close(directory); - storage_file_free(directory); - furi_record_close("storage"); - - return false; - } - - items_cnt = 0; - archive_file_array_rm_all(browser); - while(items_cnt < count) { - if(!storage_dir_read(directory, &file_info, &name[path_len], MAX_NAME_LEN - path_len)) { - break; - } - - if(storage_file_get_error(directory) == FSE_OK) { - archive_add_file_item(browser, &file_info, name); - items_cnt++; - } else { - break; - } - } - storage_dir_close(directory); - storage_file_free(directory); - furi_record_close("storage"); - - return (items_cnt == count); -} - void archive_file_append(const char* path, const char* format, ...) { furi_assert(path); diff --git a/applications/archive/helpers/archive_files.h b/applications/archive/helpers/archive_files.h index 58dd0011..84b7e24a 100644 --- a/applications/archive/helpers/archive_files.h +++ b/applications/archive/helpers/archive_files.h @@ -19,7 +19,7 @@ typedef enum { } ArchiveFileTypeEnum; typedef struct { - string_t name; + string_t path; ArchiveFileTypeEnum type; bool fav; bool is_app; @@ -29,25 +29,25 @@ static void ArchiveFile_t_init(ArchiveFile_t* obj) { obj->type = ArchiveFileTypeUnknown; obj->is_app = false; obj->fav = false; - string_init(obj->name); + string_init(obj->path); } static void ArchiveFile_t_init_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { obj->type = src->type; obj->is_app = src->is_app; obj->fav = src->fav; - string_init_set(obj->name, src->name); + string_init_set(obj->path, src->path); } static void ArchiveFile_t_set(ArchiveFile_t* obj, const ArchiveFile_t* src) { obj->type = src->type; obj->is_app = src->is_app; obj->fav = src->fav; - string_set(obj->name, src->name); + string_set(obj->path, src->path); } static void ArchiveFile_t_clear(ArchiveFile_t* obj) { - string_clear(obj->name); + string_clear(obj->path); } ARRAY_DEF( @@ -58,12 +58,7 @@ ARRAY_DEF( INIT_SET(API_6(ArchiveFile_t_init_set)), CLEAR(API_2(ArchiveFile_t_clear)))) -bool archive_filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name); -void archive_set_file_type(ArchiveFile_t* file, FileInfo* file_info, const char* path, bool is_app); -void archive_trim_file_path(char* name, bool ext); -void archive_get_file_extension(char* name, char* ext); -bool archive_get_filenames(void* context, const char* path); -uint32_t archive_dir_count_items(void* context, const char* path); -uint32_t archive_dir_read_items(void* context, const char* path, uint32_t offset, uint32_t count); +void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder, bool is_app); +bool archive_get_items(void* context, const char* path); void archive_file_append(const char* path, const char* format, ...); void archive_delete_file(void* context, const char* format, ...); diff --git a/applications/archive/scenes/archive_scene_browser.c b/applications/archive/scenes/archive_scene_browser.c index 9df3463b..e3581538 100644 --- a/applications/archive/scenes/archive_scene_browser.c +++ b/applications/archive/scenes/archive_scene_browser.c @@ -4,9 +4,13 @@ #include "../helpers/archive_favorites.h" #include "../helpers/archive_browser.h" #include "../views/archive_browser_view.h" +#include "archive/scenes/archive_scene.h" #define TAG "ArchiveSceneBrowser" +#define SCENE_STATE_DEFAULT (0) +#define SCENE_STATE_NEED_REFRESH (1) + static const char* flipper_app_name[] = { [ArchiveFileTypeIButton] = "iButton", [ArchiveFileTypeNFC] = "NFC", @@ -26,7 +30,7 @@ static void archive_loader_callback(const void* message, void* context) { if(event->type == LoaderEventTypeApplicationStopped) { view_dispatcher_send_custom_event( - archive->view_dispatcher, ArchiveBrowserEventLoaderAppExit); + archive->view_dispatcher, ArchiveBrowserEventListRefresh); } } @@ -36,14 +40,14 @@ static void archive_run_in_app(ArchiveBrowserView* browser, ArchiveFile_t* selec LoaderStatus status; if(selected->is_app) { - char* param = strrchr(string_get_cstr(selected->name), '/'); + char* param = strrchr(string_get_cstr(selected->path), '/'); if(param != NULL) { param++; } status = loader_start(loader, flipper_app_name[selected->type], param); } else { status = loader_start( - loader, flipper_app_name[selected->type], string_get_cstr(selected->name)); + loader, flipper_app_name[selected->type], string_get_cstr(selected->path)); } if(status != LoaderStatusOk) { @@ -61,6 +65,7 @@ void archive_scene_browser_callback(ArchiveBrowserEvent event, void* context) { void archive_scene_browser_on_enter(void* context) { ArchiveApp* archive = (ArchiveApp*)context; ArchiveBrowserView* browser = archive->browser; + browser->is_root = true; archive_browser_set_callback(browser, archive_scene_browser_callback, archive); archive_update_focus(browser, archive->text_store); @@ -70,6 +75,16 @@ void archive_scene_browser_on_enter(void* context) { archive->loader_stop_subscription = furi_pubsub_subscribe(loader_get_pubsub(loader), archive_loader_callback, archive); furi_record_close("loader"); + + uint32_t state = scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneBrowser); + + if(state == SCENE_STATE_NEED_REFRESH) { + view_dispatcher_send_custom_event( + archive->view_dispatcher, ArchiveBrowserEventListRefresh); + } + + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_DEFAULT); } bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { @@ -95,8 +110,8 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { case ArchiveBrowserEventFileMenuRun: if(known_app) { archive_run_in_app(browser, selected); + archive_show_file_menu(browser, false); } - archive_show_file_menu(browser, false); consumed = true; break; case ArchiveBrowserEventFileMenuPin: @@ -115,11 +130,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; - case ArchiveBrowserEventFileMenuAction: + case ArchiveBrowserEventFileMenuRename: if(favorites) { browser->callback(ArchiveBrowserEventEnterFavMove, browser->context); } else if((known_app) && (selected->is_app == false)) { archive_show_file_menu(browser, false); + scene_manager_set_scene_state( + archive->scene_manager, ArchiveAppSceneBrowser, SCENE_STATE_NEED_REFRESH); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneRename); } consumed = true; @@ -131,7 +148,7 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventEnterDir: - archive_enter_dir(browser, selected->name); + archive_enter_dir(browser, selected->path); consumed = true; break; case ArchiveBrowserEventFavMoveUp: @@ -143,13 +160,13 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { consumed = true; break; case ArchiveBrowserEventEnterFavMove: - strlcpy(archive->text_store, archive_get_name(browser), MAX_NAME_LEN); + string_set(archive->fav_move_str, selected->path); archive_show_file_menu(browser, false); archive_favorites_move_mode(archive->browser, true); consumed = true; break; case ArchiveBrowserEventExitFavMove: - archive_update_focus(browser, archive->text_store); + archive_update_focus(browser, string_get_cstr(archive->fav_move_str)); archive_favorites_move_mode(archive->browser, false); consumed = true; break; @@ -166,18 +183,17 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_file_array_load(archive->browser, 1); consumed = true; break; - case ArchiveBrowserEventLoaderAppExit: + case ArchiveBrowserEventListRefresh: if(!favorites) { - archive_enter_dir(browser, browser->path); + archive_refresh_dir(browser); } else { archive_favorites_read(browser); } - consumed = true; break; case ArchiveBrowserEventExit: - if(archive_get_depth(browser)) { + if(!archive_is_home(browser)) { archive_leave_dir(browser); } else { Loader* loader = furi_record_open("loader"); diff --git a/applications/archive/scenes/archive_scene_delete.c b/applications/archive/scenes/archive_scene_delete.c index fbcba777..d882fd06 100644 --- a/applications/archive/scenes/archive_scene_delete.c +++ b/applications/archive/scenes/archive_scene_delete.c @@ -3,6 +3,8 @@ #include "../helpers/archive_files.h" #include "../helpers/archive_apps.h" #include "../helpers/archive_browser.h" +#include "toolbox/path.h" +#include "m-string.h" #define SCENE_DELETE_CUSTOM_EVENT (0UL) #define MAX_TEXT_INPUT_LEN 22 @@ -24,18 +26,19 @@ void archive_scene_delete_on_enter(void* context) { widget_add_button_element( app->widget, GuiButtonTypeRight, "Delete", archive_scene_delete_widget_callback, app); + string_t filename; + string_init(filename); + ArchiveFile_t* current = archive_get_current_file(app->browser); - strlcpy(app->text_store, string_get_cstr(current->name), MAX_NAME_LEN); - char* name = strrchr(app->text_store, '/'); - if(name != NULL) { - name++; - } + path_extract_filename(current->path, filename, false); char delete_str[64]; - snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", name); + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", string_get_cstr(filename)); widget_add_text_box_element( app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); + string_clear(filename); + view_dispatcher_switch_to_view(app->view_dispatcher, ArchiveViewWidget); } diff --git a/applications/archive/scenes/archive_scene_rename.c b/applications/archive/scenes/archive_scene_rename.c index e0c4ba6c..d292dd60 100644 --- a/applications/archive/scenes/archive_scene_rename.c +++ b/applications/archive/scenes/archive_scene_rename.c @@ -2,6 +2,8 @@ #include "../helpers/archive_favorites.h" #include "../helpers/archive_files.h" #include "../helpers/archive_browser.h" +#include "archive/views/archive_browser_view.h" +#include "toolbox/path.h" #define SCENE_RENAME_CUSTOM_EVENT (0UL) #define MAX_TEXT_INPUT_LEN 22 @@ -16,10 +18,13 @@ void archive_scene_rename_on_enter(void* context) { TextInput* text_input = archive->text_input; ArchiveFile_t* current = archive_get_current_file(archive->browser); - strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN); - archive_get_file_extension(archive->text_store, archive->file_extension); - archive_trim_file_path(archive->text_store, true); + string_t filename; + string_init(filename); + path_extract_filename(current->path, filename, true); + strlcpy(archive->text_store, string_get_cstr(filename), MAX_NAME_LEN); + + path_extract_extension(current->path, archive->file_extension, MAX_EXT_LEN); text_input_set_header_text(text_input, "Rename:"); @@ -32,9 +37,11 @@ void archive_scene_rename_on_enter(void* context) { false); ValidatorIsFile* validator_is_file = validator_is_file_alloc_init( - archive_get_path(archive->browser), archive->file_extension, NULL); + string_get_cstr(archive->browser->path), archive->file_extension, NULL); text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); + string_clear(filename); + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); } @@ -46,30 +53,22 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) { if(event.event == SCENE_RENAME_CUSTOM_EVENT) { Storage* fs_api = furi_record_open("storage"); - string_t buffer_src; - string_t buffer_dst; - - const char* path = archive_get_path(archive->browser); - const char* name = archive_get_name(archive->browser); - - string_init_printf(buffer_src, "%s", name); - //TODO: take path from src name - string_init_printf(buffer_dst, "%s/%s", path, archive->text_store); - - // append extension + const char* path_src = archive_get_name(archive->browser); ArchiveFile_t* file = archive_get_current_file(archive->browser); - string_cat(buffer_dst, known_ext[file->type]); - storage_common_rename( - fs_api, string_get_cstr(buffer_src), string_get_cstr(buffer_dst)); + string_t path_dst; + string_init(path_dst); + path_extract_dirname(path_src, path_dst); + string_cat_printf(path_dst, "/%s%s", archive->text_store, known_ext[file->type]); + + storage_common_rename(fs_api, path_src, string_get_cstr(path_dst)); furi_record_close("storage"); if(file->fav) { - archive_favorites_rename(name, string_get_cstr(buffer_dst)); + archive_favorites_rename(path_src, string_get_cstr(path_dst)); } - string_clear(buffer_src); - string_clear(buffer_dst); + string_clear(path_dst); scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneBrowser); consumed = true; diff --git a/applications/archive/views/archive_browser_view.c b/applications/archive/views/archive_browser_view.c index fc578b36..34b875b8 100644 --- a/applications/archive/views/archive_browser_view.c +++ b/applications/archive/views/archive_browser_view.c @@ -1,3 +1,5 @@ +#include "assets_icons.h" +#include "toolbox/path.h" #include #include "../archive_i.h" #include "archive_browser_view.h" @@ -26,7 +28,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, - [ArchiveFileTypeLoading] = &I_unknown_10px, // TODO loading icon + [ArchiveFileTypeLoading] = &I_loading_10px, }; void archive_browser_set_callback( @@ -100,6 +102,22 @@ static void archive_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar, boo canvas_draw_dot(canvas, scrollbar ? 121 : 126, (15 + idx * FRAME_HEIGHT) + 11); } +static void archive_draw_loading(Canvas* canvas, ArchiveBrowserViewModel* model) { + furi_assert(model); + + uint8_t width = 49; + uint8_t height = 47; + uint8_t x = 128 / 2 - width / 2; + uint8_t y = 64 / 2 - height / 2 + 6; + + elements_bold_rounded_frame(canvas, x, y, width, height); + + canvas_set_font(canvas, FontSecondary); + elements_multiline_text(canvas, x + 7, y + 13, "Loading..."); + + canvas_draw_icon(canvas, x + 13, y + 19, &A_Loading_24); +} + static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { furi_assert(model); @@ -107,8 +125,8 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { bool scrollbar = model->item_cnt > 4; for(uint32_t i = 0; i < MIN(model->item_cnt, MENU_ITEMS); ++i) { - string_t str_buff; - char cstr_buff[MAX_NAME_LEN]; + string_t str_buf; + string_init(str_buf); int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); uint8_t x_offset = (model->move_fav && model->item_idx == idx) ? MOVE_OFFSET : 0; @@ -117,16 +135,14 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { if(archive_is_item_in_array(model, idx)) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); - strlcpy(cstr_buff, string_get_cstr(file->name), string_size(file->name) + 1); - archive_trim_file_path(cstr_buff, archive_is_known_app(file->type)); - string_init_set_str(str_buff, cstr_buff); + path_extract_filename(file->path, str_buf, archive_is_known_app(file->type)); file_type = file->type; } else { - string_init_set_str(str_buff, "---"); + string_set_str(str_buf, "---"); } elements_string_fit_width( - canvas, str_buff, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); + canvas, str_buf, (scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX) - x_offset); if(model->item_idx == idx) { archive_draw_frame(canvas, i, scrollbar, model->move_fav); @@ -135,9 +151,9 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { } canvas_draw_icon(canvas, 2 + x_offset, 16 + i * FRAME_HEIGHT, ArchiveItemIcons[file_type]); - canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buff)); + canvas_draw_str(canvas, 15 + x_offset, 24 + i * FRAME_HEIGHT, string_get_cstr(str_buf)); - string_clear(str_buff); + string_clear(str_buf); } if(scrollbar) { @@ -190,7 +206,9 @@ void archive_view_render(Canvas* canvas, void* mdl) { archive_render_status_bar(canvas, mdl); - if(model->item_cnt > 0) { + if(model->folder_loading) { + archive_draw_loading(canvas, model); + } else if(model->item_cnt > 0) { draw_list(canvas, model); } else { canvas_draw_str_aligned( @@ -350,25 +368,31 @@ ArchiveBrowserView* browser_alloc() { view_set_draw_callback(browser->view, archive_view_render); view_set_input_callback(browser->view, archive_view_input); - string_init(browser->path); + string_init_set_str(browser->path, archive_get_default_path(TAB_DEFAULT)); with_view_model( browser->view, (ArchiveBrowserViewModel * model) { files_array_init(model->files); - idx_last_array_init(model->idx_last); + model->tab_idx = TAB_DEFAULT; return true; }); + browser->worker = file_browser_worker_alloc(browser->path, "*", false); + archive_file_browser_set_callbacks(browser); + + file_browser_worker_set_callback_context(browser->worker, browser); + return browser; } void browser_free(ArchiveBrowserView* browser) { furi_assert(browser); + file_browser_worker_free(browser->worker); + with_view_model( browser->view, (ArchiveBrowserViewModel * model) { files_array_clear(model->files); - idx_last_array_clear(model->idx_last); return false; }); diff --git a/applications/archive/views/archive_browser_view.h b/applications/archive/views/archive_browser_view.h index db079d6b..2de04a16 100644 --- a/applications/archive/views/archive_browser_view.h +++ b/applications/archive/views/archive_browser_view.h @@ -8,6 +8,7 @@ #include #include "../helpers/archive_files.h" #include "../helpers/archive_favorites.h" +#include "gui/modules/file_browser_worker.h" #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 @@ -34,7 +35,7 @@ typedef enum { ArchiveBrowserEventFileMenuClose, ArchiveBrowserEventFileMenuRun, ArchiveBrowserEventFileMenuPin, - ArchiveBrowserEventFileMenuAction, + ArchiveBrowserEventFileMenuRename, ArchiveBrowserEventFileMenuDelete, ArchiveBrowserEventEnterDir, @@ -48,7 +49,7 @@ typedef enum { ArchiveBrowserEventLoadPrevItems, ArchiveBrowserEventLoadNextItems, - ArchiveBrowserEventLoaderAppExit, + ArchiveBrowserEventListRefresh, ArchiveBrowserEventExit, } ArchiveBrowserEvent; @@ -56,7 +57,7 @@ typedef enum { static const uint8_t file_menu_actions[MENU_ITEMS] = { [0] = ArchiveBrowserEventFileMenuRun, [1] = ArchiveBrowserEventFileMenuPin, - [2] = ArchiveBrowserEventFileMenuAction, + [2] = ArchiveBrowserEventFileMenuRename, [3] = ArchiveBrowserEventFileMenuDelete, }; @@ -72,23 +73,23 @@ typedef enum { struct ArchiveBrowserView { View* view; + BrowserWorker* worker; ArchiveBrowserViewCallback callback; void* context; string_t path; + InputKey last_tab_switch_dir; + bool is_root; }; -ARRAY_DEF(idx_last_array, int32_t) - typedef struct { ArchiveTabEnum tab_idx; - ArchiveTabEnum last_tab; files_array_t files; - idx_last_array_t idx_last; uint8_t menu_idx; - bool move_fav; bool menu; + bool move_fav; bool list_loading; + bool folder_loading; uint32_t item_cnt; int32_t item_idx; diff --git a/applications/bad_usb/views/bad_usb_view.c b/applications/bad_usb/views/bad_usb_view.c index 430885df..0679669f 100644 --- a/applications/bad_usb/views/bad_usb_view.c +++ b/applications/bad_usb/views/bad_usb_view.c @@ -155,7 +155,7 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { furi_assert(name); with_view_model( bad_usb->view, (BadUsbModel * model) { - strncpy(model->file_name, name, MAX_NAME_LEN); + strlcpy(model->file_name, name, MAX_NAME_LEN); return true; }); } diff --git a/applications/debug_tools/usb_test.c b/applications/debug_tools/usb_test.c index 44a17ceb..a4f42f47 100644 --- a/applications/debug_tools/usb_test.c +++ b/applications/debug_tools/usb_test.c @@ -42,8 +42,8 @@ void usb_test_submenu_callback(void* context, uint32_t index) { } else if(index == UsbTestSubmenuIndexHidWithParams) { app->hid_cfg.vid = 0x1234; app->hid_cfg.pid = 0xabcd; - strncpy(app->hid_cfg.manuf, "WEN", sizeof(app->hid_cfg.manuf)); - strncpy(app->hid_cfg.product, "FLIP", sizeof(app->hid_cfg.product)); + strlcpy(app->hid_cfg.manuf, "WEN", sizeof(app->hid_cfg.manuf)); + strlcpy(app->hid_cfg.product, "FLIP", sizeof(app->hid_cfg.product)); furi_hal_usb_set_config(&usb_hid, &app->hid_cfg); } else if(index == UsbTestSubmenuIndexHidU2F) { furi_hal_usb_set_config(&usb_hid_u2f, NULL); diff --git a/applications/gui/modules/file_browser_worker.c b/applications/gui/modules/file_browser_worker.c index 93baba00..4931cb26 100644 --- a/applications/gui/modules/file_browser_worker.c +++ b/applications/gui/modules/file_browser_worker.c @@ -22,10 +22,13 @@ typedef enum { WorkerEvtLoad = (1 << 1), WorkerEvtFolderEnter = (1 << 2), WorkerEvtFolderExit = (1 << 3), + WorkerEvtFolderRefresh = (1 << 4), + WorkerEvtConfigChange = (1 << 5), } WorkerEvtFlags; -#define WORKER_FLAGS_ALL \ - (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit) +#define WORKER_FLAGS_ALL \ + (WorkerEvtStop | WorkerEvtLoad | WorkerEvtFolderEnter | WorkerEvtFolderExit | \ + WorkerEvtFolderRefresh | WorkerEvtConfigChange) ARRAY_DEF(idx_last_array, int32_t) @@ -253,19 +256,25 @@ static int32_t browser_worker(void* context) { string_init_set_str(path, BROWSER_ROOT); browser->item_sel_idx = -1; - // If start path is a path to the file - try finding index of this file in a folder string_t filename; string_init(filename); - if(browser_path_is_file(browser->path_next)) { - path_extract_filename(browser->path_next, filename, false); - } - osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtConfigChange); while(1) { uint32_t flags = osThreadFlagsWait(WORKER_FLAGS_ALL, osFlagsWaitAny, osWaitForever); furi_assert((flags & osFlagsError) == 0); + if(flags & WorkerEvtConfigChange) { + // If start path is a path to the file - try finding index of this file in a folder + if(browser_path_is_file(browser->path_next)) { + path_extract_filename(browser->path_next, filename, false); + } + idx_last_array_reset(browser->idx_last); + + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderEnter); + } + if(flags & WorkerEvtFolderEnter) { string_set(path, browser->path_next); bool is_root = browser_folder_check_and_switch(path); @@ -304,6 +313,23 @@ static int32_t browser_worker(void* context) { } } + if(flags & WorkerEvtFolderRefresh) { + bool is_root = browser_folder_check_and_switch(path); + + int32_t file_idx = 0; + string_reset(filename); + browser_folder_init(browser, path, filename, &items_cnt, &file_idx); + FURI_LOG_D( + TAG, + "Refresh folder: %s items: %u idx: %d", + string_get_cstr(path), + items_cnt, + browser->item_sel_idx); + if(browser->folder_cb) { + browser->folder_cb(browser->cb_ctx, items_cnt, browser->item_sel_idx, is_root); + } + } + if(flags & WorkerEvtLoad) { FURI_LOG_D(TAG, "Load offset: %u cnt: %u", browser->load_offset, browser->load_count); browser_folder_load(browser, path, browser->load_offset, browser->load_count); @@ -388,6 +414,18 @@ void file_browser_worker_set_long_load_callback( browser->long_load_cb = cb; } +void file_browser_worker_set_config( + BrowserWorker* browser, + string_t path, + const char* filter_ext, + bool skip_assets) { + furi_assert(browser); + string_set(browser->path_next, path); + string_set_str(browser->filter_extension, filter_ext); + browser->skip_assets = skip_assets; + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtConfigChange); +} + void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx) { furi_assert(browser); string_set(browser->path_next, path); @@ -400,6 +438,12 @@ void file_browser_worker_folder_exit(BrowserWorker* browser) { osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderExit); } +void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx) { + furi_assert(browser); + browser->item_sel_idx = item_idx; + osThreadFlagsSet(furi_thread_get_thread_id(browser->thread), WorkerEvtFolderRefresh); +} + void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count) { furi_assert(browser); browser->load_offset = offset; diff --git a/applications/gui/modules/file_browser_worker.h b/applications/gui/modules/file_browser_worker.h index b0d360a3..18c0b481 100644 --- a/applications/gui/modules/file_browser_worker.h +++ b/applications/gui/modules/file_browser_worker.h @@ -44,10 +44,18 @@ void file_browser_worker_set_long_load_callback( BrowserWorker* browser, BrowserWorkerLongLoadCallback cb); +void file_browser_worker_set_config( + BrowserWorker* browser, + string_t path, + const char* filter_ext, + bool skip_assets); + void file_browser_worker_folder_enter(BrowserWorker* browser, string_t path, int32_t item_idx); void file_browser_worker_folder_exit(BrowserWorker* browser); +void file_browser_worker_folder_refresh(BrowserWorker* browser, int32_t item_idx); + void file_browser_worker_load(BrowserWorker* browser, uint32_t offset, uint32_t count); #ifdef __cplusplus diff --git a/lib/toolbox/path.c b/lib/toolbox/path.c index a99e57d1..c6add856 100644 --- a/lib/toolbox/path.c +++ b/lib/toolbox/path.c @@ -1,4 +1,6 @@ #include "path.h" +#include "m-string.h" +#include void path_extract_filename_no_ext(const char* path, string_t filename) { string_set(filename, path); @@ -33,6 +35,15 @@ void path_extract_filename(string_t path, string_t name, bool trim_ext) { } } +void path_extract_extension(string_t path, char* ext, size_t ext_len_max) { + size_t dot = string_search_rchar(path, '.'); + size_t filename_start = string_search_rchar(path, '/'); + + if((dot > 0) && (filename_start < dot)) { + strlcpy(ext, &(string_get_cstr(path))[dot], ext_len_max); + } +} + static inline void path_cleanup(string_t path) { string_strim(path); while(string_end_with_str_p(path, "/")) { diff --git a/lib/toolbox/path.h b/lib/toolbox/path.h index 76e501cc..5c91e3fc 100644 --- a/lib/toolbox/path.h +++ b/lib/toolbox/path.h @@ -23,6 +23,15 @@ void path_extract_filename_no_ext(const char* path, string_t filename); */ void path_extract_filename(string_t path, string_t filename, bool trim_ext); +/** + * @brief Extract file extension from path. + * + * @param path path string + * @param ext output extension string + * @param ext_len_max maximum extension string length + */ +void path_extract_extension(string_t path, char* ext, size_t ext_len_max); + /** * @brief Extract last path component *