From fc5c48edef0e091b23402cb6709fcc25c85c0cee Mon Sep 17 00:00:00 2001 From: SG Date: Tue, 25 May 2021 21:04:22 +1000 Subject: [PATCH] [FL-1300] iButton app: save selected menu item and selected file position. (#489) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GUI module submenu: fix documentation * GUI module submenu: add submenu_set_selected_item fn * App iButton: use submenu_set_selected_item to store and set selected item in submenu * App iButton: swap write and emulate in "saved key menu" scene * App iButton: file select can now switch to the previous selected file * App iButton: swap write and emulate indexes in "saved key menu" scene Co-authored-by: あく --- applications/gui/modules/file_select.c | 86 +++++++++++++++++++ applications/gui/modules/file_select.h | 1 + applications/gui/modules/submenu.c | 36 ++++++++ applications/gui/modules/submenu.h | 28 ++++-- .../ibutton/scene/ibutton-scene-add-type.cpp | 2 + .../ibutton/scene/ibutton-scene-add-type.h | 1 + .../scene/ibutton-scene-readed-key-menu.cpp | 2 + .../scene/ibutton-scene-readed-key-menu.h | 1 + .../scene/ibutton-scene-saved-key-menu.cpp | 6 +- .../scene/ibutton-scene-saved-key-menu.h | 1 + .../scene/ibutton-scene-select-key.cpp | 3 +- .../ibutton/scene/ibutton-scene-start.cpp | 2 + .../ibutton/scene/ibutton-scene-start.h | 1 + applications/sd-filesystem/sd-filesystem.c | 25 ++++-- lib/common-api/sd-card-api.h | 3 +- 15 files changed, 179 insertions(+), 19 deletions(-) diff --git a/applications/gui/modules/file_select.c b/applications/gui/modules/file_select.c index 2c2894577..32319b04c 100644 --- a/applications/gui/modules/file_select.c +++ b/applications/gui/modules/file_select.c @@ -387,4 +387,90 @@ bool file_select_fill_count(FileSelect* file_select) { dir_api->close(&directory); free(name); return true; +} + +void file_select_set_selected_file_internal(FileSelect* file_select, const char* filename) { + furi_assert(file_select); + furi_assert(filename); + furi_assert(file_select->fs_api); + furi_assert(file_select->path); + furi_assert(file_select->extension); + + if(strlen(filename) == 0) return; + + FileInfo file_info; + File directory; + bool result; + FS_Dir_Api* dir_api = &file_select->fs_api->dir; + const uint8_t name_length = 100; + char* name = calloc(name_length, sizeof(char)); + + uint16_t file_position = 0; + + if(name == NULL) { + return; + } + + result = dir_api->open(&directory, file_select->path); + + if(!result) { + dir_api->close(&directory); + free(name); + return; + } + + while(1) { + result = dir_api->read(&directory, &file_info, name, name_length); + + if(directory.error_id == FSE_NOT_EXIST || name[0] == 0) { + break; + } + + if(result) { + if(directory.error_id == FSE_OK) { + if(filter_file(file_select, &file_info, name)) { + if(strcmp(filename, name) == 0) { + break; + } + + file_position++; + } + } else { + dir_api->close(&directory); + free(name); + return; + } + } + } + + with_view_model( + file_select->view, (FileSelectModel * model) { + uint16_t max_first_file_index = + model->file_count > FILENAME_COUNT ? model->file_count - FILENAME_COUNT : 0; + + model->first_file_index = file_position; + + if(model->first_file_index > 0) { + model->first_file_index -= 1; + } + + if(model->first_file_index >= max_first_file_index) { + model->first_file_index = max_first_file_index; + } + + model->position = file_position - model->first_file_index; + + return true; + }); + + dir_api->close(&directory); + free(name); +} + +void file_select_set_selected_file(FileSelect* file_select, const char* filename) { + file_select_set_selected_file_internal(file_select, filename); + + if(!file_select_fill_strings(file_select)) { + file_select->callback(false, file_select->context); + } } \ No newline at end of file diff --git a/applications/gui/modules/file_select.h b/applications/gui/modules/file_select.h index 8f52771ae..26a64cd68 100644 --- a/applications/gui/modules/file_select.h +++ b/applications/gui/modules/file_select.h @@ -20,6 +20,7 @@ void file_select_set_callback(FileSelect* file_select, FileSelectCallback callba void file_select_set_filter(FileSelect* file_select, const char* path, const char* extension); void file_select_set_result_buffer(FileSelect* file_select, char* buffer, uint8_t buffer_size); bool file_select_init(FileSelect* file_select); +void file_select_set_selected_file(FileSelect* file_select, const char* filename); #ifdef __cplusplus } diff --git a/applications/gui/modules/submenu.c b/applications/gui/modules/submenu.c index c2623dc79..75ae96c12 100644 --- a/applications/gui/modules/submenu.c +++ b/applications/gui/modules/submenu.c @@ -168,6 +168,42 @@ void submenu_clean(Submenu* submenu) { }); } +void submenu_set_selected_item(Submenu* submenu, uint32_t index) { + with_view_model( + submenu->view, (SubmenuModel * model) { + uint32_t position = 0; + SubmenuItemArray_it_t it; + for(SubmenuItemArray_it(it, model->items); !SubmenuItemArray_end_p(it); + SubmenuItemArray_next(it)) { + if(index == SubmenuItemArray_cref(it)->index) { + break; + } + position++; + } + + if(position >= SubmenuItemArray_size(model->items)) { + position = 0; + } + + model->position = position; + model->window_position = position; + + if(model->window_position > 0) { + model->window_position -= 1; + } + + if(SubmenuItemArray_size(model->items) <= 4) { + model->window_position = 0; + } else { + if(model->window_position >= (SubmenuItemArray_size(model->items) - 4)) { + model->window_position = (SubmenuItemArray_size(model->items) - 4); + } + } + + return true; + }); +} + void submenu_process_up(Submenu* submenu) { with_view_model( submenu->view, (SubmenuModel * model) { diff --git a/applications/gui/modules/submenu.h b/applications/gui/modules/submenu.h index cf963731e..7350d2577 100644 --- a/applications/gui/modules/submenu.h +++ b/applications/gui/modules/submenu.h @@ -10,23 +10,33 @@ typedef struct Submenu Submenu; typedef struct SubmenuItem SubmenuItem; typedef void (*SubmenuItemCallback)(void* context, uint32_t index); -/* Allocate and initialize submenu +/** + * @brief Allocate and initialize submenu * This submenu is used to select one option */ Submenu* submenu_alloc(); -/* Deinitialize and free submenu +/** + * @brief Allocate and initialize submenu for vertical display + * This submenu is used to select one option + */ +Submenu* submenu_vertical_alloc(); + +/** + * @brief Deinitialize and free submenu * @param submenu - Submenu instance */ void submenu_free(Submenu* submenu); -/* Get submenu view +/** + * @brief Get submenu view * @param submenu - Submenu instance * @return View instance that can be used for embedding */ View* submenu_get_view(Submenu* submenu); -/* Add item to submenu +/** + * @brief Add item to submenu * @param submenu - Submenu instance * @param label - menu item label * @param index - menu item index, used for callback, may be the same with other items @@ -41,12 +51,18 @@ SubmenuItem* submenu_add_item( SubmenuItemCallback callback, void* callback_context); -/* Remove all items from submenu +/** + * @brief Remove all items from submenu * @param submenu - Submenu instance */ void submenu_clean(Submenu* submenu); -Submenu* submenu_vertical_alloc(); +/** + * @brief Set submenu item selector + * @param submenu + * @param index + */ +void submenu_set_selected_item(Submenu* submenu, uint32_t index); #ifdef __cplusplus } diff --git a/applications/ibutton/scene/ibutton-scene-add-type.cpp b/applications/ibutton/scene/ibutton-scene-add-type.cpp index be875d3c7..fbac366b2 100644 --- a/applications/ibutton/scene/ibutton-scene-add-type.cpp +++ b/applications/ibutton/scene/ibutton-scene-add-type.cpp @@ -18,6 +18,7 @@ void iButtonSceneAddType::on_enter(iButtonApp* app) { submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, callback, app); submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, callback, app); submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); } @@ -26,6 +27,7 @@ bool iButtonSceneAddType::on_event(iButtonApp* app, iButtonEvent* event) { bool consumed = false; if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexCyfral: app->get_key()->set_type(iButtonKeyType::KeyCyfral); diff --git a/applications/ibutton/scene/ibutton-scene-add-type.h b/applications/ibutton/scene/ibutton-scene-add-type.h index b442ba8a9..0435b5074 100644 --- a/applications/ibutton/scene/ibutton-scene-add-type.h +++ b/applications/ibutton/scene/ibutton-scene-add-type.h @@ -9,4 +9,5 @@ public: private: void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; }; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp b/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp index 8a562be9f..09f114eae 100644 --- a/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp +++ b/applications/ibutton/scene/ibutton-scene-readed-key-menu.cpp @@ -20,6 +20,7 @@ void iButtonSceneReadedKeyMenu::on_enter(iButtonApp* app) { submenu_add_item(submenu, "Name and save", SubmenuIndexNameAndSave, callback, app); submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); submenu_add_item(submenu, "Read new key", SubmenuIndexReadNewKey, callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); } @@ -28,6 +29,7 @@ bool iButtonSceneReadedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { bool consumed = false; if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexWrite: app->switch_to_next_scene(iButtonApp::Scene::SceneWrite); diff --git a/applications/ibutton/scene/ibutton-scene-readed-key-menu.h b/applications/ibutton/scene/ibutton-scene-readed-key-menu.h index b33d1869b..0d80f1b36 100644 --- a/applications/ibutton/scene/ibutton-scene-readed-key-menu.h +++ b/applications/ibutton/scene/ibutton-scene-readed-key-menu.h @@ -9,4 +9,5 @@ public: private: void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; }; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp b/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp index c4486198c..05596caf7 100644 --- a/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp +++ b/applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp @@ -5,8 +5,8 @@ #include typedef enum { - SubmenuIndexWrite, SubmenuIndexEmulate, + SubmenuIndexWrite, SubmenuIndexEdit, SubmenuIndexDelete, SubmenuIndexInfo, @@ -17,11 +17,12 @@ void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) { Submenu* submenu = view_manager->get_submenu(); auto callback = cbc::obtain_connector(this, &iButtonSceneSavedKeyMenu::submenu_callback); - submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, callback, app); + submenu_add_item(submenu, "Write", SubmenuIndexWrite, callback, app); submenu_add_item(submenu, "Edit", SubmenuIndexEdit, callback, app); submenu_add_item(submenu, "Delete", SubmenuIndexDelete, callback, app); submenu_add_item(submenu, "Info", SubmenuIndexInfo, callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); } @@ -30,6 +31,7 @@ bool iButtonSceneSavedKeyMenu::on_event(iButtonApp* app, iButtonEvent* event) { bool consumed = false; if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexWrite: app->switch_to_next_scene(iButtonApp::Scene::SceneWrite); diff --git a/applications/ibutton/scene/ibutton-scene-saved-key-menu.h b/applications/ibutton/scene/ibutton-scene-saved-key-menu.h index fc86d3161..97104d394 100644 --- a/applications/ibutton/scene/ibutton-scene-saved-key-menu.h +++ b/applications/ibutton/scene/ibutton-scene-saved-key-menu.h @@ -9,4 +9,5 @@ public: private: void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; }; \ No newline at end of file diff --git a/applications/ibutton/scene/ibutton-scene-select-key.cpp b/applications/ibutton/scene/ibutton-scene-select-key.cpp index dffc7684d..69b33ab8c 100644 --- a/applications/ibutton/scene/ibutton-scene-select-key.cpp +++ b/applications/ibutton/scene/ibutton-scene-select-key.cpp @@ -10,7 +10,8 @@ void iButtonSceneSelectKey::on_enter(iButtonApp* app) { "ibutton", "*", app->get_file_name(), - app->get_file_name_size()); + app->get_file_name_size(), + app->get_key()->get_name()); // Process file_select return if(res) { diff --git a/applications/ibutton/scene/ibutton-scene-start.cpp b/applications/ibutton/scene/ibutton-scene-start.cpp index bbeedbd9d..168ace47a 100644 --- a/applications/ibutton/scene/ibutton-scene-start.cpp +++ b/applications/ibutton/scene/ibutton-scene-start.cpp @@ -19,6 +19,7 @@ void iButtonSceneStart::on_enter(iButtonApp* app) { submenu_add_item(submenu, "Read", SubmenuIndexRead, callback, app); submenu_add_item(submenu, "Saved", SubmenuIndexSaved, callback, app); submenu_add_item(submenu, "Add manually", SubmenuIndexAdd, callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu); } @@ -27,6 +28,7 @@ bool iButtonSceneStart::on_event(iButtonApp* app, iButtonEvent* event) { bool consumed = false; if(event->type == iButtonEvent::Type::EventTypeMenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexRead: app->switch_to_next_scene(iButtonApp::Scene::SceneRead); diff --git a/applications/ibutton/scene/ibutton-scene-start.h b/applications/ibutton/scene/ibutton-scene-start.h index 8652b2e0e..3e273f9a7 100644 --- a/applications/ibutton/scene/ibutton-scene-start.h +++ b/applications/ibutton/scene/ibutton-scene-start.h @@ -9,4 +9,5 @@ public: private: void submenu_callback(void* context, uint32_t index); + uint32_t submenu_item_selected = 0; }; \ No newline at end of file diff --git a/applications/sd-filesystem/sd-filesystem.c b/applications/sd-filesystem/sd-filesystem.c index 4bf9f1dda..2ebdadca1 100644 --- a/applications/sd-filesystem/sd-filesystem.c +++ b/applications/sd-filesystem/sd-filesystem.c @@ -41,6 +41,7 @@ typedef struct { const char* extension; char* result; uint8_t result_size; + char* selected_filename; } SdAppFileSelectData; typedef struct { @@ -60,7 +61,8 @@ bool sd_api_file_select( const char* path, const char* extension, char* result, - uint8_t result_size); + uint8_t result_size, + char* selected_filename); void sd_api_check_error(SdApp* sd_app); /******************* Allocators *******************/ @@ -427,7 +429,8 @@ bool sd_api_file_select( const char* path, const char* extension, char* result, - uint8_t result_size) { + uint8_t result_size, + char* selected_filename) { bool retval = false; SdAppEvent message = { @@ -437,7 +440,9 @@ bool sd_api_file_select( .path = path, .extension = extension, .result = result, - .result_size = result_size}}}; + .result_size = result_size, + .selected_filename = selected_filename, + }}}; furi_check(osMessageQueuePut(sd_app->event_queue, &message, 0, osWaitForever) == osOK); @@ -859,15 +864,13 @@ int32_t sd_filesystem(void* p) { } if(try_to_alloc_view_holder(sd_app, gui)) { FileSelect* file_select = alloc_and_attach_file_select(sd_app); + SdAppFileSelectData* file_select_data = &event.payload.file_select_data; + file_select_set_api(file_select, fs_api); file_select_set_filter( - file_select, - event.payload.file_select_data.path, - event.payload.file_select_data.extension); + file_select, file_select_data->path, file_select_data->extension); file_select_set_result_buffer( - file_select, - event.payload.file_select_data.result, - event.payload.file_select_data.result_size); + file_select, file_select_data->result, file_select_data->result_size); if(!file_select_init(file_select)) { SdAppFileSelectResultEvent retval = {.result = false}; furi_check( @@ -876,6 +879,10 @@ int32_t sd_filesystem(void* p) { app_reset_state(sd_app); } else { sd_app->sd_app_state = SdAppStateFileSelect; + if(file_select_data->selected_filename != NULL) { + file_select_set_selected_file( + file_select, file_select_data->selected_filename); + } view_holder_start(sd_app->view_holder); } } else { diff --git a/lib/common-api/sd-card-api.h b/lib/common-api/sd-card-api.h index 61589088f..d7488ffaf 100644 --- a/lib/common-api/sd-card-api.h +++ b/lib/common-api/sd-card-api.h @@ -14,7 +14,8 @@ typedef struct { const char* path, const char* extension, char* result, - uint8_t result_size); + uint8_t result_size, + char* selected_filename); void (*check_error)(SdApp* context); } SdCard_Api;