Forráskód Böngészése

[FL-2212] File validators and archive fixes #972

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Nikolay Minaylov 4 éve
szülő
commit
6264ee8c3b

+ 1 - 0
applications/archive/archive_i.h

@@ -25,4 +25,5 @@ struct ArchiveApp {
     ArchiveBrowserView* browser;
     ArchiveBrowserView* browser;
     TextInput* text_input;
     TextInput* text_input;
     char text_store[MAX_NAME_LEN];
     char text_store[MAX_NAME_LEN];
+    char file_extension[MAX_EXT_LEN + 1];
 };
 };

+ 0 - 1
applications/archive/helpers/archive_browser.c

@@ -272,7 +272,6 @@ void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
     with_view_model(
     with_view_model(
         browser->view, (ArchiveBrowserViewModel * model) {
         browser->view, (ArchiveBrowserViewModel * model) {
             model->last_idx = model->idx;
             model->last_idx = model->idx;
-            model->last_offset = model->list_offset;
             model->idx = 0;
             model->idx = 0;
             model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
             model->depth = CLAMP(model->depth + 1, MAX_DEPTH, 0);
             return false;
             return false;

+ 48 - 1
applications/archive/helpers/archive_favorites.c

@@ -31,6 +31,40 @@ uint16_t archive_favorites_count(void* context) {
     return lines;
     return lines;
 }
 }
 
 
+static bool archive_favourites_rescan() {
+    string_t buffer;
+    string_init(buffer);
+    FileWorker* file_worker = file_worker_alloc(true);
+
+    bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
+    if(result) {
+        while(1) {
+            if(!file_worker_read_until(file_worker, buffer, '\n')) {
+                break;
+            }
+            if(!string_size(buffer)) {
+                break;
+            }
+
+            bool file_exists = false;
+            file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
+            if(file_exists) {
+                archive_file_append(ARCHIVE_FAV_TEMP_PATH, "%s\n", string_get_cstr(buffer));
+            }
+        }
+    }
+
+    string_clear(buffer);
+
+    file_worker_close(file_worker);
+    file_worker_remove(file_worker, ARCHIVE_FAV_PATH);
+    file_worker_rename(file_worker, ARCHIVE_FAV_TEMP_PATH, ARCHIVE_FAV_PATH);
+
+    file_worker_free(file_worker);
+
+    return result;
+}
+
 bool archive_favorites_read(void* context) {
 bool archive_favorites_read(void* context) {
     furi_assert(context);
     furi_assert(context);
 
 
@@ -41,6 +75,8 @@ bool archive_favorites_read(void* context) {
     FileInfo file_info;
     FileInfo file_info;
     string_init(buffer);
     string_init(buffer);
 
 
+    bool need_refresh = false;
+
     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
     bool result = file_worker_open(file_worker, ARCHIVE_FAV_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
 
 
     if(result) {
     if(result) {
@@ -52,13 +88,24 @@ bool archive_favorites_read(void* context) {
                 break;
                 break;
             }
             }
 
 
-            archive_add_item(browser, &file_info, string_get_cstr(buffer));
+            bool file_exists = false;
+            file_worker_is_file_exist(file_worker, string_get_cstr(buffer), &file_exists);
+
+            if(file_exists)
+                archive_add_item(browser, &file_info, string_get_cstr(buffer));
+            else
+                need_refresh = true;
             string_reset(buffer);
             string_reset(buffer);
         }
         }
     }
     }
     string_clear(buffer);
     string_clear(buffer);
     file_worker_close(file_worker);
     file_worker_close(file_worker);
     file_worker_free(file_worker);
     file_worker_free(file_worker);
+
+    if(need_refresh) {
+        archive_favourites_rescan();
+    }
+
     return result;
     return result;
 }
 }
 
 

+ 8 - 0
applications/archive/helpers/archive_files.c

@@ -30,6 +30,14 @@ void archive_trim_file_path(char* name, bool ext) {
     }
     }
 }
 }
 
 
+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 set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
 void set_file_type(ArchiveFile_t* file, FileInfo* file_info) {
     furi_assert(file);
     furi_assert(file);
     furi_assert(file_info);
     furi_assert(file_info);

+ 1 - 0
applications/archive/helpers/archive_files.h

@@ -50,6 +50,7 @@ ARRAY_DEF(
 bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
 bool filter_by_extension(FileInfo* file_info, const char* tab_ext, const char* name);
 void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
 void set_file_type(ArchiveFile_t* file, FileInfo* file_info);
 void archive_trim_file_path(char* name, bool ext);
 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);
 bool archive_get_filenames(void* context, const char* path);
 bool archive_dir_empty(void* context, const char* path);
 bool archive_dir_empty(void* context, const char* path);
 bool archive_read_dir(void* context, const char* path);
 bool archive_read_dir(void* context, const char* path);

+ 10 - 0
applications/archive/scenes/archive_scene_rename.c

@@ -18,6 +18,7 @@ void archive_scene_rename_on_enter(void* context) {
     ArchiveFile_t* current = archive_get_current_file(archive->browser);
     ArchiveFile_t* current = archive_get_current_file(archive->browser);
     strlcpy(archive->text_store, string_get_cstr(current->name), MAX_NAME_LEN);
     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);
     archive_trim_file_path(archive->text_store, true);
 
 
     text_input_set_header_text(text_input, "Rename:");
     text_input_set_header_text(text_input, "Rename:");
@@ -30,6 +31,10 @@ void archive_scene_rename_on_enter(void* context) {
         MAX_TEXT_INPUT_LEN,
         MAX_TEXT_INPUT_LEN,
         false);
         false);
 
 
+    ValidatorIsFile* validator_is_file =
+        validator_is_file_alloc_init(archive_get_path(archive->browser), archive->file_extension);
+    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
     view_dispatcher_switch_to_view(archive->view_dispatcher, ArchiveViewTextInput);
 }
 }
 
 
@@ -74,6 +79,11 @@ bool archive_scene_rename_on_event(void* context, SceneManagerEvent event) {
 
 
 void archive_scene_rename_on_exit(void* context) {
 void archive_scene_rename_on_exit(void* context) {
     ArchiveApp* archive = (ArchiveApp*)context;
     ArchiveApp* archive = (ArchiveApp*)context;
+
     // Clear view
     // Clear view
+    void* validator_context = text_input_get_validator_callback_context(archive->text_input);
+    text_input_set_validator(archive->text_input, NULL, NULL);
+    validator_is_file_free(validator_context);
+
     text_input_reset(archive->text_input);
     text_input_reset(archive->text_input);
 }
 }

+ 1 - 0
applications/archive/views/archive_browser_view.h

@@ -11,6 +11,7 @@
 
 
 #define MAX_LEN_PX 110
 #define MAX_LEN_PX 110
 #define MAX_NAME_LEN 255
 #define MAX_NAME_LEN 255
+#define MAX_EXT_LEN 6
 #define FRAME_HEIGHT 12
 #define FRAME_HEIGHT 12
 #define MENU_ITEMS 4
 #define MENU_ITEMS 4
 #define MAX_DEPTH 32
 #define MAX_DEPTH 32

+ 6 - 2
applications/desktop/scenes/desktop_scene_lock_menu.c

@@ -18,6 +18,9 @@ void desktop_scene_lock_menu_on_enter(void* context) {
 
 
     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
     desktop_lock_menu_set_callback(desktop->lock_menu, desktop_scene_lock_menu_callback, desktop);
     desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0);
     desktop_lock_menu_pin_set(desktop->lock_menu, desktop->settings.pincode.length > 0);
+
+    uint8_t idx = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu);
+    desktop_lock_menu_set_idx(desktop->lock_menu, idx);
     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
     view_dispatcher_switch_to_view(desktop->view_dispatcher, DesktopViewLockMenu);
 }
 }
 
 
@@ -30,6 +33,7 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
         case DesktopLockMenuEventLock:
         case DesktopLockMenuEventLock:
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedNoPin);
                 desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedNoPin);
+            scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
             scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
             consumed = true;
             consumed = true;
             break;
             break;
@@ -39,12 +43,14 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
                     desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin);
                     desktop->scene_manager, DesktopSceneMain, DesktopMainSceneStateLockedWithPin);
                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
                 scene_manager_next_scene(desktop->scene_manager, DesktopSceneMain);
             } else {
             } else {
+                scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1);
                 scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup);
                 scene_manager_next_scene(desktop->scene_manager, DesktopScenePinSetup);
             }
             }
 
 
             consumed = true;
             consumed = true;
             break;
             break;
         case DesktopLockMenuEventExit:
         case DesktopLockMenuEventExit:
+            scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0);
             scene_manager_search_and_switch_to_previous_scene(
             scene_manager_search_and_switch_to_previous_scene(
                 desktop->scene_manager, DesktopSceneMain);
                 desktop->scene_manager, DesktopSceneMain);
             consumed = true;
             consumed = true;
@@ -57,6 +63,4 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) {
 }
 }
 
 
 void desktop_scene_lock_menu_on_exit(void* context) {
 void desktop_scene_lock_menu_on_exit(void* context) {
-    Desktop* desktop = (Desktop*)context;
-    desktop_lock_menu_reset_idx(desktop->lock_menu);
 }
 }

+ 12 - 8
applications/desktop/views/desktop_lock_menu.c

@@ -4,6 +4,8 @@
 #include "../desktop_i.h"
 #include "../desktop_i.h"
 #include "desktop_lock_menu.h"
 #include "desktop_lock_menu.h"
 
 
+#define LOCK_MENU_ITEMS_NB 3
+
 void desktop_lock_menu_set_callback(
 void desktop_lock_menu_set_callback(
     DesktopLockMenuView* lock_menu,
     DesktopLockMenuView* lock_menu,
     DesktopLockMenuViewCallback callback,
     DesktopLockMenuViewCallback callback,
@@ -22,10 +24,11 @@ void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set)
         });
         });
 }
 }
 
 
-void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu) {
+void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx) {
+    furi_assert(idx < LOCK_MENU_ITEMS_NB);
     with_view_model(
     with_view_model(
         lock_menu->view, (DesktopLockMenuViewModel * model) {
         lock_menu->view, (DesktopLockMenuViewModel * model) {
-            model->idx = 0;
+            model->idx = idx;
             return true;
             return true;
         });
         });
 }
 }
@@ -51,7 +54,7 @@ static void lock_menu_callback(void* context, uint8_t index) {
 }
 }
 
 
 void desktop_lock_menu_render(Canvas* canvas, void* model) {
 void desktop_lock_menu_render(Canvas* canvas, void* model) {
-    const char* Lockmenu_Items[3] = {"Lock", "Lock with PIN", "DUMB mode"};
+    const char* Lockmenu_Items[LOCK_MENU_ITEMS_NB] = {"Lock", "Lock with PIN", "DUMB mode"};
 
 
     DesktopLockMenuViewModel* m = model;
     DesktopLockMenuViewModel* m = model;
     canvas_clear(canvas);
     canvas_clear(canvas);
@@ -60,14 +63,15 @@ void desktop_lock_menu_render(Canvas* canvas, void* model) {
     canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55);
     canvas_draw_icon(canvas, 116, 0 + STATUS_BAR_Y_SHIFT, &I_DoorRight_70x55);
     canvas_set_font(canvas, FontSecondary);
     canvas_set_font(canvas, FontSecondary);
 
 
-    for(uint8_t i = 0; i < 3; ++i) {
+    for(uint8_t i = 0; i < LOCK_MENU_ITEMS_NB; ++i) {
         const char* str = Lockmenu_Items[i];
         const char* str = Lockmenu_Items[i];
 
 
         if(i == 1 && !m->pin_set) str = "Set PIN";
         if(i == 1 && !m->pin_set) str = "Set PIN";
         if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented";
         if(m->hint_timeout && m->idx == 2 && m->idx == i) str = "Not implemented";
 
 
-        canvas_draw_str_aligned(
-            canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str);
+        if(str != NULL)
+            canvas_draw_str_aligned(
+                canvas, 64, 9 + (i * 17) + STATUS_BAR_Y_SHIFT, AlignCenter, AlignCenter, str);
 
 
         if(m->idx == i) elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15);
         if(m->idx == i) elements_frame(canvas, 15, 1 + (i * 17) + STATUS_BAR_Y_SHIFT, 98, 15);
     }
     }
@@ -90,9 +94,9 @@ bool desktop_lock_menu_input(InputEvent* event, void* context) {
         lock_menu->view, (DesktopLockMenuViewModel * model) {
         lock_menu->view, (DesktopLockMenuViewModel * model) {
             model->hint_timeout = 0; // clear hint timeout
             model->hint_timeout = 0; // clear hint timeout
             if(event->key == InputKeyUp) {
             if(event->key == InputKeyUp) {
-                model->idx = CLAMP(model->idx - 1, 2, 0);
+                model->idx = CLAMP(model->idx - 1, LOCK_MENU_ITEMS_NB - 1, 0);
             } else if(event->key == InputKeyDown) {
             } else if(event->key == InputKeyDown) {
-                model->idx = CLAMP(model->idx + 1, 2, 0);
+                model->idx = CLAMP(model->idx + 1, LOCK_MENU_ITEMS_NB - 1, 0);
             }
             }
             idx = model->idx;
             idx = model->idx;
             return true;
             return true;

+ 1 - 1
applications/desktop/views/desktop_lock_menu.h

@@ -28,6 +28,6 @@ void desktop_lock_menu_set_callback(
 
 
 View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
 View* desktop_lock_menu_get_view(DesktopLockMenuView* lock_menu);
 void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set);
 void desktop_lock_menu_pin_set(DesktopLockMenuView* lock_menu, bool pin_is_set);
-void desktop_lock_menu_reset_idx(DesktopLockMenuView* lock_menu);
+void desktop_lock_menu_set_idx(DesktopLockMenuView* lock_menu, uint8_t idx);
 DesktopLockMenuView* desktop_lock_menu_alloc();
 DesktopLockMenuView* desktop_lock_menu_alloc();
 void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);
 void desktop_lock_menu_free(DesktopLockMenuView* lock_menu);

+ 4 - 4
applications/ibutton/ibutton_app.h

@@ -64,6 +64,10 @@ public:
         SceneAddValue,
         SceneAddValue,
     };
     };
 
 
+    static const char* app_folder;
+    static const char* app_extension;
+    static const char* app_filetype;
+
     iButtonAppViewManager* get_view_manager();
     iButtonAppViewManager* get_view_manager();
     void switch_to_next_scene(Scene index);
     void switch_to_next_scene(Scene index);
     void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
     void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
@@ -137,10 +141,6 @@ private:
     static const uint8_t text_store_size = 128;
     static const uint8_t text_store_size = 128;
     char text_store[text_store_size + 1];
     char text_store[text_store_size + 1];
 
 
-    static const char* app_folder;
-    static const char* app_extension;
-    static const char* app_filetype;
-
     bool load_key_data(string_t key_path);
     bool load_key_data(string_t key_path);
     void make_app_folder();
     void make_app_folder();
 };
 };

+ 9 - 0
applications/ibutton/scene/ibutton_scene_save_name.cpp

@@ -25,6 +25,10 @@ void iButtonSceneSaveName::on_enter(iButtonApp* app) {
     text_input_set_result_callback(
     text_input_set_result_callback(
         text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty);
         text_input, callback, app, app->get_text_store(), IBUTTON_KEY_NAME_SIZE, key_name_empty);
 
 
+    ValidatorIsFile* validator_is_file =
+        validator_is_file_alloc_init(app->app_folder, app->app_extension);
+    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput);
     view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput);
 }
 }
 
 
@@ -48,6 +52,11 @@ bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
 
 
 void iButtonSceneSaveName::on_exit(iButtonApp* app) {
 void iButtonSceneSaveName::on_exit(iButtonApp* app) {
     TextInput* text_input = app->get_view_manager()->get_text_input();
     TextInput* text_input = app->get_view_manager()->get_text_input();
+
+    void* validator_context = text_input_get_validator_callback_context(text_input);
+    text_input_set_validator(text_input, NULL, NULL);
+    validator_is_file_free((ValidatorIsFile*)validator_context);
+
     text_input_reset(text_input);
     text_input_reset(text_input);
 }
 }
 
 

+ 10 - 0
applications/irda/scene/irda_app_scene_edit_rename.cpp

@@ -20,6 +20,10 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size());
         strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size());
         enter_name_length = IrdaAppRemoteManager::max_remote_name_length;
         enter_name_length = IrdaAppRemoteManager::max_remote_name_length;
         text_input_set_header_text(text_input, "Name the remote");
         text_input_set_header_text(text_input, "Name the remote");
+
+        ValidatorIsFile* validator_is_file =
+            validator_is_file_alloc_init(app->irda_directory, app->irda_extension);
+        text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
     }
     }
 
 
     text_input_set_result_callback(
     text_input_set_result_callback(
@@ -59,4 +63,10 @@ bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) {
 }
 }
 
 
 void IrdaAppSceneEditRename::on_exit(IrdaApp* app) {
 void IrdaAppSceneEditRename::on_exit(IrdaApp* app) {
+    TextInput* text_input = app->get_view_manager()->get_text_input();
+
+    void* validator_context = text_input_get_validator_callback_context(text_input);
+    text_input_set_validator(text_input, NULL, NULL);
+
+    if(validator_context != NULL) validator_is_file_free((ValidatorIsFile*)validator_context);
 }
 }

+ 4 - 3
applications/lfrfid/scene/lfrfid_app_scene_delete_confirm.cpp

@@ -35,9 +35,9 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool need_restore) {
 
 
     string_printf(string_header, "Delete %s?", key.get_name());
     string_printf(string_header, "Delete %s?", key.get_name());
     line_1->set_text(
     line_1->set_text(
-        string_get_cstr(string_header), 64, 19, AlignCenter, AlignBottom, FontPrimary);
+        string_get_cstr(string_header), 64, 19, 128 - 2, AlignCenter, AlignBottom, FontPrimary);
     line_2->set_text(
     line_2->set_text(
-        string_get_cstr(string_data), 64, 29, AlignCenter, AlignBottom, FontSecondary);
+        string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontSecondary);
 
 
     switch(key.get_type()) {
     switch(key.get_type()) {
     case LfrfidKeyType::KeyEM4100:
     case LfrfidKeyType::KeyEM4100:
@@ -52,12 +52,13 @@ void LfRfidAppSceneDeleteConfirm::on_enter(LfRfidApp* app, bool need_restore) {
         break;
         break;
     }
     }
     line_3->set_text(
     line_3->set_text(
-        string_get_cstr(string_decrypted), 64, 39, AlignCenter, AlignBottom, FontSecondary);
+        string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary);
 
 
     line_4->set_text(
     line_4->set_text(
         lfrfid_key_get_type_string(key.get_type()),
         lfrfid_key_get_type_string(key.get_type()),
         64,
         64,
         49,
         49,
+        0,
         AlignCenter,
         AlignCenter,
         AlignBottom,
         AlignBottom,
         FontSecondary);
         FontSecondary);

+ 12 - 12
applications/lfrfid/scene/lfrfid_app_scene_read_success.cpp

@@ -36,9 +36,9 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
 
 
     switch(app->worker.key.get_type()) {
     switch(app->worker.key.get_type()) {
     case LfrfidKeyType::KeyEM4100:
     case LfrfidKeyType::KeyEM4100:
-        line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary);
-        line_2_text->set_text("Mod:", 65, 35, AlignRight, AlignBottom, FontSecondary);
-        line_3_text->set_text("ID:", 65, 47, AlignRight, AlignBottom, FontSecondary);
+        line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
+        line_2_text->set_text("Mod:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
+        line_3_text->set_text("ID:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
 
 
         for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
         for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
             string_cat_printf(string[0], "%02X", data[i]);
             string_cat_printf(string[0], "%02X", data[i]);
@@ -48,17 +48,17 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
         string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
         string_printf(string[2], "%03u,%05u", data[2], (uint16_t)((data[3] << 8) | (data[4])));
 
 
         line_1_value->set_text(
         line_1_value->set_text(
-            string_get_cstr(string[0]), 68, 23, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
         line_2_value->set_text(
         line_2_value->set_text(
-            string_get_cstr(string[1]), 68, 35, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
         line_3_value->set_text(
         line_3_value->set_text(
-            string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
         break;
         break;
     case LfrfidKeyType::KeyH10301:
     case LfrfidKeyType::KeyH10301:
     case LfrfidKeyType::KeyI40134:
     case LfrfidKeyType::KeyI40134:
-        line_1_text->set_text("HEX:", 65, 23, AlignRight, AlignBottom, FontSecondary);
-        line_2_text->set_text("FC:", 65, 35, AlignRight, AlignBottom, FontSecondary);
-        line_3_text->set_text("Card:", 65, 47, AlignRight, AlignBottom, FontSecondary);
+        line_1_text->set_text("HEX:", 65, 23, 0, AlignRight, AlignBottom, FontSecondary);
+        line_2_text->set_text("FC:", 65, 35, 0, AlignRight, AlignBottom, FontSecondary);
+        line_3_text->set_text("Card:", 65, 47, 0, AlignRight, AlignBottom, FontSecondary);
 
 
         for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
         for(uint8_t i = 0; i < app->worker.key.get_type_data_count(); i++) {
             string_cat_printf(string[0], "%02X", data[i]);
             string_cat_printf(string[0], "%02X", data[i]);
@@ -68,11 +68,11 @@ void LfRfidAppSceneReadSuccess::on_enter(LfRfidApp* app, bool need_restore) {
         string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2])));
         string_printf(string[2], "%u", (uint16_t)((data[1] << 8) | (data[2])));
 
 
         line_1_value->set_text(
         line_1_value->set_text(
-            string_get_cstr(string[0]), 68, 23, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[0]), 68, 23, 0, AlignLeft, AlignBottom, FontSecondary);
         line_2_value->set_text(
         line_2_value->set_text(
-            string_get_cstr(string[1]), 68, 35, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[1]), 68, 35, 0, AlignLeft, AlignBottom, FontSecondary);
         line_3_value->set_text(
         line_3_value->set_text(
-            string_get_cstr(string[2]), 68, 47, AlignLeft, AlignBottom, FontSecondary);
+            string_get_cstr(string[2]), 68, 47, 0, AlignLeft, AlignBottom, FontSecondary);
         break;
         break;
     }
     }
 
 

+ 9 - 0
applications/lfrfid/scene/lfrfid_app_scene_save_name.cpp

@@ -21,6 +21,10 @@ void LfRfidAppSceneSaveName::on_enter(LfRfidApp* app, bool need_restore) {
         app->worker.key.get_name_length(),
         app->worker.key.get_name_length(),
         key_name_empty);
         key_name_empty);
 
 
+    ValidatorIsFile* validator_is_file =
+        validator_is_file_alloc_init(app->app_folder, app->app_extension);
+    text_input->set_validator(validator_is_file_callback, validator_is_file);
+
     app->view_controller.switch_to<TextInputVM>();
     app->view_controller.switch_to<TextInputVM>();
 }
 }
 
 
@@ -46,6 +50,11 @@ bool LfRfidAppSceneSaveName::on_event(LfRfidApp* app, LfRfidApp::Event* event) {
 }
 }
 
 
 void LfRfidAppSceneSaveName::on_exit(LfRfidApp* app) {
 void LfRfidAppSceneSaveName::on_exit(LfRfidApp* app) {
+    void* validator_context =
+        app->view_controller.get<TextInputVM>()->get_validator_callback_context();
+    app->view_controller.get<TextInputVM>()->set_validator(NULL, NULL);
+    validator_is_file_free((ValidatorIsFile*)validator_context);
+
     app->view_controller.get<TextInputVM>()->clean();
     app->view_controller.get<TextInputVM>()->clean();
 }
 }
 
 

+ 5 - 3
applications/lfrfid/scene/lfrfid_app_scene_saved_info.cpp

@@ -28,8 +28,9 @@ void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool need_restore) {
         string_cat_printf(string_data, "%02X", data[i]);
         string_cat_printf(string_data, "%02X", data[i]);
     }
     }
 
 
-    line_1->set_text(key.get_name(), 64, 17, AlignCenter, AlignBottom, FontSecondary);
-    line_2->set_text(string_get_cstr(string_data), 64, 29, AlignCenter, AlignBottom, FontPrimary);
+    line_1->set_text(key.get_name(), 64, 17, 128 - 2, AlignCenter, AlignBottom, FontSecondary);
+    line_2->set_text(
+        string_get_cstr(string_data), 64, 29, 0, AlignCenter, AlignBottom, FontPrimary);
 
 
     switch(key.get_type()) {
     switch(key.get_type()) {
     case LfrfidKeyType::KeyEM4100:
     case LfrfidKeyType::KeyEM4100:
@@ -44,12 +45,13 @@ void LfRfidAppSceneSavedInfo::on_enter(LfRfidApp* app, bool need_restore) {
         break;
         break;
     }
     }
     line_3->set_text(
     line_3->set_text(
-        string_get_cstr(string_decrypted), 64, 39, AlignCenter, AlignBottom, FontSecondary);
+        string_get_cstr(string_decrypted), 64, 39, 0, AlignCenter, AlignBottom, FontSecondary);
 
 
     line_4->set_text(
     line_4->set_text(
         lfrfid_key_get_type_string(key.get_type()),
         lfrfid_key_get_type_string(key.get_type()),
         64,
         64,
         49,
         49,
+        0,
         AlignCenter,
         AlignCenter,
         AlignBottom,
         AlignBottom,
         FontSecondary);
         FontSecondary);

+ 13 - 2
applications/lfrfid/view/elements/string_element.cpp

@@ -9,8 +9,17 @@ StringElement::~StringElement() {
 
 
 void StringElement::draw(Canvas* canvas) {
 void StringElement::draw(Canvas* canvas) {
     if(text) {
     if(text) {
+        string_t line;
+        string_init(line);
+        string_set_str(line, text);
+
         canvas_set_font(canvas, font);
         canvas_set_font(canvas, font);
-        elements_multiline_text_aligned(canvas, x, y, horizontal, vertical, text);
+        if(fit_width != 0) {
+            elements_string_fit_width(canvas, line, fit_width);
+        }
+        elements_multiline_text_aligned(canvas, x, y, horizontal, vertical, string_get_cstr(line));
+
+        string_clear(line);
     }
     }
 }
 }
 
 
@@ -22,6 +31,7 @@ void StringElement::set_text(
     const char* _text,
     const char* _text,
     uint8_t _x,
     uint8_t _x,
     uint8_t _y,
     uint8_t _y,
+    uint8_t _fit_w,
     Align _horizontal,
     Align _horizontal,
     Align _vertical,
     Align _vertical,
     Font _font) {
     Font _font) {
@@ -29,8 +39,9 @@ void StringElement::set_text(
     text = _text;
     text = _text;
     x = _x;
     x = _x;
     y = _y;
     y = _y;
+    fit_width = _fit_w;
     horizontal = _horizontal;
     horizontal = _horizontal;
     vertical = _vertical;
     vertical = _vertical;
     font = _font;
     font = _font;
     unlock_model(true);
     unlock_model(true);
-}
+}

+ 2 - 0
applications/lfrfid/view/elements/string_element.h

@@ -12,6 +12,7 @@ public:
         const char* text = NULL,
         const char* text = NULL,
         uint8_t x = 0,
         uint8_t x = 0,
         uint8_t y = 0,
         uint8_t y = 0,
+        uint8_t fit_width = 0,
         Align horizontal = AlignLeft,
         Align horizontal = AlignLeft,
         Align vertical = AlignTop,
         Align vertical = AlignTop,
         Font font = FontPrimary);
         Font font = FontPrimary);
@@ -20,6 +21,7 @@ private:
     const char* text = NULL;
     const char* text = NULL;
     uint8_t x = 0;
     uint8_t x = 0;
     uint8_t y = 0;
     uint8_t y = 0;
+    uint8_t fit_width = 0;
     Align horizontal = AlignLeft;
     Align horizontal = AlignLeft;
     Align vertical = AlignTop;
     Align vertical = AlignTop;
     Font font = FontPrimary;
     Font font = FontPrimary;

+ 12 - 15
applications/nfc/nfc_device.c

@@ -4,9 +4,6 @@
 #include <lib/toolbox/path.h>
 #include <lib/toolbox/path.h>
 #include <lib/flipper_file/flipper_file.h>
 #include <lib/flipper_file/flipper_file.h>
 
 
-static const char* nfc_app_folder = "/any/nfc";
-static const char* nfc_app_extension = ".nfc";
-static const char* nfc_app_shadow_extension = ".shd";
 static const char* nfc_file_header = "Flipper NFC device";
 static const char* nfc_file_header = "Flipper NFC device";
 static const uint32_t nfc_file_version = 2;
 static const uint32_t nfc_file_version = 2;
 
 
@@ -243,7 +240,7 @@ static bool nfc_device_save_file(
 
 
     do {
     do {
         // Create nfc directory if necessary
         // Create nfc directory if necessary
-        if(!storage_simply_mkdir(dev->storage, nfc_app_folder)) break;
+        if(!storage_simply_mkdir(dev->storage, NFC_APP_FOLDER)) break;
         // First remove nfc device file if it was saved
         // First remove nfc device file if it was saved
         string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
         string_printf(temp_str, "%s/%s%s", folder, dev_name, extension);
         // Open file
         // Open file
@@ -281,12 +278,12 @@ static bool nfc_device_save_file(
 }
 }
 
 
 bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
 bool nfc_device_save(NfcDevice* dev, const char* dev_name) {
-    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_extension);
+    return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_EXTENSION);
 }
 }
 
 
 bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
 bool nfc_device_save_shadow(NfcDevice* dev, const char* dev_name) {
     dev->shadow_file_exist = true;
     dev->shadow_file_exist = true;
-    return nfc_device_save_file(dev, dev_name, nfc_app_folder, nfc_app_shadow_extension);
+    return nfc_device_save_file(dev, dev_name, NFC_APP_FOLDER, NFC_APP_SHADOW_EXTENSION);
 }
 }
 
 
 static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
 static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
@@ -300,9 +297,9 @@ static bool nfc_device_load_data(NfcDevice* dev, string_t path) {
 
 
     do {
     do {
         // Check existance of shadow file
         // Check existance of shadow file
-        size_t ext_start = string_search_str(path, nfc_app_extension);
+        size_t ext_start = string_search_str(path, NFC_APP_EXTENSION);
         string_set_n(temp_str, path, 0, ext_start);
         string_set_n(temp_str, path, 0, ext_start);
-        string_cat_printf(temp_str, "%s", nfc_app_shadow_extension);
+        string_cat_printf(temp_str, "%s", NFC_APP_SHADOW_EXTENSION);
         dev->shadow_file_exist =
         dev->shadow_file_exist =
             storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK;
             storage_common_stat(dev->storage, string_get_cstr(temp_str), NULL) == FSE_OK;
         // Open shadow file if it exists. If not - open original
         // Open shadow file if it exists. If not - open original
@@ -374,15 +371,15 @@ bool nfc_file_select(NfcDevice* dev) {
     // Input events and views are managed by file_select
     // Input events and views are managed by file_select
     bool res = dialog_file_select_show(
     bool res = dialog_file_select_show(
         dev->dialogs,
         dev->dialogs,
-        nfc_app_folder,
-        nfc_app_extension,
+        NFC_APP_FOLDER,
+        NFC_APP_EXTENSION,
         dev->file_name,
         dev->file_name,
         sizeof(dev->file_name),
         sizeof(dev->file_name),
         dev->dev_name);
         dev->dev_name);
     if(res) {
     if(res) {
         string_t dev_str;
         string_t dev_str;
         // Get key file path
         // Get key file path
-        string_init_printf(dev_str, "%s/%s%s", nfc_app_folder, dev->file_name, nfc_app_extension);
+        string_init_printf(dev_str, "%s/%s%s", NFC_APP_FOLDER, dev->file_name, NFC_APP_EXTENSION);
         res = nfc_device_load_data(dev, dev_str);
         res = nfc_device_load_data(dev, dev_str);
         if(res) {
         if(res) {
             nfc_device_set_name(dev, dev->file_name);
             nfc_device_set_name(dev, dev->file_name);
@@ -409,12 +406,12 @@ bool nfc_device_delete(NfcDevice* dev) {
 
 
     do {
     do {
         // Delete original file
         // Delete original file
-        string_init_printf(file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
+        string_init_printf(file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION);
         if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
         if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
         // Delete shadow file if it exists
         // Delete shadow file if it exists
         if(dev->shadow_file_exist) {
         if(dev->shadow_file_exist) {
             string_printf(
             string_printf(
-                file_path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
+                file_path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION);
             if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
             if(!storage_simply_remove(dev->storage, string_get_cstr(file_path))) break;
         }
         }
         deleted = true;
         deleted = true;
@@ -437,10 +434,10 @@ bool nfc_device_restore(NfcDevice* dev) {
 
 
     do {
     do {
         string_init_printf(
         string_init_printf(
-            path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_shadow_extension);
+            path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_SHADOW_EXTENSION);
         if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break;
         if(!storage_simply_remove(dev->storage, string_get_cstr(path))) break;
         dev->shadow_file_exist = false;
         dev->shadow_file_exist = false;
-        string_printf(path, "%s/%s%s", nfc_app_folder, dev->dev_name, nfc_app_extension);
+        string_printf(path, "%s/%s%s", NFC_APP_FOLDER, dev->dev_name, NFC_APP_EXTENSION);
         if(!nfc_device_load_data(dev, path)) break;
         if(!nfc_device_load_data(dev, path)) break;
         restored = true;
         restored = true;
     } while(0);
     } while(0);

+ 4 - 0
applications/nfc/nfc_device.h

@@ -10,6 +10,10 @@
 #define NFC_DEV_NAME_MAX_LEN 22
 #define NFC_DEV_NAME_MAX_LEN 22
 #define NFC_FILE_NAME_MAX_LEN 120
 #define NFC_FILE_NAME_MAX_LEN 120
 
 
+#define NFC_APP_FOLDER "/any/nfc"
+#define NFC_APP_EXTENSION ".nfc"
+#define NFC_APP_SHADOW_EXTENSION ".shd"
+
 typedef enum {
 typedef enum {
     NfcDeviceNfca,
     NfcDeviceNfca,
     NfcDeviceNfcb,
     NfcDeviceNfcb,

+ 10 - 0
applications/nfc/scenes/nfc_scene_save_name.c

@@ -1,5 +1,6 @@
 #include "../nfc_i.h"
 #include "../nfc_i.h"
 #include <lib/toolbox/random_name.h>
 #include <lib/toolbox/random_name.h>
+#include <gui/modules/validators.h>
 
 
 #define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
 #define SCENE_SAVE_NAME_CUSTOM_EVENT (0UL)
 
 
@@ -29,6 +30,11 @@ void nfc_scene_save_name_on_enter(void* context) {
         nfc->text_store,
         nfc->text_store,
         NFC_DEV_NAME_MAX_LEN,
         NFC_DEV_NAME_MAX_LEN,
         dev_name_empty);
         dev_name_empty);
+
+    ValidatorIsFile* validator_is_file =
+        validator_is_file_alloc_init(NFC_APP_FOLDER, NFC_APP_EXTENSION);
+    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextInput);
 }
 }
 
 
@@ -60,5 +66,9 @@ void nfc_scene_save_name_on_exit(void* context) {
     Nfc* nfc = (Nfc*)context;
     Nfc* nfc = (Nfc*)context;
 
 
     // Clear view
     // Clear view
+    void* validator_context = text_input_get_validator_callback_context(nfc->text_input);
+    text_input_set_validator(nfc->text_input, NULL, NULL);
+    validator_is_file_free(validator_context);
+
     text_input_reset(nfc->text_input);
     text_input_reset(nfc->text_input);
 }
 }

+ 8 - 0
lib/app-scened-template/view_modules/text_input_vm.cpp

@@ -29,3 +29,11 @@ void TextInputVM::set_result_callback(
 void TextInputVM::set_header_text(const char* text) {
 void TextInputVM::set_header_text(const char* text) {
     text_input_set_header_text(text_input, text);
     text_input_set_header_text(text_input, text);
 }
 }
+
+void TextInputVM::set_validator(TextInputValidatorCallback callback, void* callback_context) {
+    text_input_set_validator(text_input, callback, callback_context);
+}
+
+void* TextInputVM::get_validator_callback_context() {
+    return text_input_get_validator_callback_context(text_input);
+}

+ 4 - 0
lib/app-scened-template/view_modules/text_input_vm.h

@@ -32,6 +32,10 @@ public:
      */
      */
     void set_header_text(const char* text);
     void set_header_text(const char* text);
 
 
+    void set_validator(TextInputValidatorCallback callback, void* callback_context);
+
+    void* get_validator_callback_context();
+
 private:
 private:
     TextInput* text_input;
     TextInput* text_input;
 };
 };