소스 검색

[FL-1300] iButton app: save selected menu item and selected file position. (#489)

* 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: あく <alleteam@gmail.com>
SG 4 년 전
부모
커밋
fc5c48edef

+ 86 - 0
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);
+    }
 }

+ 1 - 0
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
 }

+ 36 - 0
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) {

+ 22 - 6
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
 }

+ 2 - 0
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);

+ 1 - 0
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;
 };

+ 2 - 0
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);

+ 1 - 0
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;
 };

+ 4 - 2
applications/ibutton/scene/ibutton-scene-saved-key-menu.cpp

@@ -5,8 +5,8 @@
 #include <callback-connector.h>
 
 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);

+ 1 - 0
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;
 };

+ 2 - 1
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) {

+ 2 - 0
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);

+ 1 - 0
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;
 };

+ 16 - 9
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 {

+ 2 - 1
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;