From 761ea8045419eb023a665f46951a8c1ac6473633 Mon Sep 17 00:00:00 2001 From: Willy-JL <49810075+Willy-JL@users.noreply.github.com> Date: Mon, 3 Jul 2023 22:31:54 +0200 Subject: [PATCH] Archive: New tab for searching files --- applications/main/archive/archive.c | 13 +++ .../main/archive/helpers/archive_apps.c | 12 +- .../main/archive/helpers/archive_apps.h | 2 + .../main/archive/helpers/archive_browser.c | 17 +++ .../main/archive/helpers/archive_browser.h | 3 + .../main/archive/helpers/archive_files.h | 1 + .../archive/scenes/archive_scene_browser.c | 13 +++ .../archive/scenes/archive_scene_config.h | 1 + .../main/archive/scenes/archive_scene_info.c | 2 +- .../archive/scenes/archive_scene_search.c | 105 ++++++++++++++++++ .../main/archive/views/archive_browser_view.c | 20 +++- .../main/archive/views/archive_browser_view.h | 4 + assets/icons/Archive/search_10px.png | Bin 0 -> 600 bytes firmware/targets/f7/api_symbols.csv | 1 + 14 files changed, 187 insertions(+), 7 deletions(-) create mode 100644 applications/main/archive/scenes/archive_scene_search.c create mode 100644 assets/icons/Archive/search_10px.png diff --git a/applications/main/archive/archive.c b/applications/main/archive/archive.c index 5b7638152..2f77eb47c 100644 --- a/applications/main/archive/archive.c +++ b/applications/main/archive/archive.c @@ -51,6 +51,11 @@ static ArchiveApp* archive_alloc() { view_dispatcher, ArchiveViewStack, view_stack_get_view(archive->view_stack)); archive->browser = browser_alloc(); + with_view_model( + archive->browser->view, + ArchiveBrowserViewModel * model, + { model->archive = archive; }, + true); view_dispatcher_add_view( archive->view_dispatcher, ArchiveViewBrowser, archive_browser_get_view(archive->browser)); @@ -64,6 +69,14 @@ void archive_free(ArchiveApp* archive) { furi_assert(archive); ViewDispatcher* view_dispatcher = archive->view_dispatcher; + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneInfo, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + // Loading loading_free(archive->loading); diff --git a/applications/main/archive/helpers/archive_apps.c b/applications/main/archive/helpers/archive_apps.c index 978911072..8fdb14c79 100644 --- a/applications/main/archive/helpers/archive_apps.c +++ b/applications/main/archive/helpers/archive_apps.c @@ -5,6 +5,7 @@ static const char* known_apps[] = { [ArchiveAppTypeU2f] = "u2f", + [ArchiveAppTypeSearch] = "search", }; ArchiveAppTypeEnum archive_get_app_type(const char* path) { @@ -35,6 +36,9 @@ bool archive_app_is_available(void* context, const char* path) { res = storage_file_exists(storage, U2F_KEY_FILE) && storage_file_exists(storage, U2F_CNT_FILE); break; + case ArchiveAppTypeSearch: + res = true; + break; default: break; } @@ -48,15 +52,17 @@ bool archive_app_read_dir(void* context, const char* path) { furi_assert(path); ArchiveBrowserView* browser = context; - archive_file_array_rm_all(browser); - ArchiveAppTypeEnum app = archive_get_app_type(path); switch(app) { case ArchiveAppTypeU2f: archive_add_app_item(browser, "/app:u2f/U2F Token"); + archive_file_array_rm_all(browser); + return true; + case ArchiveAppTypeSearch: return true; default: + archive_file_array_rm_all(browser); return false; } } @@ -78,6 +84,8 @@ void archive_app_delete_file(void* context, const char* path) { archive_favorites_delete("/app:u2f/U2F Token"); } break; + case ArchiveAppTypeSearch: + break; default: break; } diff --git a/applications/main/archive/helpers/archive_apps.h b/applications/main/archive/helpers/archive_apps.h index 8bc904587..2e3535e5d 100644 --- a/applications/main/archive/helpers/archive_apps.h +++ b/applications/main/archive/helpers/archive_apps.h @@ -2,12 +2,14 @@ typedef enum { ArchiveAppTypeU2f, + ArchiveAppTypeSearch, ArchiveAppTypeUnknown, ArchiveAppsTotal, } ArchiveAppTypeEnum; static const ArchiveFileTypeEnum app_file_types[] = { [ArchiveAppTypeU2f] = ArchiveFileTypeU2f, + [ArchiveAppTypeSearch] = ArchiveFileTypeSearch, [ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 567ff9c73..c0325ff16 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -489,6 +489,18 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { furi_assert(browser); ArchiveTabEnum tab = archive_get_tab(browser); + if(tab == ArchiveTabSearch) { + ArchiveApp* archive; + with_view_model( + browser->view, ArchiveBrowserViewModel * model, { archive = model->archive; }, false); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + } + browser->last_tab_switch_dir = key; for(int i = 0; i < 2; i++) { @@ -516,6 +528,11 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { if(app_name != NULL) { if(archive_app_is_available(browser, furi_string_get_cstr(browser->path))) { tab_empty = false; + if(tab == ArchiveTabSearch) { + archive_file_array_rm_all(browser); + archive_add_app_item(browser, "/app:search/Search for files"); + archive_set_item_count(browser, 1); + } } } } else { diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 1b39e9c72..8074d2532 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadKb] = EXT_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = EXT_PATH("apps"), + [ArchiveTabSearch] = "/app:search", [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_EXT_PATH_PREFIX, }; @@ -33,6 +34,7 @@ static const char* known_ext[] = { [ArchiveFileTypeBadKb] = ".txt", [ArchiveFileTypeU2f] = "?", [ArchiveFileTypeApplication] = ".fap", + [ArchiveFileTypeSearch] = "*", [ArchiveFileTypeUpdateManifest] = ".fuf", [ArchiveFileTypeFolder] = "?", [ArchiveFileTypeUnknown] = "*", @@ -48,6 +50,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadKb] = ArchiveFileTypeBadKb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabSearch] = ArchiveFileTypeSearch, [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index eccf731c5..84a35ec30 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -21,6 +21,7 @@ typedef enum { ArchiveFileTypeBadKb, ArchiveFileTypeU2f, ArchiveFileTypeApplication, + ArchiveFileTypeSearch, ArchiveFileTypeUpdateManifest, ArchiveFileTypeFolder, ArchiveFileTypeUnknown, diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index aa95737b5..df9243f1d 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -301,6 +301,19 @@ bool archive_scene_browser_on_event(void* context, SceneManagerEvent event) { archive_enter_dir(browser, selected->path); consumed = true; break; + case ArchiveBrowserEventSearch: { + bool open = + !scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + if(archive->thread) { + furi_thread_join(archive->thread); + furi_thread_free(archive->thread); + archive->thread = NULL; + } + if(open) scene_manager_next_scene(archive->scene_manager, ArchiveAppSceneSearch); + consumed = true; + break; + } case ArchiveBrowserEventFavMoveUp: archive_file_array_swap(browser, 1); consumed = true; diff --git a/applications/main/archive/scenes/archive_scene_config.h b/applications/main/archive/scenes/archive_scene_config.h index 63631c65c..00ff4cf38 100644 --- a/applications/main/archive/scenes/archive_scene_config.h +++ b/applications/main/archive/scenes/archive_scene_config.h @@ -2,5 +2,6 @@ ADD_SCENE(archive, browser, Browser) ADD_SCENE(archive, new_dir, NewDir) ADD_SCENE(archive, rename, Rename) ADD_SCENE(archive, delete, Delete) +ADD_SCENE(archive, search, Search) ADD_SCENE(archive, info, Info) ADD_SCENE(archive, show, Show) diff --git a/applications/main/archive/scenes/archive_scene_info.c b/applications/main/archive/scenes/archive_scene_info.c index 19bbc900e..14725eaed 100644 --- a/applications/main/archive/scenes/archive_scene_info.c +++ b/applications/main/archive/scenes/archive_scene_info.c @@ -134,7 +134,7 @@ void archive_scene_info_on_enter(void* context) { if(is_dir) { scene_manager_set_scene_state(instance->scene_manager, ArchiveAppSceneInfo, true); instance->thread = furi_thread_alloc_ex( - "ArchiveDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); + "ArchiveInfoDirWalk", 1024, (FuriThreadCallback)archive_scene_info_dirwalk, instance); furi_thread_start(instance->thread); } } diff --git a/applications/main/archive/scenes/archive_scene_search.c b/applications/main/archive/scenes/archive_scene_search.c new file mode 100644 index 000000000..06f995038 --- /dev/null +++ b/applications/main/archive/scenes/archive_scene_search.c @@ -0,0 +1,105 @@ +#include "../archive_i.h" +#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" +#include + +#define TAG "Archive" + +#define SCENE_SEARCH_CUSTOM_EVENT (0UL) + +void archive_scene_search_text_input_callback(void* context) { + ArchiveApp* archive = (ArchiveApp*)context; + view_dispatcher_send_custom_event(archive->view_dispatcher, SCENE_SEARCH_CUSTOM_EVENT); +} + +void archive_scene_search_on_enter(void* context) { + ArchiveApp* archive = context; + + TextInput* text_input = archive->text_input; + strlcpy(archive->text_store, "", MAX_NAME_LEN); + text_input_set_header_text(text_input, "Search for files:"); + + text_input_set_result_callback( + text_input, + archive_scene_search_text_input_callback, + context, + archive->text_store, + MAX_NAME_LEN, + false); + + view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput); +} + +uint32_t archive_scene_search_dirwalk(void* context) { + furi_assert(context); + ArchiveApp* archive = context; + + uint32_t count = 1; + DirWalk* dir_walk = dir_walk_alloc(furi_record_open(RECORD_STORAGE)); + FuriString* path = furi_string_alloc(); + FileInfo fileinfo; + + if(dir_walk_open(dir_walk, STORAGE_EXT_PATH_PREFIX)) { + while(scene_manager_get_scene_state(archive->scene_manager, ArchiveAppSceneSearch)) { + DirWalkResult result = dir_walk_read(dir_walk, path, &fileinfo); + if(result == DirWalkError) { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + break; + } + if(result == DirWalkLast) { + break; + } + if(!file_info_is_dir(&fileinfo) && + furi_string_search_str(path, archive->text_store) != FURI_STRING_FAILURE) { + archive_add_file_item(archive->browser, false, furi_string_get_cstr(path)); + archive_set_item_count(archive->browser, ++count); + } + } + } else { + archive_add_app_item(archive->browser, "/app:search/Error while searching!"); + archive_set_item_count(archive->browser, ++count); + } + furi_string_set( + archive_get_file_at(archive->browser, 0)->path, "/app:search/Search for files"); + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, false); + + furi_string_free(path); + dir_walk_free(dir_walk); + return 0; +} + +bool archive_scene_search_on_event(void* context, SceneManagerEvent event) { + ArchiveApp* archive = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SCENE_SEARCH_CUSTOM_EVENT) { + archive_file_array_rm_all(archive->browser); + archive_add_app_item(archive->browser, "/app:search/Cancel search"); + archive_set_item_count(archive->browser, 1); + + // Thread here is fine because only the info pane uses it too, + // but only for directories, which are ignored for search + scene_manager_set_scene_state(archive->scene_manager, ArchiveAppSceneSearch, true); + archive->thread = furi_thread_alloc_ex( + "ArchiveSearchDirWalk", + 1024, + (FuriThreadCallback)archive_scene_search_dirwalk, + archive); + furi_thread_start(archive->thread); + + scene_manager_previous_scene(archive->scene_manager); + consumed = true; + } + } + return consumed; +} + +void archive_scene_search_on_exit(void* context) { + ArchiveApp* archive = context; + text_input_reset(archive->text_input); +} diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index e0caa9494..c6d595098 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadKb] = "Bad KB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabSearch] = "Search", [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; @@ -35,6 +36,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeBadKb] = &I_badkb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeApplication] = &I_Apps_10px, + [ArchiveFileTypeSearch] = &I_search_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px, [ArchiveFileTypeFolder] = &I_dir_10px, [ArchiveFileTypeUnknown] = &I_unknown_10px, @@ -197,7 +199,8 @@ static void draw_list(Canvas* canvas, ArchiveBrowserViewModel* model) { ArchiveFile_t* file = files_array_get( model->files, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); file_type = file->type; - bool ext = model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; + bool ext = strncmp(archive_get_default_path(model->tab_idx), "/app:", 5) == 0 || + model->tab_idx == ArchiveTabBrowser || model->tab_idx == ArchiveTabInternal; if(file_type == ArchiveFileTypeApplication) { if(file->custom_icon_data) { custom_icon_data = file->custom_icon_data; @@ -261,6 +264,10 @@ static void archive_render_status_bar(Canvas* canvas, ArchiveBrowserViewModel* m furi_assert(model); const char* tab_name = ArchiveTabNames[model->tab_idx]; + if(model->tab_idx == ArchiveTabSearch && + scene_manager_get_scene_state(model->archive->scene_manager, ArchiveAppSceneSearch)) { + tab_name = "Searching"; + } bool clip = model->clipboard != NULL; canvas_draw_icon(canvas, 0, 0, &I_Background_128x11); @@ -354,6 +361,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserView* browser = context; bool in_menu; + int32_t cur_item_idx; bool move_fav_mode; bool is_loading; with_view_model( @@ -361,6 +369,7 @@ static bool archive_view_input(InputEvent* event, void* context) { ArchiveBrowserViewModel * model, { in_menu = model->menu; + cur_item_idx = model->item_idx; move_fav_mode = model->move_fav; is_loading = model->folder_loading || model->list_loading; }, @@ -492,9 +501,12 @@ static bool archive_view_input(InputEvent* event, void* context) { archive_update_offset(browser); } - if(event->key == InputKeyOk) { - ArchiveFile_t* selected = archive_get_current_file(browser); - + ArchiveFile_t* selected = archive_get_current_file(browser); + if(selected && selected->type == ArchiveFileTypeSearch) { + if(event->key == InputKeyOk && event->type == InputTypeShort && cur_item_idx == 0) { + browser->callback(ArchiveBrowserEventSearch, browser->context); + } + } else if(event->key == InputKeyOk) { if(selected) { bool favorites = archive_get_tab(browser) == ArchiveTabFavorites; bool folder = selected->type == ArchiveFileTypeFolder; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 43322eaf8..f87d59a2b 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -30,6 +30,7 @@ typedef enum { ArchiveTabBadKb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabSearch, ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, @@ -53,6 +54,8 @@ typedef enum { ArchiveBrowserEventEnterDir, + ArchiveBrowserEventSearch, + ArchiveBrowserEventFavMoveUp, ArchiveBrowserEventFavMoveDown, ArchiveBrowserEventEnterFavMove, @@ -90,6 +93,7 @@ struct ArchiveBrowserView { }; typedef struct { + ArchiveApp* archive; ArchiveTabEnum tab_idx; files_array_t files; diff --git a/assets/icons/Archive/search_10px.png b/assets/icons/Archive/search_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..6b521107454124c8299cd1f986bacd83f5303fa6 GIT binary patch literal 600 zcmV-e0;m0nP)EX>4Tx04R}tkv&MmKpe$i6Qv@R4t5X`$xxl_q9Ts93Pq?;YK2xEOfLO`CJjl7 zi=*ILaPVWX>fqw6tAnc`2!4RLx;QDiNQwVT3N2zhIPS;0dyl(!fY7Wm&FYB*nr@q! zWJ1d2SEb-9f)EkLC`6fA##}m`g75gcM}V()F`ngr?$0rx7Ayt?B;q(TOq+OvcxKZ! zIPVijSXowy&xt2Yx*+i**ACxG}2TR^4>lKOlcDPX000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0000( zNkl<1qlINi7Cq@!sM90000< literal 0 HcmV?d00001 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 97fa8b27b..dc24b0d91 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -3466,6 +3466,7 @@ Variable,+,I_passport_DB,Icon, Variable,+,I_passport_bad_46x49,Icon, Variable,+,I_passport_happy_46x49,Icon, Variable,+,I_passport_okay_46x49,Icon, +Variable,+,I_search_10px,Icon, Variable,+,I_sub1_10px,Icon, Variable,+,I_subplaylist_10px,Icon, Variable,+,I_subrem_10px,Icon,