mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-07-23 01:18:12 -07:00
Archive: New tab for searching files
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
typedef enum {
|
||||
ArchiveAppTypeU2f,
|
||||
ArchiveAppTypeSearch,
|
||||
ArchiveAppTypeUnknown,
|
||||
ArchiveAppsTotal,
|
||||
} ArchiveAppTypeEnum;
|
||||
|
||||
static const ArchiveFileTypeEnum app_file_types[] = {
|
||||
[ArchiveAppTypeU2f] = ArchiveFileTypeU2f,
|
||||
[ArchiveAppTypeSearch] = ArchiveFileTypeSearch,
|
||||
[ArchiveAppTypeUnknown] = ArchiveFileTypeUnknown,
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ typedef enum {
|
||||
ArchiveFileTypeBadKb,
|
||||
ArchiveFileTypeU2f,
|
||||
ArchiveFileTypeApplication,
|
||||
ArchiveFileTypeSearch,
|
||||
ArchiveFileTypeUpdateManifest,
|
||||
ArchiveFileTypeFolder,
|
||||
ArchiveFileTypeUnknown,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <dialogs/dialogs.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 600 B |
@@ -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,
|
||||
|
||||
|
Reference in New Issue
Block a user