Browse Source

[FL-2514] Port iButton application to C (#1198)

* Initial C iButton app setup
* Add more scenes
* Add even more scenes
* Add even more scenes again
* More scenes...
* Add key info scene
* Add delete success scene
* Use scene state to store internal data
* Add parameter parsing
* Add emulate scene
* Add write scene
* Add write success scene
* Add Read scene
* Add read success scene
* Add exit confirm scene
* Add retry confirm scene
* Add CRC error scene
* Add not key scene
* Add read key menu scene
* Rename some scenes
* Refactor conditionals
* Remove unneeded custom events
* Remove the old iButton app
* Correct formatting
* Remove rogue comments and function prototypes
* iButton: cleanup merge artifacts and fix warnings

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Georgii Surkov 3 years ago
parent
commit
5171a6ad14
73 changed files with 1785 additions and 2186 deletions
  1. 349 0
      applications/ibutton/ibutton.c
  2. 0 10
      applications/ibutton/ibutton.cpp
  3. 3 0
      applications/ibutton/ibutton.h
  4. 0 342
      applications/ibutton/ibutton_app.cpp
  5. 0 144
      applications/ibutton/ibutton_app.h
  6. 12 0
      applications/ibutton/ibutton_custom_event.h
  7. 0 36
      applications/ibutton/ibutton_event.h
  8. 86 0
      applications/ibutton/ibutton_i.h
  9. 0 140
      applications/ibutton/ibutton_view_manager.cpp
  10. 0 51
      applications/ibutton/ibutton_view_manager.h
  11. 0 66
      applications/ibutton/scene/ibutton_scene_add_type.cpp
  12. 0 12
      applications/ibutton/scene/ibutton_scene_add_type.h
  13. 0 53
      applications/ibutton/scene/ibutton_scene_add_value.cpp
  14. 0 13
      applications/ibutton/scene/ibutton_scene_add_value.h
  15. 0 86
      applications/ibutton/scene/ibutton_scene_delete_confirm.cpp
  16. 0 9
      applications/ibutton/scene/ibutton_scene_delete_confirm.h
  17. 0 47
      applications/ibutton/scene/ibutton_scene_delete_success.cpp
  18. 0 9
      applications/ibutton/scene/ibutton_scene_delete_success.h
  19. 0 9
      applications/ibutton/scene/ibutton_scene_emulate.h
  20. 0 51
      applications/ibutton/scene/ibutton_scene_exit_confirm.cpp
  21. 0 9
      applications/ibutton/scene/ibutton_scene_exit_confirm.h
  22. 0 14
      applications/ibutton/scene/ibutton_scene_generic.h
  23. 0 58
      applications/ibutton/scene/ibutton_scene_info.cpp
  24. 0 9
      applications/ibutton/scene/ibutton_scene_info.h
  25. 0 72
      applications/ibutton/scene/ibutton_scene_read.cpp
  26. 0 9
      applications/ibutton/scene/ibutton_scene_read.h
  27. 0 74
      applications/ibutton/scene/ibutton_scene_read_crc_error.cpp
  28. 0 9
      applications/ibutton/scene/ibutton_scene_read_crc_error.h
  29. 0 65
      applications/ibutton/scene/ibutton_scene_read_key_menu.cpp
  30. 0 12
      applications/ibutton/scene/ibutton_scene_read_key_menu.h
  31. 0 74
      applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp
  32. 0 9
      applications/ibutton/scene/ibutton_scene_read_not_key_error.h
  33. 0 86
      applications/ibutton/scene/ibutton_scene_read_success.cpp
  34. 0 9
      applications/ibutton/scene/ibutton_scene_read_success.h
  35. 0 51
      applications/ibutton/scene/ibutton_scene_retry_confirm.cpp
  36. 0 9
      applications/ibutton/scene/ibutton_scene_retry_confirm.h
  37. 0 70
      applications/ibutton/scene/ibutton_scene_save_name.cpp
  38. 0 9
      applications/ibutton/scene/ibutton_scene_save_name.h
  39. 0 52
      applications/ibutton/scene/ibutton_scene_save_success.cpp
  40. 0 9
      applications/ibutton/scene/ibutton_scene_save_success.h
  41. 0 73
      applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp
  42. 0 12
      applications/ibutton/scene/ibutton_scene_saved_key_menu.h
  43. 0 18
      applications/ibutton/scene/ibutton_scene_select_key.cpp
  44. 0 9
      applications/ibutton/scene/ibutton_scene_select_key.h
  45. 0 60
      applications/ibutton/scene/ibutton_scene_start.cpp
  46. 0 12
      applications/ibutton/scene/ibutton_scene_start.h
  47. 0 111
      applications/ibutton/scene/ibutton_scene_write.cpp
  48. 0 12
      applications/ibutton/scene/ibutton_scene_write.h
  49. 0 52
      applications/ibutton/scene/ibutton_scene_write_success.cpp
  50. 0 9
      applications/ibutton/scene/ibutton_scene_write_success.h
  51. 30 0
      applications/ibutton/scenes/ibutton_scene.c
  52. 29 0
      applications/ibutton/scenes/ibutton_scene.h
  53. 58 0
      applications/ibutton/scenes/ibutton_scene_add_type.c
  54. 57 0
      applications/ibutton/scenes/ibutton_scene_add_value.c
  55. 20 0
      applications/ibutton/scenes/ibutton_scene_config.h
  56. 91 0
      applications/ibutton/scenes/ibutton_scene_delete_confirm.c
  57. 48 0
      applications/ibutton/scenes/ibutton_scene_delete_success.c
  58. 35 31
      applications/ibutton/scenes/ibutton_scene_emulate.c
  59. 51 0
      applications/ibutton/scenes/ibutton_scene_exit_confirm.c
  60. 61 0
      applications/ibutton/scenes/ibutton_scene_info.c
  61. 72 0
      applications/ibutton/scenes/ibutton_scene_read.c
  62. 71 0
      applications/ibutton/scenes/ibutton_scene_read_crc_error.c
  63. 61 0
      applications/ibutton/scenes/ibutton_scene_read_key_menu.c
  64. 72 0
      applications/ibutton/scenes/ibutton_scene_read_not_key_error.c
  65. 87 0
      applications/ibutton/scenes/ibutton_scene_read_success.c
  66. 51 0
      applications/ibutton/scenes/ibutton_scene_retry_confirm.c
  67. 68 0
      applications/ibutton/scenes/ibutton_scene_save_name.c
  68. 52 0
      applications/ibutton/scenes/ibutton_scene_save_success.c
  69. 75 0
      applications/ibutton/scenes/ibutton_scene_saved_key_menu.c
  70. 22 0
      applications/ibutton/scenes/ibutton_scene_select_key.c
  71. 51 0
      applications/ibutton/scenes/ibutton_scene_start.c
  72. 121 0
      applications/ibutton/scenes/ibutton_scene_write.c
  73. 52 0
      applications/ibutton/scenes/ibutton_scene_write_success.c

+ 349 - 0
applications/ibutton/ibutton.c

@@ -0,0 +1,349 @@
+#include "ibutton.h"
+#include "ibutton_i.h"
+#include "ibutton/scenes/ibutton_scene.h"
+
+#include <toolbox/path.h>
+#include <flipper_format/flipper_format.h>
+
+static const NotificationSequence* ibutton_notification_sequences[] = {
+    &sequence_error,
+    &sequence_success,
+    &sequence_blink_cyan_10,
+    &sequence_blink_magenta_10,
+    &sequence_blink_yellow_10,
+    &sequence_set_red_255,
+    &sequence_reset_red,
+    &sequence_set_green_255,
+    &sequence_reset_green,
+};
+
+static void ibutton_make_app_folder(iButton* ibutton) {
+    if(!storage_simply_mkdir(ibutton->storage, IBUTTON_APP_FOLDER)) {
+        dialog_message_show_storage_error(ibutton->dialogs, "Cannot create\napp folder");
+    }
+}
+
+static bool ibutton_load_key_data(iButton* ibutton, string_t key_path) {
+    FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
+    bool result = false;
+    string_t data;
+    string_init(data);
+
+    do {
+        if(!flipper_format_file_open_existing(file, string_get_cstr(key_path))) break;
+
+        // header
+        uint32_t version;
+        if(!flipper_format_read_header(file, data, &version)) break;
+        if(string_cmp_str(data, IBUTTON_APP_FILE_TYPE) != 0) break;
+        if(version != 1) break;
+
+        // key type
+        iButtonKeyType type;
+        if(!flipper_format_read_string(file, "Key type", data)) break;
+        if(!ibutton_key_get_type_by_string(string_get_cstr(data), &type)) break;
+
+        // key data
+        uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
+        if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
+            break;
+
+        ibutton_key_set_type(ibutton->key, type);
+        ibutton_key_set_data(ibutton->key, key_data, IBUTTON_KEY_DATA_SIZE);
+
+        result = true;
+    } while(false);
+
+    flipper_format_free(file);
+    string_clear(data);
+
+    if(!result) {
+        dialog_message_show_storage_error(ibutton->dialogs, "Cannot load\nkey file");
+    }
+
+    return result;
+}
+
+bool ibutton_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    iButton* ibutton = context;
+    return scene_manager_handle_custom_event(ibutton->scene_manager, event);
+}
+
+bool ibutton_back_event_callback(void* context) {
+    furi_assert(context);
+    iButton* ibutton = context;
+    return scene_manager_handle_back_event(ibutton->scene_manager);
+}
+
+void ibutton_tick_event_callback(void* context) {
+    furi_assert(context);
+    iButton* ibutton = context;
+    scene_manager_handle_tick_event(ibutton->scene_manager);
+}
+
+iButton* ibutton_alloc() {
+    iButton* ibutton = malloc(sizeof(iButton));
+
+    ibutton->scene_manager = scene_manager_alloc(&ibutton_scene_handlers, ibutton);
+
+    ibutton->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_enable_queue(ibutton->view_dispatcher);
+    view_dispatcher_set_event_callback_context(ibutton->view_dispatcher, ibutton);
+    view_dispatcher_set_custom_event_callback(
+        ibutton->view_dispatcher, ibutton_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        ibutton->view_dispatcher, ibutton_back_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        ibutton->view_dispatcher, ibutton_tick_event_callback, 100);
+
+    ibutton->gui = furi_record_open("gui");
+    view_dispatcher_attach_to_gui(
+        ibutton->view_dispatcher, ibutton->gui, ViewDispatcherTypeFullscreen);
+
+    ibutton->storage = furi_record_open("storage");
+    ibutton->dialogs = furi_record_open("dialogs");
+    ibutton->notifications = furi_record_open("notification");
+
+    ibutton->key = ibutton_key_alloc();
+    ibutton->key_worker = ibutton_worker_alloc();
+    ibutton_worker_start_thread(ibutton->key_worker);
+
+    ibutton->submenu = submenu_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewSubmenu, submenu_get_view(ibutton->submenu));
+
+    ibutton->byte_input = byte_input_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewByteInput, byte_input_get_view(ibutton->byte_input));
+
+    ibutton->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewTextInput, text_input_get_view(ibutton->text_input));
+
+    ibutton->popup = popup_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewPopup, popup_get_view(ibutton->popup));
+
+    ibutton->widget = widget_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewWidget, widget_get_view(ibutton->widget));
+
+    ibutton->dialog_ex = dialog_ex_alloc();
+    view_dispatcher_add_view(
+        ibutton->view_dispatcher, iButtonViewDialogEx, dialog_ex_get_view(ibutton->dialog_ex));
+
+    return ibutton;
+}
+
+void ibutton_free(iButton* ibutton) {
+    furi_assert(ibutton);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+    dialog_ex_free(ibutton->dialog_ex);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewWidget);
+    widget_free(ibutton->widget);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewPopup);
+    popup_free(ibutton->popup);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewTextInput);
+    text_input_free(ibutton->text_input);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewByteInput);
+    byte_input_free(ibutton->byte_input);
+
+    view_dispatcher_remove_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+    submenu_free(ibutton->submenu);
+
+    view_dispatcher_free(ibutton->view_dispatcher);
+    scene_manager_free(ibutton->scene_manager);
+
+    furi_record_close("storage");
+    ibutton->storage = NULL;
+
+    furi_record_close("notification");
+    ibutton->notifications = NULL;
+
+    furi_record_close("dialogs");
+    ibutton->dialogs = NULL;
+
+    furi_record_close("gui");
+    ibutton->gui = NULL;
+
+    ibutton_worker_stop_thread(ibutton->key_worker);
+    ibutton_worker_free(ibutton->key_worker);
+    ibutton_key_free(ibutton->key);
+
+    free(ibutton);
+}
+
+bool ibutton_file_select(iButton* ibutton) {
+    bool success = dialog_file_select_show(
+        ibutton->dialogs,
+        IBUTTON_APP_FOLDER,
+        IBUTTON_APP_EXTENSION,
+        ibutton->file_name,
+        IBUTTON_FILE_NAME_SIZE,
+        ibutton_key_get_name_p(ibutton->key));
+
+    if(success) {
+        string_t key_str;
+        string_init_printf(
+            key_str, "%s/%s%s", IBUTTON_APP_FOLDER, ibutton->file_name, IBUTTON_APP_EXTENSION);
+        success = ibutton_load_key_data(ibutton, key_str);
+
+        if(success) {
+            ibutton_key_set_name(ibutton->key, ibutton->file_name);
+        }
+
+        string_clear(key_str);
+    }
+
+    return success;
+}
+
+bool ibutton_load_key(iButton* ibutton, const char* key_name) {
+    string_t key_path;
+    string_init_set_str(key_path, key_name);
+
+    const bool success = ibutton_load_key_data(ibutton, key_path);
+
+    if(success) {
+        path_extract_filename_no_ext(key_name, key_path);
+        ibutton_key_set_name(ibutton->key, string_get_cstr(key_path));
+    }
+
+    string_clear(key_path);
+    return success;
+}
+
+bool ibutton_save_key(iButton* ibutton, const char* key_name) {
+    // Create ibutton directory if necessary
+    ibutton_make_app_folder(ibutton);
+
+    FlipperFormat* file = flipper_format_file_alloc(ibutton->storage);
+    iButtonKey* key = ibutton->key;
+
+    string_t key_file_name;
+    bool result = false;
+    string_init(key_file_name);
+
+    do {
+        // First remove key if it was saved (we rename the key)
+        if(!ibutton_delete_key(ibutton)) break;
+
+        // Save the key
+        ibutton_key_set_name(key, key_name);
+
+        // Set full file name, for new key
+        string_printf(
+            key_file_name,
+            "%s/%s%s",
+            IBUTTON_APP_FOLDER,
+            ibutton_key_get_name_p(key),
+            IBUTTON_APP_EXTENSION);
+
+        // Open file for write
+        if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break;
+
+        // Write header
+        if(!flipper_format_write_header_cstr(file, IBUTTON_APP_FILE_TYPE, 1)) break;
+
+        // Write key type
+        if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
+            break;
+        const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
+        if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
+
+        // Write data
+        if(!flipper_format_write_comment_cstr(
+               file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
+            break;
+
+        if(!flipper_format_write_hex(
+               file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
+            break;
+        result = true;
+
+    } while(false);
+
+    flipper_format_free(file);
+
+    string_clear(key_file_name);
+
+    if(!result) {
+        dialog_message_show_storage_error(ibutton->dialogs, "Cannot save\nkey file");
+    }
+
+    return result;
+}
+
+bool ibutton_delete_key(iButton* ibutton) {
+    string_t file_name;
+    bool result = false;
+
+    string_init_printf(
+        file_name,
+        "%s/%s%s",
+        IBUTTON_APP_FOLDER,
+        ibutton_key_get_name_p(ibutton->key),
+        IBUTTON_APP_EXTENSION);
+    result = storage_simply_remove(ibutton->storage, string_get_cstr(file_name));
+    string_clear(file_name);
+
+    return result;
+}
+
+void ibutton_text_store_set(iButton* ibutton, const char* text, ...) {
+    va_list args;
+    va_start(args, text);
+
+    vsnprintf(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE, text, args);
+
+    va_end(args);
+}
+
+void ibutton_text_store_clear(iButton* ibutton) {
+    memset(ibutton->text_store, 0, IBUTTON_TEXT_STORE_SIZE);
+}
+
+void ibutton_switch_to_previous_scene_one_of(
+    iButton* ibutton,
+    const uint32_t* scene_ids,
+    size_t scene_ids_size) {
+    furi_assert(scene_ids_size);
+    SceneManager* scene_manager = ibutton->scene_manager;
+
+    for(size_t i = 0; i < scene_ids_size; ++i) {
+        const uint32_t scene_id = scene_ids[i];
+        if(scene_manager_has_previous_scene(scene_manager, scene_id)) {
+            scene_manager_search_and_switch_to_previous_scene(scene_manager, scene_id);
+            return;
+        }
+    }
+}
+
+void ibutton_notification_message(iButton* ibutton, uint32_t message) {
+    furi_assert(message < sizeof(ibutton_notification_sequences) / sizeof(NotificationSequence*));
+    notification_message(ibutton->notifications, ibutton_notification_sequences[message]);
+}
+
+int32_t ibutton_app(void* p) {
+    iButton* ibutton = ibutton_alloc();
+
+    ibutton_make_app_folder(ibutton);
+
+    if(p && ibutton_load_key(ibutton, (const char*)p)) {
+        // TODO: Display an error if the key from p could not be loaded
+        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+    } else {
+        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneStart);
+    }
+
+    view_dispatcher_run(ibutton->view_dispatcher);
+
+    ibutton_free(ibutton);
+    return 0;
+}

+ 0 - 10
applications/ibutton/ibutton.cpp

@@ -1,10 +0,0 @@
-#include "ibutton_app.h"
-
-// app enter function
-extern "C" int32_t ibutton_app(void* p) {
-    iButtonApp* app = new iButtonApp();
-    app->run(p);
-    delete app;
-
-    return 255;
-}

+ 3 - 0
applications/ibutton/ibutton.h

@@ -0,0 +1,3 @@
+#pragma once
+
+typedef struct iButton iButton;

+ 0 - 342
applications/ibutton/ibutton_app.cpp

@@ -1,342 +0,0 @@
-#include "ibutton_app.h"
-#include <stdarg.h>
-#include <furi_hal.h>
-#include <m-string.h>
-#include <toolbox/path.h>
-#include <flipper_format/flipper_format.h>
-
-const char* iButtonApp::app_folder = "/any/ibutton";
-const char* iButtonApp::app_extension = ".ibtn";
-const char* iButtonApp::app_filetype = "Flipper iButton key";
-
-void iButtonApp::run(void* args) {
-    iButtonEvent event;
-    bool consumed;
-    bool exit = false;
-
-    make_app_folder();
-
-    if(args && load_key((const char*)args)) {
-        current_scene = Scene::SceneEmulate;
-    }
-
-    scenes[current_scene]->on_enter(this);
-
-    while(!exit) {
-        view.receive_event(&event);
-
-        consumed = scenes[current_scene]->on_event(this, &event);
-
-        if(!consumed) {
-            if(event.type == iButtonEvent::Type::EventTypeBack) {
-                exit = switch_to_previous_scene();
-            }
-        }
-    };
-
-    scenes[current_scene]->on_exit(this);
-}
-
-iButtonApp::iButtonApp()
-    : notification{"notification"}
-    , storage{"storage"}
-    , dialogs{"dialogs"} {
-    key = ibutton_key_alloc();
-    key_worker = ibutton_worker_alloc();
-    ibutton_worker_start_thread(key_worker);
-}
-
-iButtonApp::~iButtonApp() {
-    for(std::map<Scene, iButtonScene*>::iterator it = scenes.begin(); it != scenes.end(); ++it) {
-        delete it->second;
-    }
-    scenes.clear();
-
-    ibutton_worker_stop_thread(key_worker);
-    ibutton_worker_free(key_worker);
-    ibutton_key_free(key);
-}
-
-iButtonAppViewManager* iButtonApp::get_view_manager() {
-    return &view;
-}
-
-void iButtonApp::switch_to_next_scene(Scene next_scene) {
-    previous_scenes_list.push_front(current_scene);
-
-    if(next_scene != Scene::SceneExit) {
-        scenes[current_scene]->on_exit(this);
-        current_scene = next_scene;
-        scenes[current_scene]->on_enter(this);
-    }
-}
-
-void iButtonApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
-    Scene previous_scene = Scene::SceneStart;
-    bool scene_found = false;
-
-    while(!scene_found) {
-        previous_scene = get_previous_scene();
-        for(Scene element : scenes_list) {
-            if(previous_scene == element || previous_scene == Scene::SceneStart) {
-                scene_found = true;
-                break;
-            }
-        }
-    }
-
-    scenes[current_scene]->on_exit(this);
-    current_scene = previous_scene;
-    scenes[current_scene]->on_enter(this);
-}
-
-bool iButtonApp::switch_to_previous_scene(uint8_t count) {
-    Scene previous_scene = Scene::SceneStart;
-
-    for(uint8_t i = 0; i < count; i++) {
-        previous_scene = get_previous_scene();
-        if(previous_scene == Scene::SceneExit) break;
-    }
-
-    if(previous_scene == Scene::SceneExit) {
-        return true;
-    } else {
-        scenes[current_scene]->on_exit(this);
-        current_scene = previous_scene;
-        scenes[current_scene]->on_enter(this);
-        return false;
-    }
-}
-
-iButtonApp::Scene iButtonApp::get_previous_scene() {
-    Scene scene = previous_scenes_list.front();
-    previous_scenes_list.pop_front();
-    return scene;
-}
-
-iButtonWorker* iButtonApp::get_key_worker() {
-    return key_worker;
-}
-
-iButtonKey* iButtonApp::get_key() {
-    return key;
-}
-
-char* iButtonApp::get_file_name() {
-    return file_name;
-}
-
-uint8_t iButtonApp::get_file_name_size() {
-    return file_name_size;
-}
-
-void iButtonApp::notify_read() {
-    notification_message(notification, &sequence_blink_cyan_10);
-}
-
-void iButtonApp::notify_emulate() {
-    notification_message(notification, &sequence_blink_magenta_10);
-}
-
-void iButtonApp::notify_yellow_blink() {
-    notification_message(notification, &sequence_blink_yellow_10);
-}
-
-void iButtonApp::notify_error() {
-    notification_message(notification, &sequence_error);
-}
-
-void iButtonApp::notify_success() {
-    notification_message(notification, &sequence_success);
-}
-
-void iButtonApp::notify_green_on() {
-    notification_message_block(notification, &sequence_set_green_255);
-}
-
-void iButtonApp::notify_green_off() {
-    notification_message(notification, &sequence_reset_green);
-}
-
-void iButtonApp::notify_red_on() {
-    notification_message_block(notification, &sequence_set_red_255);
-}
-
-void iButtonApp::notify_red_off() {
-    notification_message(notification, &sequence_reset_red);
-}
-
-void iButtonApp::set_text_store(const char* text...) {
-    va_list args;
-    va_start(args, text);
-
-    vsnprintf(text_store, text_store_size, text, args);
-
-    va_end(args);
-}
-
-char* iButtonApp::get_text_store() {
-    return text_store;
-}
-
-uint8_t iButtonApp::get_text_store_size() {
-    return text_store_size;
-}
-
-// file managment
-bool iButtonApp::save_key(const char* key_name) {
-    // Create ibutton directory if necessary
-    make_app_folder();
-
-    FlipperFormat* file = flipper_format_file_alloc(storage);
-    string_t key_file_name;
-    bool result = false;
-    string_init(key_file_name);
-
-    do {
-        // First remove key if it was saved (we rename the key)
-        if(!delete_key()) break;
-
-        // Save the key
-        ibutton_key_set_name(key, key_name);
-
-        // Set full file name, for new key
-        string_printf(
-            key_file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension);
-
-        // Open file for write
-        if(!flipper_format_file_open_always(file, string_get_cstr(key_file_name))) break;
-
-        // Write header
-        if(!flipper_format_write_header_cstr(file, iButtonApp::app_filetype, 1)) break;
-
-        // Write key type
-        if(!flipper_format_write_comment_cstr(file, "Key type can be Cyfral, Dallas or Metakom"))
-            break;
-        const char* key_type = ibutton_key_get_string_by_type(ibutton_key_get_type(key));
-        if(!flipper_format_write_string_cstr(file, "Key type", key_type)) break;
-
-        // Write data
-        if(!flipper_format_write_comment_cstr(
-               file, "Data size for Cyfral is 2, for Metakom is 4, for Dallas is 8"))
-            break;
-
-        if(!flipper_format_write_hex(
-               file, "Data", ibutton_key_get_data_p(key), ibutton_key_get_data_size(key)))
-            break;
-        result = true;
-
-    } while(false);
-
-    flipper_format_free(file);
-
-    string_clear(key_file_name);
-
-    if(!result) {
-        dialog_message_show_storage_error(dialogs, "Cannot save\nkey file");
-    }
-
-    return result;
-}
-
-bool iButtonApp::load_key_data(string_t key_path) {
-    FlipperFormat* file = flipper_format_file_alloc(storage);
-    bool result = false;
-    string_t data;
-    string_init(data);
-
-    do {
-        if(!flipper_format_file_open_existing(file, string_get_cstr(key_path))) break;
-
-        // header
-        uint32_t version;
-        if(!flipper_format_read_header(file, data, &version)) break;
-        if(string_cmp_str(data, iButtonApp::app_filetype) != 0) break;
-        if(version != 1) break;
-
-        // key type
-        iButtonKeyType type;
-        if(!flipper_format_read_string(file, "Key type", data)) break;
-        if(!ibutton_key_get_type_by_string(string_get_cstr(data), &type)) break;
-
-        // key data
-        uint8_t key_data[IBUTTON_KEY_DATA_SIZE] = {0};
-        if(!flipper_format_read_hex(file, "Data", key_data, ibutton_key_get_size_by_type(type)))
-            break;
-
-        ibutton_key_set_type(key, type);
-        ibutton_key_set_data(key, key_data, IBUTTON_KEY_DATA_SIZE);
-
-        result = true;
-    } while(false);
-
-    flipper_format_free(file);
-    string_clear(data);
-
-    if(!result) {
-        dialog_message_show_storage_error(dialogs, "Cannot load\nkey file");
-    }
-
-    return result;
-}
-
-bool iButtonApp::load_key(const char* key_name) {
-    bool result = false;
-    string_t key_path;
-
-    string_init_set_str(key_path, key_name);
-
-    result = load_key_data(key_path);
-    if(result) {
-        path_extract_filename_no_ext(key_name, key_path);
-        ibutton_key_set_name(key, string_get_cstr(key_path));
-    }
-    string_clear(key_path);
-    return result;
-}
-
-bool iButtonApp::load_key() {
-    bool result = false;
-
-    // Input events and views are managed by file_select
-    bool res = dialog_file_select_show(
-        dialogs,
-        app_folder,
-        app_extension,
-        get_file_name(),
-        get_file_name_size(),
-        ibutton_key_get_name_p(key));
-
-    if(res) {
-        string_t key_str;
-
-        // Get key file path
-        string_init_printf(key_str, "%s/%s%s", app_folder, get_file_name(), app_extension);
-
-        result = load_key_data(key_str);
-        if(result) {
-            ibutton_key_set_name(key, get_file_name());
-        }
-        string_clear(key_str);
-    }
-
-    return result;
-}
-
-bool iButtonApp::delete_key() {
-    string_t file_name;
-    bool result = false;
-
-    string_init_printf(
-        file_name, "%s/%s%s", app_folder, ibutton_key_get_name_p(key), app_extension);
-    result = storage_simply_remove(storage, string_get_cstr(file_name));
-    string_clear(file_name);
-
-    return result;
-}
-
-void iButtonApp::make_app_folder() {
-    if(!storage_simply_mkdir(storage, app_folder)) {
-        dialog_message_show_storage_error(dialogs, "Cannot create\napp folder");
-    }
-}

+ 0 - 144
applications/ibutton/ibutton_app.h

@@ -1,144 +0,0 @@
-#pragma once
-#include <map>
-#include <list>
-
-#include "ibutton_view_manager.h"
-#include "scene/ibutton_scene_generic.h"
-#include "scene/ibutton_scene_start.h"
-#include "scene/ibutton_scene_read.h"
-#include "scene/ibutton_scene_read_crc_error.h"
-#include "scene/ibutton_scene_read_not_key_error.h"
-#include "scene/ibutton_scene_read_success.h"
-#include "scene/ibutton_scene_retry_confirm.h"
-#include "scene/ibutton_scene_exit_confirm.h"
-#include "scene/ibutton_scene_read_key_menu.h"
-#include "scene/ibutton_scene_write.h"
-#include "scene/ibutton_scene_write_success.h"
-#include "scene/ibutton_scene_saved_key_menu.h"
-#include "scene/ibutton_scene_delete_confirm.h"
-#include "scene/ibutton_scene_delete_success.h"
-#include "scene/ibutton_scene_emulate.h"
-#include "scene/ibutton_scene_save_name.h"
-#include "scene/ibutton_scene_save_success.h"
-#include "scene/ibutton_scene_info.h"
-#include "scene/ibutton_scene_select_key.h"
-#include "scene/ibutton_scene_add_type.h"
-#include "scene/ibutton_scene_add_value.h"
-#include <one_wire/ibutton/ibutton_worker.h>
-#include <notification/notification_messages.h>
-#include <storage/storage.h>
-#include <dialogs/dialogs.h>
-#include <record_controller.hpp>
-
-class iButtonApp {
-public:
-    void run(void* args);
-
-    iButtonApp();
-    ~iButtonApp();
-
-    enum class Scene : uint8_t {
-        SceneExit,
-        SceneStart,
-        SceneRead,
-        SceneReadNotKeyError,
-        SceneReadCRCError,
-        SceneReadSuccess,
-        SceneRetryConfirm,
-        SceneExitConfirm,
-        SceneReadKeyMenu,
-        SceneWrite,
-        SceneWriteSuccess,
-        SceneEmulate,
-        SceneSavedKeyMenu,
-        SceneDeleteConfirm,
-        SceneDeleteSuccess,
-        SceneSaveName,
-        SceneSaveSuccess,
-        SceneInfo,
-        SceneSelectKey,
-        SceneAddType,
-        SceneAddValue,
-    };
-
-    static const char* app_folder;
-    static const char* app_extension;
-    static const char* app_filetype;
-
-    iButtonAppViewManager* get_view_manager();
-    void switch_to_next_scene(Scene index);
-    void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
-    bool switch_to_previous_scene(uint8_t count = 1);
-    Scene get_previous_scene();
-
-    const GpioPin* get_ibutton_pin();
-    iButtonWorker* get_key_worker();
-    iButtonKey* get_key();
-
-    void notify_read();
-    void notify_yellow_blink();
-    void notify_emulate();
-
-    void notify_error();
-    void notify_success();
-    void notify_green_on();
-    void notify_green_off();
-    void notify_red_on();
-    void notify_red_off();
-
-    void set_text_store(const char* text...);
-    char* get_text_store();
-    uint8_t get_text_store_size();
-
-    char* get_file_name();
-    uint8_t get_file_name_size();
-
-    bool save_key(const char* key_name);
-    bool load_key();
-    bool load_key(const char* key_name);
-    bool delete_key();
-
-private:
-    std::list<Scene> previous_scenes_list = {Scene::SceneExit};
-    Scene current_scene = Scene::SceneStart;
-    iButtonAppViewManager view;
-
-    std::map<Scene, iButtonScene*> scenes = {
-        {Scene::SceneStart, new iButtonSceneStart()},
-        {Scene::SceneRead, new iButtonSceneRead()},
-        {Scene::SceneReadCRCError, new iButtonSceneReadCRCError()},
-        {Scene::SceneReadNotKeyError, new iButtonSceneReadNotKeyError()},
-        {Scene::SceneReadSuccess, new iButtonSceneReadSuccess()},
-        {Scene::SceneRetryConfirm, new iButtonSceneRetryConfirm()},
-        {Scene::SceneExitConfirm, new iButtonSceneExitConfirm()},
-        {Scene::SceneReadKeyMenu, new iButtonSceneReadKeyMenu()},
-        {Scene::SceneWrite, new iButtonSceneWrite()},
-        {Scene::SceneWriteSuccess, new iButtonSceneWriteSuccess()},
-        {Scene::SceneEmulate, new iButtonSceneEmulate()},
-        {Scene::SceneSavedKeyMenu, new iButtonSceneSavedKeyMenu()},
-        {Scene::SceneDeleteConfirm, new iButtonSceneDeleteConfirm()},
-        {Scene::SceneDeleteSuccess, new iButtonSceneDeleteSuccess()},
-        {Scene::SceneSaveName, new iButtonSceneSaveName()},
-        {Scene::SceneSaveSuccess, new iButtonSceneSaveSuccess()},
-        {Scene::SceneInfo, new iButtonSceneInfo()},
-        {Scene::SceneSelectKey, new iButtonSceneSelectKey()},
-        {Scene::SceneAddType, new iButtonSceneAddType()},
-        {Scene::SceneAddValue, new iButtonSceneAddValue()},
-    };
-
-    iButtonWorker* key_worker;
-    iButtonKey* key;
-
-    RecordController<NotificationApp> notification;
-    RecordController<Storage> storage;
-    RecordController<DialogsApp> dialogs;
-
-    static const uint8_t file_name_size = 100;
-    char file_name[file_name_size];
-
-    static const uint8_t text_store_size = 128;
-    char text_store[text_store_size + 1];
-
-    bool load_key_data(string_t key_path);
-    void make_app_folder();
-};

+ 12 - 0
applications/ibutton/ibutton_custom_event.h

@@ -0,0 +1,12 @@
+#pragma once
+
+enum iButtonCustomEvent {
+    // Reserve first 100 events for button types and indexes, starting from 0
+    iButtonCustomEventReserved = 100,
+
+    iButtonCustomEventBack,
+    iButtonCustomEventTextEditResult,
+    iButtonCustomEventByteEditResult,
+    iButtonCustomEventWorkerEmulated,
+    iButtonCustomEventWorkerRead,
+};

+ 0 - 36
applications/ibutton/ibutton_event.h

@@ -1,36 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <gui/modules/dialog_ex.h>
-#include <gui/modules/widget.h>
-#include <one_wire/ibutton/ibutton_worker.h>
-
-class iButtonApp;
-
-class iButtonEvent {
-public:
-    // events enum
-    enum class Type : uint8_t {
-        EventTypeTick,
-        EventTypeBack,
-        EventTypeMenuSelected,
-        EventTypeDialogResult,
-        EventTypeTextEditResult,
-        EventTypeByteEditResult,
-        EventTypeWidgetButtonResult,
-        EventTypeWorkerEmulated,
-        EventTypeWorkerRead,
-        EventTypeWorkerWrite,
-    };
-
-    // payload
-    union {
-        uint32_t dummy;
-        uint32_t menu_index;
-        DialogExResult dialog_result;
-        GuiButtonType widget_button_result;
-        iButtonWorkerWriteResult worker_write_result;
-    } payload;
-
-    // event type
-    Type type;
-};

+ 86 - 0
applications/ibutton/ibutton_i.h

@@ -0,0 +1,86 @@
+#pragma once
+
+#include "ibutton.h"
+
+#include <gui/gui.h>
+#include <gui/view.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <notification/notification_messages.h>
+
+#include <one_wire/ibutton/ibutton_worker.h>
+#include <storage/storage.h>
+#include <dialogs/dialogs.h>
+
+#include <gui/modules/submenu.h>
+#include <gui/modules/popup.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/modules/text_input.h>
+#include <gui/modules/byte_input.h>
+#include <gui/modules/widget.h>
+
+#include "ibutton_custom_event.h"
+#include "scenes/ibutton_scene.h"
+
+#define IBUTTON_FILE_NAME_SIZE 100
+#define IBUTTON_TEXT_STORE_SIZE 128
+
+#define IBUTTON_APP_FOLDER "/any/ibutton"
+#define IBUTTON_APP_EXTENSION ".ibtn"
+#define IBUTTON_APP_FILE_TYPE "Flipper iButton key"
+
+struct iButton {
+    SceneManager* scene_manager;
+    ViewDispatcher* view_dispatcher;
+
+    Gui* gui;
+    Storage* storage;
+    DialogsApp* dialogs;
+    NotificationApp* notifications;
+
+    iButtonWorker* key_worker;
+    iButtonKey* key;
+
+    char file_name[IBUTTON_FILE_NAME_SIZE];
+    char text_store[IBUTTON_TEXT_STORE_SIZE + 1];
+
+    Submenu* submenu;
+    ByteInput* byte_input;
+    TextInput* text_input;
+    Popup* popup;
+    Widget* widget;
+    DialogEx* dialog_ex;
+};
+
+typedef enum {
+    iButtonViewSubmenu,
+    iButtonViewByteInput,
+    iButtonViewTextInput,
+    iButtonViewPopup,
+    iButtonViewWidget,
+    iButtonViewDialogEx,
+} iButtonView;
+
+typedef enum {
+    iButtonNotificationMessageError,
+    iButtonNotificationMessageSuccess,
+    iButtonNotificationMessageRead,
+    iButtonNotificationMessageEmulate,
+    iButtonNotificationMessageYellowBlink,
+    iButtonNotificationMessageRedOn,
+    iButtonNotificationMessageRedOff,
+    iButtonNotificationMessageGreenOn,
+    iButtonNotificationMessageGreenOff,
+} iButtonNotificationMessage;
+
+bool ibutton_file_select(iButton* ibutton);
+bool ibutton_load_key(iButton* ibutton, const char* key_name);
+bool ibutton_save_key(iButton* ibutton, const char* key_name);
+bool ibutton_delete_key(iButton* ibutton);
+void ibutton_text_store_set(iButton* ibutton, const char* text, ...);
+void ibutton_text_store_clear(iButton* ibutton);
+void ibutton_switch_to_previous_scene_one_of(
+    iButton* ibutton,
+    const uint32_t* scene_ids,
+    size_t scene_ids_size);
+void ibutton_notification_message(iButton* ibutton, uint32_t message);

+ 0 - 140
applications/ibutton/ibutton_view_manager.cpp

@@ -1,140 +0,0 @@
-#include "ibutton_view_manager.h"
-#include "ibutton_event.h"
-#include <callback-connector.h>
-
-iButtonAppViewManager::iButtonAppViewManager() {
-    event_queue = osMessageQueueNew(10, sizeof(iButtonEvent), NULL);
-
-    view_dispatcher = view_dispatcher_alloc();
-    auto callback = cbc::obtain_connector(this, &iButtonAppViewManager::previous_view_callback);
-
-    dialog_ex = dialog_ex_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewDialogEx),
-        dialog_ex_get_view(dialog_ex));
-
-    submenu = submenu_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewSubmenu),
-        submenu_get_view(submenu));
-
-    text_input = text_input_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewTextInput),
-        text_input_get_view(text_input));
-
-    byte_input = byte_input_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewByteInput),
-        byte_input_get_view(byte_input));
-
-    popup = popup_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewPopup),
-        popup_get_view(popup));
-
-    widget = widget_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewWidget),
-        widget_get_view(widget));
-
-    gui = static_cast<Gui*>(furi_record_open("gui"));
-    view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
-
-    //TODO think about that method, seems unsafe and over-engineered
-    view_set_previous_callback(dialog_ex_get_view(dialog_ex), callback);
-    view_set_previous_callback(submenu_get_view(submenu), callback);
-    view_set_previous_callback(text_input_get_view(text_input), callback);
-    view_set_previous_callback(byte_input_get_view(byte_input), callback);
-    view_set_previous_callback(popup_get_view(popup), callback);
-    view_set_previous_callback(widget_get_view(widget), callback);
-}
-
-iButtonAppViewManager::~iButtonAppViewManager() {
-    // remove views
-    view_dispatcher_remove_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewDialogEx));
-    view_dispatcher_remove_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewSubmenu));
-    view_dispatcher_remove_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewTextInput));
-    view_dispatcher_remove_view(
-        view_dispatcher, static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewPopup));
-    view_dispatcher_remove_view(
-        view_dispatcher,
-        static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewByteInput));
-    view_dispatcher_remove_view(
-        view_dispatcher, static_cast<uint32_t>(iButtonAppViewManager::Type::iButtonAppViewWidget));
-
-    // free view modules
-    popup_free(popup);
-    text_input_free(text_input);
-    byte_input_free(byte_input);
-    submenu_free(submenu);
-    dialog_ex_free(dialog_ex);
-    widget_free(widget);
-
-    // free dispatcher
-    view_dispatcher_free(view_dispatcher);
-
-    // free event queue
-    osMessageQueueDelete(event_queue);
-}
-
-void iButtonAppViewManager::switch_to(Type type) {
-    view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
-}
-
-Submenu* iButtonAppViewManager::get_submenu() {
-    return submenu;
-}
-
-Popup* iButtonAppViewManager::get_popup() {
-    return popup;
-}
-
-DialogEx* iButtonAppViewManager::get_dialog_ex() {
-    return dialog_ex;
-}
-
-TextInput* iButtonAppViewManager::get_text_input() {
-    return text_input;
-}
-
-ByteInput* iButtonAppViewManager::get_byte_input() {
-    return byte_input;
-}
-
-Widget* iButtonAppViewManager::get_widget() {
-    return widget;
-}
-
-void iButtonAppViewManager::receive_event(iButtonEvent* event) {
-    if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
-        event->type = iButtonEvent::Type::EventTypeTick;
-    }
-}
-
-void iButtonAppViewManager::send_event(iButtonEvent* event) {
-    osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0);
-    furi_check(result == osOK);
-}
-
-uint32_t iButtonAppViewManager::previous_view_callback(void*) {
-    if(event_queue != NULL) {
-        iButtonEvent event;
-        event.type = iButtonEvent::Type::EventTypeBack;
-        send_event(&event);
-    }
-
-    return VIEW_IGNORE;
-}

+ 0 - 51
applications/ibutton/ibutton_view_manager.h

@@ -1,51 +0,0 @@
-#pragma once
-#include <furi.h>
-#include <gui/view_dispatcher.h>
-#include <gui/modules/dialog_ex.h>
-#include <gui/modules/submenu.h>
-#include <gui/modules/text_input.h>
-#include <gui/modules/byte_input.h>
-#include <gui/modules/popup.h>
-#include <gui/modules/widget.h>
-#include "ibutton_event.h"
-
-class iButtonAppViewManager {
-public:
-    enum class Type : uint8_t {
-        iButtonAppViewTextInput,
-        iButtonAppViewByteInput,
-        iButtonAppViewSubmenu,
-        iButtonAppViewDialogEx,
-        iButtonAppViewPopup,
-        iButtonAppViewWidget,
-    };
-
-    osMessageQueueId_t event_queue;
-
-    iButtonAppViewManager();
-    ~iButtonAppViewManager();
-
-    void switch_to(Type type);
-
-    Submenu* get_submenu();
-    Popup* get_popup();
-    DialogEx* get_dialog_ex();
-    TextInput* get_text_input();
-    ByteInput* get_byte_input();
-    Widget* get_widget();
-
-    void receive_event(iButtonEvent* event);
-    void send_event(iButtonEvent* event);
-
-private:
-    ViewDispatcher* view_dispatcher;
-    DialogEx* dialog_ex;
-    Submenu* submenu;
-    TextInput* text_input;
-    ByteInput* byte_input;
-    Popup* popup;
-    Widget* widget;
-    Gui* gui;
-
-    uint32_t previous_view_callback(void* context);
-};

+ 0 - 66
applications/ibutton/scene/ibutton_scene_add_type.cpp

@@ -1,66 +0,0 @@
-#include "ibutton_scene_add_type.h"
-#include "../ibutton_app.h"
-#include <callback-connector.h>
-
-typedef enum {
-    SubmenuIndexCyfral,
-    SubmenuIndexDallas,
-    SubmenuIndexMetakom,
-} SubmenuIndex;
-
-static void submenu_callback(void* context, uint32_t index) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneAddType::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-
-    submenu_add_item(submenu, "Cyfral", SubmenuIndexCyfral, submenu_callback, app);
-    submenu_add_item(submenu, "Dallas", SubmenuIndexDallas, submenu_callback, app);
-    submenu_add_item(submenu, "Metakom", SubmenuIndexMetakom, submenu_callback, app);
-    submenu_set_selected_item(submenu, submenu_item_selected);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
-}
-
-bool iButtonSceneAddType::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeMenuSelected) {
-        submenu_item_selected = event->payload.menu_index;
-        iButtonKey* key = app->get_key();
-
-        switch(event->payload.menu_index) {
-        case SubmenuIndexCyfral:
-            ibutton_key_set_type(key, iButtonKeyCyfral);
-            break;
-        case SubmenuIndexDallas:
-            ibutton_key_set_type(key, iButtonKeyDS1990);
-            break;
-        case SubmenuIndexMetakom:
-            ibutton_key_set_type(key, iButtonKeyMetakom);
-            break;
-        }
-        ibutton_key_set_name(key, "");
-        ibutton_key_clear_data(key);
-        app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneAddType::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view = app->get_view_manager();
-    Submenu* submenu = view->get_submenu();
-
-    submenu_reset(submenu);
-}

+ 0 - 12
applications/ibutton/scene/ibutton_scene_add_type.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneAddType : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    uint32_t submenu_item_selected = 0;
-};

+ 0 - 53
applications/ibutton/scene/ibutton_scene_add_value.cpp

@@ -1,53 +0,0 @@
-#include "ibutton_scene_add_value.h"
-#include "../ibutton_app.h"
-#include <dolphin/dolphin.h>
-
-static void byte_input_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeByteEditResult;
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneAddValue::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    ByteInput* byte_input = view_manager->get_byte_input();
-    iButtonKey* key = app->get_key();
-
-    memcpy(this->new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
-
-    byte_input_set_result_callback(
-        byte_input,
-        byte_input_callback,
-        NULL,
-        app,
-        this->new_key_data,
-        ibutton_key_get_data_size(key));
-
-    byte_input_set_header_text(byte_input, "Enter the key");
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewByteInput);
-}
-
-bool iButtonSceneAddValue::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeByteEditResult) {
-        ibutton_key_set_data(app->get_key(), this->new_key_data, IBUTTON_KEY_DATA_SIZE);
-        DOLPHIN_DEED(DolphinDeedIbuttonAdd);
-        app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneAddValue::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    ByteInput* byte_input = view_manager->get_byte_input();
-
-    byte_input_set_result_callback(byte_input, NULL, NULL, NULL, NULL, 0);
-    byte_input_set_header_text(byte_input, {0});
-}

+ 0 - 13
applications/ibutton/scene/ibutton_scene_add_value.h

@@ -1,13 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-#include <one_wire/ibutton/ibutton_key.h>
-
-class iButtonSceneAddValue : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    uint8_t new_key_data[IBUTTON_KEY_DATA_SIZE] = {};
-};

+ 0 - 86
applications/ibutton/scene/ibutton_scene_delete_confirm.cpp

@@ -1,86 +0,0 @@
-#include "ibutton_scene_delete_confirm.h"
-#include "../ibutton_app.h"
-
-static void widget_callback(GuiButtonType result, InputType type, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    if(type == InputTypeShort) {
-        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
-        event.payload.widget_button_result = result;
-        app->get_view_manager()->send_event(&event);
-    }
-}
-
-void iButtonSceneDeleteConfirm::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-    iButtonKey* key = app->get_key();
-    const uint8_t* key_data = ibutton_key_get_data_p(key);
-
-    app->set_text_store("\e#Delete %s?\e#", ibutton_key_get_name_p(key));
-    widget_add_text_box_element(
-        widget, 0, 0, 128, 27, AlignCenter, AlignCenter, app->get_text_store(), false);
-    widget_add_button_element(widget, GuiButtonTypeLeft, "Cancel", widget_callback, app);
-    widget_add_button_element(widget, GuiButtonTypeRight, "Delete", widget_callback, app);
-
-    switch(ibutton_key_get_type(key)) {
-    case iButtonKeyDS1990:
-        app->set_text_store(
-            "%02X %02X %02X %02X %02X %02X %02X %02X",
-            key_data[0],
-            key_data[1],
-            key_data[2],
-            key_data[3],
-            key_data[4],
-            key_data[5],
-            key_data[6],
-            key_data[7]);
-        widget_add_string_element(
-            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas");
-        break;
-    case iButtonKeyCyfral:
-        app->set_text_store("%02X %02X", key_data[0], key_data[1]);
-        widget_add_string_element(
-            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
-        break;
-    case iButtonKeyMetakom:
-        app->set_text_store(
-            "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
-        widget_add_string_element(
-            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom");
-        break;
-    }
-    widget_add_string_element(
-        widget, 64, 33, AlignCenter, AlignBottom, FontSecondary, app->get_text_store());
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
-}
-
-bool iButtonSceneDeleteConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
-        if(event->payload.widget_button_result == GuiButtonTypeRight) {
-            if(app->delete_key()) {
-                app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteSuccess);
-            }
-        } else {
-            app->switch_to_previous_scene();
-        }
-
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneDeleteConfirm::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-
-    app->set_text_store("");
-
-    widget_reset(widget);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_delete_confirm.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneDeleteConfirm : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 47
applications/ibutton/scene/ibutton_scene_delete_success.cpp

@@ -1,47 +0,0 @@
-#include "ibutton_scene_delete_success.h"
-#include "../ibutton_app.h"
-
-static void popup_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneDeleteSuccess::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-
-    popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
-    popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
-
-    popup_set_callback(popup, popup_callback);
-    popup_set_context(popup, app);
-    popup_set_timeout(popup, 1500);
-    popup_enable_timeout(popup);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-}
-
-bool iButtonSceneDeleteSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeBack) {
-        app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneSelectKey});
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneDeleteSuccess::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-
-    popup_disable_timeout(popup);
-    popup_set_context(popup, NULL);
-    popup_set_callback(popup, NULL);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_delete_success.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneDeleteSuccess : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 9
applications/ibutton/scene/ibutton_scene_emulate.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneEmulate : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 51
applications/ibutton/scene/ibutton_scene_exit_confirm.cpp

@@ -1,51 +0,0 @@
-#include "ibutton_scene_exit_confirm.h"
-#include "../ibutton_app.h"
-
-static void widget_callback(GuiButtonType result, InputType type, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    if(type == InputTypeShort) {
-        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
-        event.payload.widget_button_result = result;
-        app->get_view_manager()->send_event(&event);
-    }
-}
-
-void iButtonSceneExitConfirm::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-
-    widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", widget_callback, app);
-    widget_add_button_element(widget, GuiButtonTypeRight, "Stay", widget_callback, app);
-    widget_add_string_element(
-        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu");
-    widget_add_string_element(
-        widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
-}
-
-bool iButtonSceneExitConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
-        if(event->payload.widget_button_result == GuiButtonTypeLeft) {
-            app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneStart});
-        } else if(event->payload.widget_button_result == GuiButtonTypeRight) {
-            app->switch_to_previous_scene();
-        }
-        consumed = true;
-    } else if(event->type == iButtonEvent::Type::EventTypeBack) {
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneExitConfirm::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-    widget_reset(widget);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_exit_confirm.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneExitConfirm : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 14
applications/ibutton/scene/ibutton_scene_generic.h

@@ -1,14 +0,0 @@
-#pragma once
-#include "../ibutton_event.h"
-
-class iButtonApp;
-
-class iButtonScene {
-public:
-    virtual void on_enter(iButtonApp* app) = 0;
-    virtual bool on_event(iButtonApp* app, iButtonEvent* event) = 0;
-    virtual void on_exit(iButtonApp* app) = 0;
-    virtual ~iButtonScene(){};
-
-private:
-};

+ 0 - 58
applications/ibutton/scene/ibutton_scene_info.cpp

@@ -1,58 +0,0 @@
-#include "ibutton_scene_info.h"
-#include "../ibutton_app.h"
-
-void iButtonSceneInfo::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-    iButtonKey* key = app->get_key();
-    const uint8_t* key_data = ibutton_key_get_data_p(key);
-
-    app->set_text_store("%s", ibutton_key_get_name_p(key));
-    widget_add_text_box_element(
-        widget, 0, 0, 128, 28, AlignCenter, AlignCenter, app->get_text_store(), false);
-
-    switch(ibutton_key_get_type(key)) {
-    case iButtonKeyDS1990:
-        app->set_text_store(
-            "%02X %02X %02X %02X %02X %02X %02X %02X",
-            key_data[0],
-            key_data[1],
-            key_data[2],
-            key_data[3],
-            key_data[4],
-            key_data[5],
-            key_data[6],
-            key_data[7]);
-        widget_add_string_element(
-            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Dallas");
-        break;
-    case iButtonKeyMetakom:
-        app->set_text_store(
-            "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
-        widget_add_string_element(
-            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Metakom");
-        break;
-    case iButtonKeyCyfral:
-        app->set_text_store("%02X %02X", key_data[0], key_data[1]);
-        widget_add_string_element(
-            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
-        break;
-    }
-    widget_add_string_element(
-        widget, 64, 35, AlignCenter, AlignBottom, FontPrimary, app->get_text_store());
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
-}
-
-bool iButtonSceneInfo::on_event(iButtonApp*, iButtonEvent*) {
-    return false;
-}
-
-void iButtonSceneInfo::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-
-    app->set_text_store("");
-
-    widget_reset(widget);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_info.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneInfo : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 72
applications/ibutton/scene/ibutton_scene_read.cpp

@@ -1,72 +0,0 @@
-#include "ibutton_scene_read.h"
-#include "../ibutton_app.h"
-#include <dolphin/dolphin.h>
-
-static void read_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event = {
-        .payload = {.dummy = 0},
-        .type = iButtonEvent::Type::EventTypeWorkerRead,
-    };
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneRead::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-    iButtonKey* key = app->get_key();
-    iButtonWorker* worker = app->get_key_worker();
-    DOLPHIN_DEED(DolphinDeedIbuttonRead);
-
-    popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
-    popup_set_text(popup, "waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-    ibutton_key_set_name(key, "");
-
-    ibutton_worker_read_set_callback(worker, read_callback, app);
-    ibutton_worker_read_start(worker, key);
-}
-
-bool iButtonSceneRead::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeWorkerRead) {
-        consumed = true;
-
-        iButtonKey* key = app->get_key();
-        bool success = false;
-        if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
-            if(!ibutton_key_dallas_crc_is_valid(key)) {
-                app->switch_to_next_scene(iButtonApp::Scene::SceneReadCRCError);
-            } else if(!ibutton_key_dallas_is_1990_key(key)) {
-                app->switch_to_next_scene(iButtonApp::Scene::SceneReadNotKeyError);
-            } else {
-                success = true;
-            }
-        } else {
-            success = true;
-        }
-        if(success) {
-            app->notify_success();
-            app->notify_green_on();
-            DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadSuccess);
-        }
-    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
-        consumed = true;
-        app->notify_read();
-    }
-
-    return consumed;
-}
-
-void iButtonSceneRead::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-    ibutton_worker_stop(app->get_key_worker());
-    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_read.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneRead : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 74
applications/ibutton/scene/ibutton_scene_read_crc_error.cpp

@@ -1,74 +0,0 @@
-#include "ibutton_scene_read_crc_error.h"
-#include "../ibutton_app.h"
-#include <one_wire/maxim_crc.h>
-
-static void dialog_ex_callback(DialogExResult result, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneReadCRCError::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    const uint8_t* key_data = ibutton_key_get_data_p(app->get_key());
-
-    app->set_text_store(
-        "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
-        key_data[0],
-        key_data[1],
-        key_data[2],
-        key_data[3],
-        key_data[4],
-        key_data[5],
-        key_data[6],
-        key_data[7],
-        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
-    dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
-    dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop);
-    dialog_ex_set_left_button_text(dialog_ex, "Retry");
-    dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
-    dialog_ex_set_context(dialog_ex, app);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
-    app->notify_error();
-    app->notify_red_on();
-}
-
-bool iButtonSceneReadCRCError::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
-        if(event->payload.dialog_result == DialogExResultRight) {
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
-        } else {
-            app->switch_to_previous_scene();
-        }
-
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneReadCRCError::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-
-    app->set_text_store("");
-
-    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
-    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
-    dialog_ex_set_left_button_text(dialog_ex, NULL);
-    dialog_ex_set_result_callback(dialog_ex, NULL);
-    dialog_ex_set_context(dialog_ex, NULL);
-
-    app->notify_red_off();
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_read_crc_error.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneReadCRCError : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 65
applications/ibutton/scene/ibutton_scene_read_key_menu.cpp

@@ -1,65 +0,0 @@
-#include "ibutton_scene_read_key_menu.h"
-#include "../ibutton_app.h"
-
-typedef enum {
-    SubmenuIndexSave,
-    SubmenuIndexEmulate,
-    SubmenuIndexWrite,
-} SubmenuIndex;
-
-static void submenu_callback(void* context, uint32_t index) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneReadKeyMenu::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-
-    submenu_add_item(submenu, "Save", SubmenuIndexSave, submenu_callback, app);
-    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app);
-    if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) {
-        submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app);
-    }
-    submenu_set_selected_item(submenu, submenu_item_selected);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
-}
-
-bool iButtonSceneReadKeyMenu::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);
-            break;
-        case SubmenuIndexEmulate:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate);
-            break;
-        case SubmenuIndexSave:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneSaveName);
-            break;
-        }
-        consumed = true;
-    } else if(event->type == iButtonEvent::Type::EventTypeBack) {
-        app->switch_to_previous_scene();
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneReadKeyMenu::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view = app->get_view_manager();
-    Submenu* submenu = view->get_submenu();
-
-    submenu_reset(submenu);
-}

+ 0 - 12
applications/ibutton/scene/ibutton_scene_read_key_menu.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneReadKeyMenu : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    uint32_t submenu_item_selected = 0;
-};

+ 0 - 74
applications/ibutton/scene/ibutton_scene_read_not_key_error.cpp

@@ -1,74 +0,0 @@
-#include "ibutton_scene_read_not_key_error.h"
-#include "../ibutton_app.h"
-#include <one_wire/maxim_crc.h>
-
-static void dialog_ex_callback(DialogExResult result, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneReadNotKeyError::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    const uint8_t* key_data = ibutton_key_get_data_p(app->get_key());
-
-    app->set_text_store(
-        "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
-        key_data[0],
-        key_data[1],
-        key_data[2],
-        key_data[3],
-        key_data[4],
-        key_data[5],
-        key_data[6],
-        key_data[7],
-        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
-
-    dialog_ex_set_header(dialog_ex, "ERROR:", 64, 10, AlignCenter, AlignCenter);
-    dialog_ex_set_text(dialog_ex, app->get_text_store(), 64, 19, AlignCenter, AlignTop);
-    dialog_ex_set_left_button_text(dialog_ex, "Retry");
-    dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
-    dialog_ex_set_context(dialog_ex, app);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
-    app->notify_error();
-    app->notify_red_on();
-}
-
-bool iButtonSceneReadNotKeyError::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
-        if(event->payload.dialog_result == DialogExResultRight) {
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
-        } else {
-            app->switch_to_previous_scene();
-        }
-
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneReadNotKeyError::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-
-    app->set_text_store("");
-
-    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
-    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
-    dialog_ex_set_left_button_text(dialog_ex, NULL);
-    dialog_ex_set_result_callback(dialog_ex, NULL);
-    dialog_ex_set_context(dialog_ex, NULL);
-
-    app->notify_red_off();
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_read_not_key_error.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneReadNotKeyError : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 86
applications/ibutton/scene/ibutton_scene_read_success.cpp

@@ -1,86 +0,0 @@
-#include "ibutton_scene_read_success.h"
-#include "../ibutton_app.h"
-#include <dolphin/dolphin.h>
-
-static void dialog_ex_callback(DialogExResult result, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeDialogResult;
-    event.payload.dialog_result = result;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneReadSuccess::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-    iButtonKey* key = app->get_key();
-    const uint8_t* key_data = ibutton_key_get_data_p(key);
-
-    switch(ibutton_key_get_type(key)) {
-    case iButtonKeyDS1990:
-        app->set_text_store(
-            "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
-            key_data[0],
-            key_data[1],
-            key_data[2],
-            key_data[3],
-            key_data[4],
-            key_data[5],
-            key_data[6],
-            key_data[7]);
-        break;
-    case iButtonKeyCyfral:
-        app->set_text_store("Cyfral\n%02X %02X", key_data[0], key_data[1]);
-        break;
-    case iButtonKeyMetakom:
-        app->set_text_store(
-            "Metakom\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
-        break;
-    }
-
-    dialog_ex_set_text(dialog_ex, app->get_text_store(), 95, 30, AlignCenter, AlignCenter);
-    dialog_ex_set_left_button_text(dialog_ex, "Retry");
-    dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
-    dialog_ex_set_result_callback(dialog_ex, dialog_ex_callback);
-    dialog_ex_set_context(dialog_ex, app);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewDialogEx);
-}
-
-bool iButtonSceneReadSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeDialogResult) {
-        if(event->payload.dialog_result == DialogExResultRight) {
-            app->switch_to_next_scene(iButtonApp::Scene::SceneReadKeyMenu);
-        } else {
-            app->switch_to_next_scene(iButtonApp::Scene::SceneRetryConfirm);
-        }
-        consumed = true;
-    } else if(event->type == iButtonEvent::Type::EventTypeBack) {
-        app->switch_to_next_scene(iButtonApp::Scene::SceneExitConfirm);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneReadSuccess::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    DialogEx* dialog_ex = view_manager->get_dialog_ex();
-
-    app->set_text_store("");
-
-    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
-    dialog_ex_set_left_button_text(dialog_ex, NULL);
-    dialog_ex_set_right_button_text(dialog_ex, NULL);
-    dialog_ex_set_result_callback(dialog_ex, NULL);
-    dialog_ex_set_context(dialog_ex, NULL);
-    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
-
-    app->notify_green_off();
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_read_success.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneReadSuccess : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 51
applications/ibutton/scene/ibutton_scene_retry_confirm.cpp

@@ -1,51 +0,0 @@
-#include "ibutton_scene_retry_confirm.h"
-#include "../ibutton_app.h"
-
-static void widget_callback(GuiButtonType result, InputType type, void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    if(type == InputTypeShort) {
-        event.type = iButtonEvent::Type::EventTypeWidgetButtonResult;
-        event.payload.widget_button_result = result;
-        app->get_view_manager()->send_event(&event);
-    }
-}
-
-void iButtonSceneRetryConfirm::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-
-    widget_add_button_element(widget, GuiButtonTypeLeft, "Exit", widget_callback, app);
-    widget_add_button_element(widget, GuiButtonTypeRight, "Stay", widget_callback, app);
-    widget_add_string_element(
-        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
-    widget_add_string_element(
-        widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewWidget);
-}
-
-bool iButtonSceneRetryConfirm::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeWidgetButtonResult) {
-        if(event->payload.widget_button_result == GuiButtonTypeLeft) {
-            app->search_and_switch_to_previous_scene({iButtonApp::Scene::SceneRead});
-        } else if(event->payload.widget_button_result == GuiButtonTypeRight) {
-            app->switch_to_previous_scene();
-        }
-        consumed = true;
-    } else if(event->type == iButtonEvent::Type::EventTypeBack) {
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneRetryConfirm::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Widget* widget = view_manager->get_widget();
-    widget_reset(widget);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_retry_confirm.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneRetryConfirm : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

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

@@ -1,70 +0,0 @@
-#include "ibutton_scene_save_name.h"
-#include "../ibutton_app.h"
-#include <lib/toolbox/random_name.h>
-
-static void text_input_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeTextEditResult;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneSaveName::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    TextInput* text_input = view_manager->get_text_input();
-
-    const char* key_name = ibutton_key_get_name_p(app->get_key());
-    bool key_name_empty = !strcmp(key_name, "");
-
-    if(key_name_empty) {
-        set_random_name(app->get_text_store(), app->get_text_store_size());
-    } else {
-        app->set_text_store("%s", key_name);
-    }
-
-    text_input_set_header_text(text_input, "Name the key");
-    text_input_set_result_callback(
-        text_input,
-        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, key_name);
-    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewTextInput);
-}
-
-bool iButtonSceneSaveName::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeTextEditResult) {
-        if(app->save_key(app->get_text_store())) {
-            app->switch_to_next_scene(iButtonApp::Scene::SceneSaveSuccess);
-        } else {
-            app->search_and_switch_to_previous_scene(
-                {iButtonApp::Scene::SceneReadKeyMenu,
-                 iButtonApp::Scene::SceneSavedKeyMenu,
-                 iButtonApp::Scene::SceneAddType});
-        }
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneSaveName::on_exit(iButtonApp* 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);
-    validator_is_file_free((ValidatorIsFile*)validator_context);
-
-    text_input_reset(text_input);
-}

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

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneSaveName : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 52
applications/ibutton/scene/ibutton_scene_save_success.cpp

@@ -1,52 +0,0 @@
-#include "ibutton_scene_save_success.h"
-#include "../ibutton_app.h"
-#include <dolphin/dolphin.h>
-
-static void popup_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneSaveSuccess::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-    DOLPHIN_DEED(DolphinDeedIbuttonSave);
-
-    popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
-    popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
-
-    popup_set_callback(popup, popup_callback);
-    popup_set_context(popup, app);
-    popup_set_timeout(popup, 1500);
-    popup_enable_timeout(popup);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-}
-
-bool iButtonSceneSaveSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeBack) {
-        app->search_and_switch_to_previous_scene(
-            {iButtonApp::Scene::SceneReadKeyMenu,
-             iButtonApp::Scene::SceneSavedKeyMenu,
-             iButtonApp::Scene::SceneAddType});
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneSaveSuccess::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-
-    popup_disable_timeout(popup);
-    popup_set_context(popup, NULL);
-    popup_set_callback(popup, NULL);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_save_success.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneSaveSuccess : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 73
applications/ibutton/scene/ibutton_scene_saved_key_menu.cpp

@@ -1,73 +0,0 @@
-#include "ibutton_scene_saved_key_menu.h"
-#include "../ibutton_app.h"
-#include <callback-connector.h>
-
-typedef enum {
-    SubmenuIndexEmulate,
-    SubmenuIndexWrite,
-    SubmenuIndexEdit,
-    SubmenuIndexDelete,
-    SubmenuIndexInfo,
-} SubmenuIndex;
-
-static void submenu_callback(void* context, uint32_t index) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneSavedKeyMenu::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-
-    submenu_add_item(submenu, "Emulate", SubmenuIndexEmulate, submenu_callback, app);
-    if(ibutton_key_get_type(app->get_key()) == iButtonKeyDS1990) {
-        submenu_add_item(submenu, "Write", SubmenuIndexWrite, submenu_callback, app);
-    }
-    submenu_add_item(submenu, "Edit", SubmenuIndexEdit, submenu_callback, app);
-    submenu_add_item(submenu, "Delete", SubmenuIndexDelete, submenu_callback, app);
-    submenu_add_item(submenu, "Info", SubmenuIndexInfo, submenu_callback, app);
-    submenu_set_selected_item(submenu, submenu_item_selected);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
-}
-
-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);
-            break;
-        case SubmenuIndexEmulate:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneEmulate);
-            break;
-        case SubmenuIndexEdit:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneAddValue);
-            break;
-        case SubmenuIndexDelete:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneDeleteConfirm);
-            break;
-        case SubmenuIndexInfo:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneInfo);
-            break;
-        }
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneSavedKeyMenu::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view = app->get_view_manager();
-    Submenu* submenu = view->get_submenu();
-
-    submenu_reset(submenu);
-}

+ 0 - 12
applications/ibutton/scene/ibutton_scene_saved_key_menu.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneSavedKeyMenu : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    uint32_t submenu_item_selected = 0;
-};

+ 0 - 18
applications/ibutton/scene/ibutton_scene_select_key.cpp

@@ -1,18 +0,0 @@
-#include "ibutton_scene_select_key.h"
-#include "../ibutton_app.h"
-
-void iButtonSceneSelectKey::on_enter(iButtonApp* app) {
-    // Process file_select return
-    if(app->load_key()) {
-        app->switch_to_next_scene(iButtonApp::Scene::SceneSavedKeyMenu);
-    } else {
-        app->switch_to_previous_scene();
-    }
-}
-
-bool iButtonSceneSelectKey::on_event(iButtonApp*, iButtonEvent*) {
-    return false;
-}
-
-void iButtonSceneSelectKey::on_exit(iButtonApp*) {
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_select_key.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneSelectKey : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 0 - 60
applications/ibutton/scene/ibutton_scene_start.cpp

@@ -1,60 +0,0 @@
-#include "ibutton_scene_start.h"
-#include "../ibutton_app.h"
-
-typedef enum {
-    SubmenuIndexRead,
-    SubmenuIndexSaved,
-    SubmenuIndexAdd,
-} SubmenuIndex;
-
-static void submenu_callback(void* context, uint32_t index) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-
-    event.type = iButtonEvent::Type::EventTypeMenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneStart::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-
-    submenu_add_item(submenu, "Read", SubmenuIndexRead, submenu_callback, app);
-    submenu_add_item(submenu, "Saved", SubmenuIndexSaved, submenu_callback, app);
-    submenu_add_item(submenu, "Add Manually", SubmenuIndexAdd, submenu_callback, app);
-    submenu_set_selected_item(submenu, submenu_item_selected);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewSubmenu);
-}
-
-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);
-            break;
-        case SubmenuIndexSaved:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneSelectKey);
-            break;
-        case SubmenuIndexAdd:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneAddType);
-            break;
-        }
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneStart::on_exit(iButtonApp* app) {
-    iButtonAppViewManager* view = app->get_view_manager();
-    Submenu* submenu = view->get_submenu();
-
-    submenu_reset(submenu);
-}

+ 0 - 12
applications/ibutton/scene/ibutton_scene_start.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneStart : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    uint32_t submenu_item_selected = 0;
-};

+ 0 - 111
applications/ibutton/scene/ibutton_scene_write.cpp

@@ -1,111 +0,0 @@
-#include "ibutton_scene_write.h"
-#include "../ibutton_app.h"
-
-static void ibutton_worker_write_cb(void* context, iButtonWorkerWriteResult result) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeWorkerWrite;
-    event.payload.worker_write_result = result;
-
-    app->get_view_manager()->send_event(&event);
-}
-
-void iButtonSceneWrite::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-    iButtonKey* key = app->get_key();
-    iButtonWorker* worker = app->get_key_worker();
-    const uint8_t* key_data = ibutton_key_get_data_p(key);
-    const char* key_name = ibutton_key_get_name_p(key);
-    uint8_t line_count = 2;
-
-    // check that stored key has name
-    if(strcmp(key_name, "") != 0) {
-        app->set_text_store("writing\n%s", key_name);
-        line_count = 2;
-    } else {
-        // if not, show key data
-        switch(ibutton_key_get_type(key)) {
-        case iButtonKeyDS1990:
-            app->set_text_store(
-                "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
-                key_data[0],
-                key_data[1],
-                key_data[2],
-                key_data[3],
-                key_data[4],
-                key_data[5],
-                key_data[6],
-                key_data[7]);
-            line_count = 3;
-            break;
-        case iButtonKeyCyfral:
-            app->set_text_store("writing\n%02X %02X", key_data[0], key_data[1]);
-            line_count = 2;
-            break;
-        case iButtonKeyMetakom:
-            app->set_text_store(
-                "writing\n%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
-            line_count = 2;
-            break;
-        }
-    }
-
-    switch(line_count) {
-    case 3:
-        popup_set_header(popup, "iButton", 82, 18, AlignCenter, AlignBottom);
-        popup_set_text(popup, app->get_text_store(), 82, 22, AlignCenter, AlignTop);
-        break;
-
-    default:
-        popup_set_header(popup, "iButton", 82, 24, AlignCenter, AlignBottom);
-        popup_set_text(popup, app->get_text_store(), 82, 28, AlignCenter, AlignTop);
-        break;
-    }
-
-    popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-
-    blink_yellow = false;
-    ibutton_worker_write_set_callback(worker, ibutton_worker_write_cb, app);
-    ibutton_worker_write_start(worker, key);
-}
-
-bool iButtonSceneWrite::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeWorkerWrite) {
-        consumed = true;
-
-        switch(event->payload.worker_write_result) {
-        case iButtonWorkerWriteOK:
-        case iButtonWorkerWriteSameKey:
-            app->switch_to_next_scene(iButtonApp::Scene::SceneWriteSuccess);
-            break;
-        case iButtonWorkerWriteNoDetect:
-            blink_yellow = false;
-            break;
-        case iButtonWorkerWriteCannotWrite:
-            blink_yellow = true;
-            break;
-        }
-    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
-        if(blink_yellow) {
-            app->notify_yellow_blink();
-        } else {
-            app->notify_emulate();
-        }
-    }
-
-    return consumed;
-}
-
-void iButtonSceneWrite::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-    ibutton_worker_stop(app->get_key_worker());
-    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-}

+ 0 - 12
applications/ibutton/scene/ibutton_scene_write.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneWrite : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-
-private:
-    bool blink_yellow;
-};

+ 0 - 52
applications/ibutton/scene/ibutton_scene_write_success.cpp

@@ -1,52 +0,0 @@
-#include "ibutton_scene_write_success.h"
-#include "../ibutton_app.h"
-
-static void popup_callback(void* context) {
-    furi_assert(context);
-    iButtonApp* app = static_cast<iButtonApp*>(context);
-    iButtonEvent event;
-    event.type = iButtonEvent::Type::EventTypeBack;
-    app->get_view_manager()->send_event(&event);
-    app->notify_green_off();
-}
-
-void iButtonSceneWriteSuccess::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-
-    popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52);
-    popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
-
-    popup_set_callback(popup, popup_callback);
-    popup_set_context(popup, app);
-    popup_set_timeout(popup, 1500);
-    popup_enable_timeout(popup);
-
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
-
-    app->notify_success();
-    app->notify_green_on();
-}
-
-bool iButtonSceneWriteSuccess::on_event(iButtonApp* app, iButtonEvent* event) {
-    bool consumed = false;
-
-    if(event->type == iButtonEvent::Type::EventTypeBack) {
-        app->search_and_switch_to_previous_scene(
-            {iButtonApp::Scene::SceneReadKeyMenu, iButtonApp::Scene::SceneStart});
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void iButtonSceneWriteSuccess::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-
-    popup_disable_timeout(popup);
-    popup_set_context(popup, NULL);
-    popup_set_callback(popup, NULL);
-}

+ 0 - 9
applications/ibutton/scene/ibutton_scene_write_success.h

@@ -1,9 +0,0 @@
-#pragma once
-#include "ibutton_scene_generic.h"
-
-class iButtonSceneWriteSuccess : public iButtonScene {
-public:
-    void on_enter(iButtonApp* app) final;
-    bool on_event(iButtonApp* app, iButtonEvent* event) final;
-    void on_exit(iButtonApp* app) final;
-};

+ 30 - 0
applications/ibutton/scenes/ibutton_scene.c

@@ -0,0 +1,30 @@
+#include "ibutton_scene.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const ibutton_on_enter_handlers[])(void*) = {
+#include "ibutton_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const ibutton_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "ibutton_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const ibutton_on_exit_handlers[])(void* context) = {
+#include "ibutton_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers ibutton_scene_handlers = {
+    .on_enter_handlers = ibutton_on_enter_handlers,
+    .on_event_handlers = ibutton_on_event_handlers,
+    .on_exit_handlers = ibutton_on_exit_handlers,
+    .scene_num = iButtonSceneNum,
+};

+ 29 - 0
applications/ibutton/scenes/ibutton_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) iButtonScene##id,
+typedef enum {
+#include "ibutton_scene_config.h"
+    iButtonSceneNum,
+} iButtonScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers ibutton_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "ibutton_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_event handlers declaration
+#define ADD_SCENE(prefix, name, id) \
+    bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event);
+#include "ibutton_scene_config.h"
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context);
+#include "ibutton_scene_config.h"
+#undef ADD_SCENE

+ 58 - 0
applications/ibutton/scenes/ibutton_scene_add_type.c

@@ -0,0 +1,58 @@
+#include "../ibutton_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexCyfral,
+    SubmenuIndexDallas,
+    SubmenuIndexMetakom,
+};
+
+void ibutton_scene_add_type_submenu_callback(void* context, uint32_t index) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_scene_add_type_on_enter(void* context) {
+    iButton* ibutton = context;
+    Submenu* submenu = ibutton->submenu;
+
+    submenu_add_item(
+        submenu, "Cyfral", SubmenuIndexCyfral, ibutton_scene_add_type_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu, "Dallas", SubmenuIndexDallas, ibutton_scene_add_type_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu, "Metakom", SubmenuIndexMetakom, ibutton_scene_add_type_submenu_callback, ibutton);
+
+    submenu_set_selected_item(submenu, SubmenuIndexCyfral);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+}
+
+bool ibutton_scene_add_type_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    iButtonKey* key = ibutton->key;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == SubmenuIndexCyfral) {
+            ibutton_key_set_type(key, iButtonKeyCyfral);
+        } else if(event.event == SubmenuIndexDallas) {
+            ibutton_key_set_type(key, iButtonKeyDS1990);
+        } else if(event.event == SubmenuIndexMetakom) {
+            ibutton_key_set_type(key, iButtonKeyMetakom);
+        } else {
+            furi_crash("Unknown key type");
+        }
+
+        ibutton_key_set_name(key, "");
+        ibutton_key_clear_data(key);
+        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_add_type_on_exit(void* context) {
+    iButton* ibutton = context;
+    submenu_reset(ibutton->submenu);
+}

+ 57 - 0
applications/ibutton/scenes/ibutton_scene_add_value.c

@@ -0,0 +1,57 @@
+#include "../ibutton_i.h"
+
+#include <dolphin/dolphin.h>
+
+void ibutton_scene_add_type_byte_input_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventByteEditResult);
+}
+
+void ibutton_scene_add_value_on_enter(void* context) {
+    iButton* ibutton = context;
+    iButtonKey* key = ibutton->key;
+    uint8_t* new_key_data = malloc(IBUTTON_KEY_DATA_SIZE);
+
+    scene_manager_set_scene_state(
+        ibutton->scene_manager, iButtonSceneAddValue, (uint32_t)new_key_data);
+    memcpy(new_key_data, ibutton_key_get_data_p(key), ibutton_key_get_data_size(key));
+
+    byte_input_set_result_callback(
+        ibutton->byte_input,
+        ibutton_scene_add_type_byte_input_callback,
+        NULL,
+        ibutton,
+        new_key_data,
+        ibutton_key_get_data_size(key));
+
+    byte_input_set_header_text(ibutton->byte_input, "Enter the key");
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewByteInput);
+}
+
+bool ibutton_scene_add_value_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    uint8_t* new_key_data =
+        (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventByteEditResult) {
+            ibutton_key_set_data(ibutton->key, new_key_data, IBUTTON_KEY_DATA_SIZE);
+            DOLPHIN_DEED(DolphinDeedIbuttonAdd);
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_add_value_on_exit(void* context) {
+    iButton* ibutton = context;
+    uint8_t* new_key_data =
+        (uint8_t*)scene_manager_get_scene_state(ibutton->scene_manager, iButtonSceneAddValue);
+
+    byte_input_set_result_callback(ibutton->byte_input, NULL, NULL, NULL, NULL, 0);
+    byte_input_set_header_text(ibutton->byte_input, NULL);
+    free(new_key_data);
+}

+ 20 - 0
applications/ibutton/scenes/ibutton_scene_config.h

@@ -0,0 +1,20 @@
+ADD_SCENE(ibutton, start, Start)
+ADD_SCENE(ibutton, emulate, Emulate)
+ADD_SCENE(ibutton, write, Write)
+ADD_SCENE(ibutton, write_success, WriteSuccess)
+ADD_SCENE(ibutton, info, Info)
+ADD_SCENE(ibutton, read, Read)
+ADD_SCENE(ibutton, read_key_menu, ReadKeyMenu)
+ADD_SCENE(ibutton, read_success, ReadSuccess)
+ADD_SCENE(ibutton, read_crc_error, ReadCRCError)
+ADD_SCENE(ibutton, read_not_key_error, ReadNotKeyError)
+ADD_SCENE(ibutton, select_key, SelectKey)
+ADD_SCENE(ibutton, add_type, AddType)
+ADD_SCENE(ibutton, add_value, AddValue)
+ADD_SCENE(ibutton, saved_key_menu, SavedKeyMenu)
+ADD_SCENE(ibutton, save_name, SaveName)
+ADD_SCENE(ibutton, save_success, SaveSuccess)
+ADD_SCENE(ibutton, delete_confirm, DeleteConfirm)
+ADD_SCENE(ibutton, delete_success, DeleteSuccess)
+ADD_SCENE(ibutton, retry_confirm, RetryConfirm)
+ADD_SCENE(ibutton, exit_confirm, ExitConfirm)

+ 91 - 0
applications/ibutton/scenes/ibutton_scene_delete_confirm.c

@@ -0,0 +1,91 @@
+#include "../ibutton_i.h"
+
+static void ibutton_scene_delete_confirm_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    iButton* ibutton = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+    }
+}
+
+void ibutton_scene_delete_confirm_on_enter(void* context) {
+    iButton* ibutton = context;
+    Widget* widget = ibutton->widget;
+    iButtonKey* key = ibutton->key;
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+
+    ibutton_text_store_set(ibutton, "\e#Delete %s?\e#", ibutton_key_get_name_p(key));
+    widget_add_text_box_element(
+        widget, 0, 0, 128, 27, AlignCenter, AlignCenter, ibutton->text_store, false);
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Cancel", ibutton_scene_delete_confirm_widget_callback, ibutton);
+    widget_add_button_element(
+        widget,
+        GuiButtonTypeRight,
+        "Delete",
+        ibutton_scene_delete_confirm_widget_callback,
+        ibutton);
+
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
+        ibutton_text_store_set(
+            ibutton,
+            "%02X %02X %02X %02X %02X %02X %02X %02X",
+            key_data[0],
+            key_data[1],
+            key_data[2],
+            key_data[3],
+            key_data[4],
+            key_data[5],
+            key_data[6],
+            key_data[7]);
+        widget_add_string_element(
+            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Dallas");
+        break;
+
+    case iButtonKeyCyfral:
+        ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
+        widget_add_string_element(
+            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
+        break;
+
+    case iButtonKeyMetakom:
+        ibutton_text_store_set(
+            ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
+        widget_add_string_element(
+            widget, 64, 45, AlignCenter, AlignBottom, FontSecondary, "Metakom");
+        break;
+    }
+    widget_add_string_element(
+        widget, 64, 33, AlignCenter, AlignBottom, FontSecondary, ibutton->text_store);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+}
+
+bool ibutton_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == GuiButtonTypeRight) {
+            if(ibutton_delete_key(ibutton)) {
+                scene_manager_next_scene(scene_manager, iButtonSceneDeleteSuccess);
+            }
+            //TODO: What if the key could not be deleted?
+        } else if(event.event == GuiButtonTypeLeft) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_delete_confirm_on_exit(void* context) {
+    iButton* ibutton = context;
+    ibutton_text_store_clear(ibutton);
+    widget_reset(ibutton->widget);
+}

+ 48 - 0
applications/ibutton/scenes/ibutton_scene_delete_success.c

@@ -0,0 +1,48 @@
+#include "../ibutton_i.h"
+
+static void ibutton_scene_delete_success_popup_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);
+}
+
+void ibutton_scene_delete_success_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+
+    popup_set_icon(popup, 0, 2, &I_DolphinMafia_115x62);
+    popup_set_header(popup, "Deleted", 83, 19, AlignLeft, AlignBottom);
+
+    popup_set_callback(popup, ibutton_scene_delete_success_popup_callback);
+    popup_set_context(popup, ibutton);
+    popup_set_timeout(popup, 1500);
+    popup_enable_timeout(popup);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
+}
+
+bool ibutton_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventBack) {
+            scene_manager_search_and_switch_to_previous_scene(
+                ibutton->scene_manager, iButtonSceneSelectKey);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_delete_success_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+
+    popup_disable_timeout(popup);
+    popup_set_context(popup, NULL);
+    popup_set_callback(popup, NULL);
+}

+ 35 - 31
applications/ibutton/scene/ibutton_scene_emulate.cpp → applications/ibutton/scenes/ibutton_scene_emulate.c

@@ -1,37 +1,35 @@
-#include "ibutton_scene_emulate.h"
-#include "../ibutton_app.h"
+#include "../ibutton_i.h"
 #include <dolphin/dolphin.h>
 
-static void emulate_callback(void* context, bool emulated) {
-    furi_assert(context);
+static void ibutton_scene_emulate_callback(void* context, bool emulated) {
+    iButton* ibutton = context;
     if(emulated) {
-        iButtonApp* app = static_cast<iButtonApp*>(context);
-        iButtonEvent event = {
-            .payload = {.worker_write_result = iButtonWorkerWriteOK},
-            .type = iButtonEvent::Type::EventTypeWorkerEmulated,
-        };
-        app->get_view_manager()->send_event(&event);
+        view_dispatcher_send_custom_event(
+            ibutton->view_dispatcher, iButtonCustomEventWorkerEmulated);
     }
 }
 
-void iButtonSceneEmulate::on_enter(iButtonApp* app) {
-    iButtonAppViewManager* view_manager = app->get_view_manager();
-    Popup* popup = view_manager->get_popup();
-    iButtonKey* key = app->get_key();
+void ibutton_scene_emulate_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    iButtonKey* key = ibutton->key;
+
     const uint8_t* key_data = ibutton_key_get_data_p(key);
     const char* key_name = ibutton_key_get_name_p(key);
+
     uint8_t line_count = 2;
     DOLPHIN_DEED(DolphinDeedIbuttonEmulate);
 
     // check that stored key has name
     if(strcmp(key_name, "") != 0) {
-        app->set_text_store("emulating\n%s", key_name);
+        ibutton_text_store_set(ibutton, "emulating\n%s", key_name);
         line_count = 2;
     } else {
         // if not, show key data
         switch(ibutton_key_get_type(key)) {
         case iButtonKeyDS1990:
-            app->set_text_store(
+            ibutton_text_store_set(
+                ibutton,
                 "emulating\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
                 key_data[0],
                 key_data[1],
@@ -44,11 +42,12 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) {
             line_count = 3;
             break;
         case iButtonKeyCyfral:
-            app->set_text_store("emulating\n%02X %02X", key_data[0], key_data[1]);
+            ibutton_text_store_set(ibutton, "emulating\n%02X %02X", key_data[0], key_data[1]);
             line_count = 2;
             break;
         case iButtonKeyMetakom:
-            app->set_text_store(
+            ibutton_text_store_set(
+                ibutton,
                 "emulating\n%02X %02X %02X %02X",
                 key_data[0],
                 key_data[1],
@@ -62,40 +61,45 @@ void iButtonSceneEmulate::on_enter(iButtonApp* app) {
     switch(line_count) {
     case 3:
         popup_set_header(popup, "iButton", 82, 18, AlignCenter, AlignBottom);
-        popup_set_text(popup, app->get_text_store(), 82, 22, AlignCenter, AlignTop);
+        popup_set_text(popup, ibutton->text_store, 82, 22, AlignCenter, AlignTop);
         break;
 
     default:
         popup_set_header(popup, "iButton", 82, 24, AlignCenter, AlignBottom);
-        popup_set_text(popup, app->get_text_store(), 82, 28, AlignCenter, AlignTop);
+        popup_set_text(popup, ibutton->text_store, 82, 28, AlignCenter, AlignTop);
         break;
     }
 
     popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44);
 
-    view_manager->switch_to(iButtonAppViewManager::Type::iButtonAppViewPopup);
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
 
-    ibutton_worker_emulate_set_callback(app->get_key_worker(), emulate_callback, app);
-    ibutton_worker_emulate_start(app->get_key_worker(), key);
+    ibutton_worker_emulate_set_callback(
+        ibutton->key_worker, ibutton_scene_emulate_callback, ibutton);
+    ibutton_worker_emulate_start(ibutton->key_worker, key);
 }
 
-bool iButtonSceneEmulate::on_event(iButtonApp* app, iButtonEvent* event) {
+bool ibutton_scene_emulate_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
     bool consumed = false;
 
-    if(event->type == iButtonEvent::Type::EventTypeWorkerEmulated) {
-        app->notify_yellow_blink();
+    if(event.type == SceneManagerEventTypeTick) {
         consumed = true;
-    } else if(event->type == iButtonEvent::Type::EventTypeTick) {
-        app->notify_emulate();
+        ibutton_notification_message(ibutton, iButtonNotificationMessageEmulate);
+    } else if(event.type == SceneManagerEventTypeCustom) {
         consumed = true;
+        if(event.event == iButtonCustomEventWorkerEmulated) {
+            ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);
+        }
     }
 
     return consumed;
 }
 
-void iButtonSceneEmulate::on_exit(iButtonApp* app) {
-    Popup* popup = app->get_view_manager()->get_popup();
-    ibutton_worker_stop(app->get_key_worker());
+void ibutton_scene_emulate_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    ibutton_worker_stop(ibutton->key_worker);
     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
     popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
     popup_set_icon(popup, 0, 0, NULL);

+ 51 - 0
applications/ibutton/scenes/ibutton_scene_exit_confirm.c

@@ -0,0 +1,51 @@
+#include "../ibutton_i.h"
+
+static void ibutton_scene_exit_confirm_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    iButton* ibutton = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+    }
+}
+
+void ibutton_scene_exit_confirm_on_enter(void* context) {
+    iButton* ibutton = context;
+    Widget* widget = ibutton->widget;
+
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Exit", ibutton_scene_exit_confirm_widget_callback, ibutton);
+    widget_add_button_element(
+        widget, GuiButtonTypeRight, "Stay", ibutton_scene_exit_confirm_widget_callback, ibutton);
+    widget_add_string_element(
+        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Exit to iButton menu");
+    widget_add_string_element(
+        widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+}
+
+bool ibutton_scene_exit_confirm_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = true; // Ignore Back button presses
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == GuiButtonTypeLeft) {
+            scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneStart);
+        } else if(event.event == GuiButtonTypeRight) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_exit_confirm_on_exit(void* context) {
+    iButton* ibutton = context;
+    widget_reset(ibutton->widget);
+}

+ 61 - 0
applications/ibutton/scenes/ibutton_scene_info.c

@@ -0,0 +1,61 @@
+#include "../ibutton_i.h"
+
+void ibutton_scene_info_on_enter(void* context) {
+    iButton* ibutton = context;
+    Widget* widget = ibutton->widget;
+    iButtonKey* key = ibutton->key;
+
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+
+    ibutton_text_store_set(ibutton, "%s", ibutton_key_get_name_p(key));
+    widget_add_text_box_element(
+        widget, 0, 0, 128, 28, AlignCenter, AlignCenter, ibutton->text_store, false);
+
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
+        ibutton_text_store_set(
+            ibutton,
+            "%02X %02X %02X %02X %02X %02X %02X %02X",
+            key_data[0],
+            key_data[1],
+            key_data[2],
+            key_data[3],
+            key_data[4],
+            key_data[5],
+            key_data[6],
+            key_data[7]);
+        widget_add_string_element(
+            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Dallas");
+        break;
+
+    case iButtonKeyMetakom:
+        ibutton_text_store_set(
+            ibutton, "%02X %02X %02X %02X", key_data[0], key_data[1], key_data[2], key_data[3]);
+        widget_add_string_element(
+            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Metakom");
+        break;
+
+    case iButtonKeyCyfral:
+        ibutton_text_store_set(ibutton, "%02X %02X", key_data[0], key_data[1]);
+        widget_add_string_element(
+            widget, 64, 51, AlignCenter, AlignBottom, FontSecondary, "Cyfral");
+        break;
+    }
+
+    widget_add_string_element(
+        widget, 64, 35, AlignCenter, AlignBottom, FontPrimary, ibutton->text_store);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+}
+
+bool ibutton_scene_info_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void ibutton_scene_info_on_exit(void* context) {
+    iButton* ibutton = context;
+    ibutton_text_store_clear(ibutton);
+    widget_reset(ibutton->widget);
+}

+ 72 - 0
applications/ibutton/scenes/ibutton_scene_read.c

@@ -0,0 +1,72 @@
+#include "../ibutton_i.h"
+#include <dolphin/dolphin.h>
+
+static void ibutton_scene_read_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventWorkerRead);
+}
+
+void ibutton_scene_read_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    iButtonKey* key = ibutton->key;
+    iButtonWorker* worker = ibutton->key_worker;
+    DOLPHIN_DEED(DolphinDeedIbuttonRead);
+
+    popup_set_header(popup, "iButton", 95, 26, AlignCenter, AlignBottom);
+    popup_set_text(popup, "waiting\nfor key ...", 95, 30, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 5, &I_DolphinWait_61x59);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
+    ibutton_key_set_name(key, "");
+
+    ibutton_worker_read_set_callback(worker, ibutton_scene_read_callback, ibutton);
+    ibutton_worker_read_start(worker, key);
+}
+
+bool ibutton_scene_read_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeTick) {
+        consumed = true;
+        ibutton_notification_message(ibutton, iButtonNotificationMessageRead);
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventWorkerRead) {
+            bool success = false;
+            iButtonKey* key = ibutton->key;
+
+            if(ibutton_key_get_type(key) == iButtonKeyDS1990) {
+                if(!ibutton_key_dallas_crc_is_valid(key)) {
+                    scene_manager_next_scene(scene_manager, iButtonSceneReadCRCError);
+                } else if(!ibutton_key_dallas_is_1990_key(key)) {
+                    scene_manager_next_scene(scene_manager, iButtonSceneReadNotKeyError);
+                } else {
+                    success = true;
+                }
+            } else {
+                success = true;
+            }
+
+            if(success) {
+                ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
+                ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
+                DOLPHIN_DEED(DolphinDeedIbuttonReadSuccess);
+                scene_manager_next_scene(scene_manager, iButtonSceneReadSuccess);
+            }
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_read_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    ibutton_worker_stop(ibutton->key_worker);
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+}

+ 71 - 0
applications/ibutton/scenes/ibutton_scene_read_crc_error.c

@@ -0,0 +1,71 @@
+#include "../ibutton_i.h"
+#include <one_wire/maxim_crc.h>
+
+static void ibutton_scene_read_crc_error_dialog_ex_callback(DialogExResult result, void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+}
+
+void ibutton_scene_read_crc_error_on_enter(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+    iButtonKey* key = ibutton->key;
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+
+    ibutton_text_store_set(
+        ibutton,
+        "%02X %02X %02X %02X %02X %02X %02X %02X\nExpected CRC: %X",
+        key_data[0],
+        key_data[1],
+        key_data[2],
+        key_data[3],
+        key_data[4],
+        key_data[5],
+        key_data[6],
+        key_data[7],
+        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
+
+    dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog_ex, "Retry");
+    dialog_ex_set_right_button_text(dialog_ex, "More");
+    dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_crc_error_dialog_ex_callback);
+    dialog_ex_set_context(dialog_ex, ibutton);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+
+    ibutton_notification_message(ibutton, iButtonNotificationMessageError);
+    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
+}
+
+bool ibutton_scene_read_crc_error_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == DialogExResultRight) {
+            scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
+        } else if(event.event == DialogExResultLeft) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_read_crc_error_on_exit(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+
+    ibutton_text_store_clear(ibutton);
+
+    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog_ex, NULL);
+    dialog_ex_set_result_callback(dialog_ex, NULL);
+    dialog_ex_set_context(dialog_ex, NULL);
+
+    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
+}

+ 61 - 0
applications/ibutton/scenes/ibutton_scene_read_key_menu.c

@@ -0,0 +1,61 @@
+#include "../ibutton_i.h"
+
+typedef enum {
+    SubmenuIndexSave,
+    SubmenuIndexEmulate,
+    SubmenuIndexWrite,
+} SubmenuIndex;
+
+void ibutton_scene_read_key_menu_submenu_callback(void* context, uint32_t index) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_scene_read_key_menu_on_enter(void* context) {
+    iButton* ibutton = context;
+    Submenu* submenu = ibutton->submenu;
+
+    submenu_add_item(
+        submenu, "Save", SubmenuIndexSave, ibutton_scene_read_key_menu_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu,
+        "Emulate",
+        SubmenuIndexEmulate,
+        ibutton_scene_read_key_menu_submenu_callback,
+        ibutton);
+    if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+        submenu_add_item(
+            submenu,
+            "Write",
+            SubmenuIndexWrite,
+            ibutton_scene_read_key_menu_submenu_callback,
+            ibutton);
+    }
+
+    submenu_set_selected_item(submenu, SubmenuIndexSave);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+}
+
+bool ibutton_scene_read_key_menu_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == SubmenuIndexSave) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveName);
+        } else if(event.event == SubmenuIndexEmulate) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+        } else if(event.event == SubmenuIndexWrite) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_read_key_menu_on_exit(void* context) {
+    iButton* ibutton = context;
+    submenu_reset(ibutton->submenu);
+}

+ 72 - 0
applications/ibutton/scenes/ibutton_scene_read_not_key_error.c

@@ -0,0 +1,72 @@
+#include "../ibutton_i.h"
+#include <one_wire/maxim_crc.h>
+
+static void
+    ibutton_scene_read_not_key_error_dialog_ex_callback(DialogExResult result, void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+}
+
+void ibutton_scene_read_not_key_error_on_enter(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+    iButtonKey* key = ibutton->key;
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+
+    ibutton_text_store_set(
+        ibutton,
+        "THIS IS NOT A KEY\n%02X %02X %02X %02X %02X %02X %02X %02X",
+        key_data[0],
+        key_data[1],
+        key_data[2],
+        key_data[3],
+        key_data[4],
+        key_data[5],
+        key_data[6],
+        key_data[7],
+        maxim_crc8(key_data, 7, MAXIM_CRC8_INIT));
+
+    dialog_ex_set_header(dialog_ex, "CRC ERROR", 64, 10, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, ibutton->text_store, 64, 19, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog_ex, "Retry");
+    dialog_ex_set_right_button_text(dialog_ex, "More");
+    dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_not_key_error_dialog_ex_callback);
+    dialog_ex_set_context(dialog_ex, ibutton);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+
+    ibutton_notification_message(ibutton, iButtonNotificationMessageError);
+    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOn);
+}
+
+bool ibutton_scene_read_not_key_error_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == DialogExResultRight) {
+            scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
+        } else if(event.event == DialogExResultLeft) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_read_not_key_error_on_exit(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+
+    ibutton_text_store_clear(ibutton);
+
+    dialog_ex_set_header(dialog_ex, NULL, 0, 0, AlignCenter, AlignCenter);
+    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog_ex, NULL);
+    dialog_ex_set_result_callback(dialog_ex, NULL);
+    dialog_ex_set_context(dialog_ex, NULL);
+
+    ibutton_notification_message(ibutton, iButtonNotificationMessageRedOff);
+}

+ 87 - 0
applications/ibutton/scenes/ibutton_scene_read_success.c

@@ -0,0 +1,87 @@
+#include "../ibutton_i.h"
+#include <dolphin/dolphin.h>
+
+static void ibutton_scene_read_success_dialog_ex_callback(DialogExResult result, void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+}
+
+void ibutton_scene_read_success_on_enter(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+    iButtonKey* key = ibutton->key;
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+
+    switch(ibutton_key_get_type(key)) {
+    case iButtonKeyDS1990:
+        ibutton_text_store_set(
+            ibutton,
+            "Dallas\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
+            key_data[0],
+            key_data[1],
+            key_data[2],
+            key_data[3],
+            key_data[4],
+            key_data[5],
+            key_data[6],
+            key_data[7]);
+        break;
+    case iButtonKeyCyfral:
+        ibutton_text_store_set(ibutton, "Cyfral\n%02X %02X", key_data[0], key_data[1]);
+        break;
+    case iButtonKeyMetakom:
+        ibutton_text_store_set(
+            ibutton,
+            "Metakom\n%02X %02X %02X %02X",
+            key_data[0],
+            key_data[1],
+            key_data[2],
+            key_data[3]);
+        break;
+    }
+
+    dialog_ex_set_text(dialog_ex, ibutton->text_store, 95, 30, AlignCenter, AlignCenter);
+    dialog_ex_set_left_button_text(dialog_ex, "Retry");
+    dialog_ex_set_right_button_text(dialog_ex, "More");
+    dialog_ex_set_icon(dialog_ex, 0, 1, &I_DolphinReadingSuccess_59x63);
+    dialog_ex_set_result_callback(dialog_ex, ibutton_scene_read_success_dialog_ex_callback);
+    dialog_ex_set_context(dialog_ex, ibutton);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewDialogEx);
+}
+
+bool ibutton_scene_read_success_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = true;
+        scene_manager_next_scene(scene_manager, iButtonSceneExitConfirm);
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == DialogExResultRight) {
+            scene_manager_next_scene(scene_manager, iButtonSceneReadKeyMenu);
+        } else if(event.event == DialogExResultLeft) {
+            scene_manager_next_scene(scene_manager, iButtonSceneRetryConfirm);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_read_success_on_exit(void* context) {
+    iButton* ibutton = context;
+    DialogEx* dialog_ex = ibutton->dialog_ex;
+
+    ibutton_text_store_clear(ibutton);
+
+    dialog_ex_set_text(dialog_ex, NULL, 0, 0, AlignCenter, AlignTop);
+    dialog_ex_set_left_button_text(dialog_ex, NULL);
+    dialog_ex_set_right_button_text(dialog_ex, NULL);
+    dialog_ex_set_result_callback(dialog_ex, NULL);
+    dialog_ex_set_context(dialog_ex, NULL);
+    dialog_ex_set_icon(dialog_ex, 0, 0, NULL);
+
+    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);
+}

+ 51 - 0
applications/ibutton/scenes/ibutton_scene_retry_confirm.c

@@ -0,0 +1,51 @@
+#include "../ibutton_i.h"
+
+static void ibutton_scene_retry_confirm_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    iButton* ibutton = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+    }
+}
+
+void ibutton_scene_retry_confirm_on_enter(void* context) {
+    iButton* ibutton = context;
+    Widget* widget = ibutton->widget;
+
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Exit", ibutton_scene_retry_confirm_widget_callback, ibutton);
+    widget_add_button_element(
+        widget, GuiButtonTypeRight, "Stay", ibutton_scene_retry_confirm_widget_callback, ibutton);
+    widget_add_string_element(
+        widget, 64, 19, AlignCenter, AlignBottom, FontPrimary, "Return to reading?");
+    widget_add_string_element(
+        widget, 64, 29, AlignCenter, AlignBottom, FontSecondary, "All unsaved data will be lost");
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewWidget);
+}
+
+bool ibutton_scene_retry_confirm_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeBack) {
+        consumed = true; // Ignore Back button presses
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == GuiButtonTypeLeft) {
+            scene_manager_search_and_switch_to_previous_scene(scene_manager, iButtonSceneRead);
+        } else if(event.event == GuiButtonTypeRight) {
+            scene_manager_previous_scene(scene_manager);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_retry_confirm_on_exit(void* context) {
+    iButton* ibutton = context;
+    widget_reset(ibutton->widget);
+}

+ 68 - 0
applications/ibutton/scenes/ibutton_scene_save_name.c

@@ -0,0 +1,68 @@
+#include "../ibutton_i.h"
+#include <lib/toolbox/random_name.h>
+
+static void ibutton_scene_save_name_text_input_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventTextEditResult);
+}
+
+void ibutton_scene_save_name_on_enter(void* context) {
+    iButton* ibutton = context;
+    TextInput* text_input = ibutton->text_input;
+
+    const char* key_name = ibutton_key_get_name_p(ibutton->key);
+    const bool key_name_is_empty = !strcmp(key_name, "");
+
+    if(key_name_is_empty) {
+        set_random_name(ibutton->text_store, IBUTTON_TEXT_STORE_SIZE);
+    } else {
+        ibutton_text_store_set(ibutton, "%s", key_name);
+    }
+
+    text_input_set_header_text(text_input, "Name the key");
+    text_input_set_result_callback(
+        text_input,
+        ibutton_scene_save_name_text_input_callback,
+        ibutton,
+        ibutton->text_store,
+        IBUTTON_KEY_NAME_SIZE,
+        key_name_is_empty);
+
+    ValidatorIsFile* validator_is_file =
+        validator_is_file_alloc_init(IBUTTON_APP_FOLDER, IBUTTON_APP_EXTENSION, key_name);
+    text_input_set_validator(text_input, validator_is_file_callback, validator_is_file);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewTextInput);
+}
+
+bool ibutton_scene_save_name_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventTextEditResult) {
+            if(ibutton_save_key(ibutton, ibutton->text_store)) {
+                scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSaveSuccess);
+            } else {
+                const uint32_t possible_scenes[] = {
+                    iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
+                ibutton_switch_to_previous_scene_one_of(
+                    ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t));
+            }
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_save_name_on_exit(void* context) {
+    iButton* ibutton = context;
+    TextInput* text_input = ibutton->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);
+}

+ 52 - 0
applications/ibutton/scenes/ibutton_scene_save_success.c

@@ -0,0 +1,52 @@
+#include "../ibutton_i.h"
+#include <dolphin/dolphin.h>
+
+static void ibutton_scene_save_success_popup_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);
+}
+
+void ibutton_scene_save_success_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    DOLPHIN_DEED(DolphinDeedIbuttonSave);
+
+    popup_set_icon(popup, 32, 5, &I_DolphinNice_96x59);
+    popup_set_header(popup, "Saved!", 5, 7, AlignLeft, AlignTop);
+
+    popup_set_callback(popup, ibutton_scene_save_success_popup_callback);
+    popup_set_context(popup, ibutton);
+    popup_set_timeout(popup, 1500);
+    popup_enable_timeout(popup);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
+}
+
+bool ibutton_scene_save_success_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventBack) {
+            const uint32_t possible_scenes[] = {
+                iButtonSceneReadKeyMenu, iButtonSceneSavedKeyMenu, iButtonSceneAddType};
+            ibutton_switch_to_previous_scene_one_of(
+                ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t));
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_save_success_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+
+    popup_disable_timeout(popup);
+    popup_set_context(popup, NULL);
+    popup_set_callback(popup, NULL);
+}

+ 75 - 0
applications/ibutton/scenes/ibutton_scene_saved_key_menu.c

@@ -0,0 +1,75 @@
+#include "../ibutton_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexEmulate,
+    SubmenuIndexWrite,
+    SubmenuIndexEdit,
+    SubmenuIndexDelete,
+    SubmenuIndexInfo,
+};
+
+void ibutton_scene_saved_key_menu_submenu_callback(void* context, uint32_t index) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_scene_saved_key_menu_on_enter(void* context) {
+    iButton* ibutton = context;
+    Submenu* submenu = ibutton->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Emulate",
+        SubmenuIndexEmulate,
+        ibutton_scene_saved_key_menu_submenu_callback,
+        ibutton);
+    if(ibutton_key_get_type(ibutton->key) == iButtonKeyDS1990) {
+        submenu_add_item(
+            submenu,
+            "Write",
+            SubmenuIndexWrite,
+            ibutton_scene_saved_key_menu_submenu_callback,
+            ibutton);
+    }
+    submenu_add_item(
+        submenu, "Edit", SubmenuIndexEdit, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu,
+        "Delete",
+        SubmenuIndexDelete,
+        ibutton_scene_saved_key_menu_submenu_callback,
+        ibutton);
+    submenu_add_item(
+        submenu, "Info", SubmenuIndexInfo, ibutton_scene_saved_key_menu_submenu_callback, ibutton);
+
+    submenu_set_selected_item(submenu, SubmenuIndexEmulate);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+}
+
+bool ibutton_scene_saved_key_menu_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == SubmenuIndexEmulate) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneEmulate);
+        } else if(event.event == SubmenuIndexWrite) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneWrite);
+        } else if(event.event == SubmenuIndexEdit) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddValue);
+        } else if(event.event == SubmenuIndexDelete) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneDeleteConfirm);
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneInfo);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_saved_key_menu_on_exit(void* context) {
+    iButton* ibutton = context;
+    submenu_reset(ibutton->submenu);
+}

+ 22 - 0
applications/ibutton/scenes/ibutton_scene_select_key.c

@@ -0,0 +1,22 @@
+#include "../ibutton_i.h"
+
+void ibutton_scene_select_key_on_enter(void* context) {
+    iButton* ibutton = context;
+
+    if(!ibutton_file_select(ibutton)) {
+        scene_manager_search_and_switch_to_previous_scene(
+            ibutton->scene_manager, iButtonSceneStart);
+    } else {
+        scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSavedKeyMenu);
+    }
+}
+
+bool ibutton_scene_select_key_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void ibutton_scene_select_key_on_exit(void* context) {
+    UNUSED(context);
+}

+ 51 - 0
applications/ibutton/scenes/ibutton_scene_start.c

@@ -0,0 +1,51 @@
+#include "../ibutton_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexRead,
+    SubmenuIndexSaved,
+    SubmenuIndexAdd,
+};
+
+void ibutton_scene_start_submenu_callback(void* context, uint32_t index) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, index);
+}
+
+void ibutton_scene_start_on_enter(void* context) {
+    iButton* ibutton = context;
+    Submenu* submenu = ibutton->submenu;
+
+    submenu_add_item(
+        submenu, "Read", SubmenuIndexRead, ibutton_scene_start_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu, "Saved", SubmenuIndexSaved, ibutton_scene_start_submenu_callback, ibutton);
+    submenu_add_item(
+        submenu, "Add Manually", SubmenuIndexAdd, ibutton_scene_start_submenu_callback, ibutton);
+
+    submenu_set_selected_item(submenu, SubmenuIndexRead);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewSubmenu);
+}
+
+bool ibutton_scene_start_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == SubmenuIndexRead) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneRead);
+        } else if(event.event == SubmenuIndexSaved) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneSelectKey);
+        } else if(event.event == SubmenuIndexAdd) {
+            scene_manager_next_scene(ibutton->scene_manager, iButtonSceneAddType);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_start_on_exit(void* context) {
+    iButton* ibutton = context;
+    submenu_reset(ibutton->submenu);
+}

+ 121 - 0
applications/ibutton/scenes/ibutton_scene_write.c

@@ -0,0 +1,121 @@
+#include "../ibutton_i.h"
+
+typedef enum {
+    iButtonSceneWriteStateDefault,
+    iButtonSceneWriteStateBlinkYellow,
+} iButtonSceneWriteState;
+
+static void ibutton_scene_write_callback(void* context, iButtonWorkerWriteResult result) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, result);
+}
+
+void ibutton_scene_write_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    iButtonKey* key = ibutton->key;
+    iButtonWorker* worker = ibutton->key_worker;
+
+    const uint8_t* key_data = ibutton_key_get_data_p(key);
+    const char* key_name = ibutton_key_get_name_p(key);
+
+    uint8_t line_count = 2;
+
+    // check that stored key has name
+    if(strcmp(key_name, "") != 0) {
+        ibutton_text_store_set(ibutton, "writing\n%s", key_name);
+        line_count = 2;
+    } else {
+        // if not, show key data
+        switch(ibutton_key_get_type(key)) {
+        case iButtonKeyDS1990:
+            ibutton_text_store_set(
+                ibutton,
+                "writing\n%02X %02X %02X %02X\n%02X %02X %02X %02X",
+                key_data[0],
+                key_data[1],
+                key_data[2],
+                key_data[3],
+                key_data[4],
+                key_data[5],
+                key_data[6],
+                key_data[7]);
+            line_count = 3;
+            break;
+        case iButtonKeyCyfral:
+            ibutton_text_store_set(ibutton, "writing\n%02X %02X", key_data[0], key_data[1]);
+            line_count = 2;
+            break;
+        case iButtonKeyMetakom:
+            ibutton_text_store_set(
+                ibutton,
+                "writing\n%02X %02X %02X %02X",
+                key_data[0],
+                key_data[1],
+                key_data[2],
+                key_data[3]);
+            line_count = 2;
+            break;
+        }
+    }
+
+    switch(line_count) {
+    case 3:
+        popup_set_header(popup, "iButton", 82, 18, AlignCenter, AlignBottom);
+        popup_set_text(popup, ibutton->text_store, 82, 22, AlignCenter, AlignTop);
+        break;
+
+    default:
+        popup_set_header(popup, "iButton", 82, 24, AlignCenter, AlignBottom);
+        popup_set_text(popup, ibutton->text_store, 82, 28, AlignCenter, AlignTop);
+        break;
+    }
+
+    popup_set_icon(popup, 2, 10, &I_iButtonKey_49x44);
+
+    scene_manager_set_scene_state(
+        ibutton->scene_manager, iButtonSceneWrite, iButtonSceneWriteStateDefault);
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
+
+    ibutton_worker_write_set_callback(worker, ibutton_scene_write_callback, ibutton);
+    ibutton_worker_write_start(worker, key);
+}
+
+bool ibutton_scene_write_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    SceneManager* scene_manager = ibutton->scene_manager;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if((event.event == iButtonWorkerWriteOK) || (event.event == iButtonWorkerWriteSameKey)) {
+            scene_manager_next_scene(scene_manager, iButtonSceneWriteSuccess);
+        } else if(event.event == iButtonWorkerWriteNoDetect) {
+            scene_manager_set_scene_state(
+                scene_manager, iButtonSceneWrite, iButtonSceneWriteStateDefault);
+        } else if(event.event == iButtonWorkerWriteCannotWrite) {
+            scene_manager_set_scene_state(
+                scene_manager, iButtonSceneWrite, iButtonSceneWriteStateBlinkYellow);
+        }
+
+    } else if(event.type == SceneManagerEventTypeTick) {
+        consumed = true;
+        if(scene_manager_get_scene_state(scene_manager, iButtonSceneWrite) ==
+           iButtonSceneWriteStateBlinkYellow) {
+            ibutton_notification_message(ibutton, iButtonNotificationMessageYellowBlink);
+        } else {
+            ibutton_notification_message(ibutton, iButtonNotificationMessageEmulate);
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_write_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+    ibutton_worker_stop(ibutton->key_worker);
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+}

+ 52 - 0
applications/ibutton/scenes/ibutton_scene_write_success.c

@@ -0,0 +1,52 @@
+#include "../ibutton_i.h"
+
+static void ibutton_scene_write_success_popup_callback(void* context) {
+    iButton* ibutton = context;
+    view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventBack);
+    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOff);
+}
+
+void ibutton_scene_write_success_on_enter(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+
+    popup_set_icon(popup, 0, 12, &I_iButtonDolphinVerySuccess_108x52);
+    popup_set_text(popup, "Successfully written!", 40, 12, AlignLeft, AlignBottom);
+
+    popup_set_callback(popup, ibutton_scene_write_success_popup_callback);
+    popup_set_context(popup, ibutton);
+    popup_set_timeout(popup, 1500);
+    popup_enable_timeout(popup);
+
+    view_dispatcher_switch_to_view(ibutton->view_dispatcher, iButtonViewPopup);
+    ibutton_notification_message(ibutton, iButtonNotificationMessageSuccess);
+    ibutton_notification_message(ibutton, iButtonNotificationMessageGreenOn);
+}
+
+bool ibutton_scene_write_success_on_event(void* context, SceneManagerEvent event) {
+    iButton* ibutton = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        consumed = true;
+        if(event.event == iButtonCustomEventBack) {
+            const uint32_t possible_scenes[] = {iButtonSceneReadKeyMenu, iButtonSceneStart};
+            ibutton_switch_to_previous_scene_one_of(
+                ibutton, possible_scenes, sizeof(possible_scenes) / sizeof(uint32_t));
+        }
+    }
+
+    return consumed;
+}
+
+void ibutton_scene_write_success_on_exit(void* context) {
+    iButton* ibutton = context;
+    Popup* popup = ibutton->popup;
+
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+
+    popup_disable_timeout(popup);
+    popup_set_context(popup, NULL);
+    popup_set_callback(popup, NULL);
+}