Explorar el Código

[FL-2714] New NFC info screens (#1605)

* nfc: add scroll element for info
* widget: format lines for scroll text element
* widget: fix new line generation
* widget: finish element text scroll
* nfc: rework ultralight and NTAG info scenes
* nfc: rework mf classic info screens
* nfc: rework nfca info scenes
* nfc: fix mf ultralight navigation
* widget: add documentation
* nfc: rework bank card infO
* nfc: rework device info scene
* nfc: fix incorrect atqa order
* mf ultralight: remove unused function
* widget: add mutex for model protection
* widget: fix memory leak
* nfc: rework delete scene
* nfc: fix selected item in saved menu scene
* widget: fix naming in text scroll element
* nfc: fix navigation from delete success
* nfc: add dictionary icon
* widget: fix memory leak
gornekich hace 3 años
padre
commit
560ea5f995
Se han modificado 39 ficheros con 915 adiciones y 744 borrados
  1. 13 0
      applications/gui/modules/widget.c
  2. 21 0
      applications/gui/modules/widget.h
  3. 8 0
      applications/gui/modules/widget_elements/widget_element_i.h
  4. 245 0
      applications/gui/modules/widget_elements/widget_element_text_scroll.c
  5. 0 9
      applications/nfc/nfc.c
  6. 1 3
      applications/nfc/nfc_i.h
  7. 5 1
      applications/nfc/scenes/nfc_scene_config.h
  8. 26 41
      applications/nfc/scenes/nfc_scene_delete.c
  9. 1 1
      applications/nfc/scenes/nfc_scene_delete_success.c
  10. 35 180
      applications/nfc/scenes/nfc_scene_device_info.c
  11. 54 0
      applications/nfc/scenes/nfc_scene_emv_menu.c
  12. 31 71
      applications/nfc/scenes/nfc_scene_emv_read_success.c
  13. 1 0
      applications/nfc/scenes/nfc_scene_file_select.c
  14. 0 72
      applications/nfc/scenes/nfc_scene_mf_classic_info.c
  15. 1 0
      applications/nfc/scenes/nfc_scene_mf_classic_keys.c
  16. 1 1
      applications/nfc/scenes/nfc_scene_mf_classic_menu.c
  17. 10 33
      applications/nfc/scenes/nfc_scene_mf_classic_read_success.c
  18. 17 0
      applications/nfc/scenes/nfc_scene_mf_desfire_menu.c
  19. 48 53
      applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c
  20. 32 0
      applications/nfc/scenes/nfc_scene_mf_ultralight_data.c
  21. 7 0
      applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c
  22. 14 60
      applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c
  23. 133 0
      applications/nfc/scenes/nfc_scene_nfc_data_info.c
  24. 62 0
      applications/nfc/scenes/nfc_scene_nfca_menu.c
  25. 72 0
      applications/nfc/scenes/nfc_scene_nfca_read_success.c
  26. 5 2
      applications/nfc/scenes/nfc_scene_read.c
  27. 10 33
      applications/nfc/scenes/nfc_scene_read_card_success.c
  28. 15 3
      applications/nfc/scenes/nfc_scene_saved_menu.c
  29. 0 80
      applications/nfc/views/bank_card.c
  30. 0 26
      applications/nfc/views/bank_card.h
  31. BIN
      assets/icons/NFC/Keychain.png
  32. 1 0
      lib/nfc/nfc_device.c
  33. 1 1
      lib/nfc/nfc_worker.c
  34. 14 0
      lib/nfc/parsers/nfc_supported_card.c
  35. 4 1
      lib/nfc/parsers/nfc_supported_card.h
  36. 25 17
      lib/nfc/parsers/troyka_parser.c
  37. 1 1
      lib/nfc/parsers/troyka_parser.h
  38. 1 43
      lib/nfc/protocols/mifare_ultralight.c
  39. 0 12
      lib/nfc/protocols/mifare_ultralight.h

+ 13 - 0
applications/gui/modules/widget.c

@@ -162,6 +162,19 @@ void widget_add_text_box_element(
     widget_add_element(widget, text_box_element);
     widget_add_element(widget, text_box_element);
 }
 }
 
 
+void widget_add_text_scroll_element(
+    Widget* widget,
+    uint8_t x,
+    uint8_t y,
+    uint8_t width,
+    uint8_t height,
+    const char* text) {
+    furi_assert(widget);
+    WidgetElement* text_scroll_element =
+        widget_element_text_scroll_create(x, y, width, height, text);
+    widget_add_element(widget, text_scroll_element);
+}
+
 void widget_add_button_element(
 void widget_add_button_element(
     Widget* widget,
     Widget* widget,
     GuiButtonType button_type,
     GuiButtonType button_type,

+ 21 - 0
applications/gui/modules/widget.h

@@ -105,6 +105,27 @@ void widget_add_text_box_element(
     const char* text,
     const char* text,
     bool strip_to_dots);
     bool strip_to_dots);
 
 
+/** Add Text Scroll Element
+ *
+ * @param      widget           Widget instance
+ * @param      x                x coordinate
+ * @param      y                y coordinate
+ * @param      width            width to fit text
+ * @param      height           height to fit text
+ * @param[in]  text             Formatted text. Default format: align left, Secondary font.
+ *                              The following formats are available:
+ *                               "\e#Bold text" - sets bold font before until next '\n' symbol
+ *                               "\ecBold text" - sets center horizontal align before until next '\n' symbol
+ *                               "\erBold text" - sets right horizontal align before until next '\n' symbol
+ */
+void widget_add_text_scroll_element(
+    Widget* widget,
+    uint8_t x,
+    uint8_t y,
+    uint8_t width,
+    uint8_t height,
+    const char* text);
+
 /** Add Button Element
 /** Add Button Element
  *
  *
  * @param      widget       Widget instance
  * @param      widget       Widget instance

+ 8 - 0
applications/gui/modules/widget_elements/widget_element_i.h

@@ -29,6 +29,7 @@ struct WidgetElement {
 
 
     // generic model holder
     // generic model holder
     void* model;
     void* model;
+    FuriMutex* model_mutex;
 
 
     // pointer to widget that hold our element
     // pointer to widget that hold our element
     Widget* parent;
     Widget* parent;
@@ -80,3 +81,10 @@ WidgetElement* widget_element_frame_create(
     uint8_t width,
     uint8_t width,
     uint8_t height,
     uint8_t height,
     uint8_t radius);
     uint8_t radius);
+
+WidgetElement* widget_element_text_scroll_create(
+    uint8_t x,
+    uint8_t y,
+    uint8_t width,
+    uint8_t height,
+    const char* text);

+ 245 - 0
applications/gui/modules/widget_elements/widget_element_text_scroll.c

@@ -0,0 +1,245 @@
+#include "widget_element_i.h"
+#include <m-string.h>
+#include <gui/elements.h>
+#include <m-array.h>
+
+#define WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET (4)
+
+typedef struct {
+    Font font;
+    Align horizontal;
+    string_t text;
+} TextScrollLineArray;
+
+ARRAY_DEF(TextScrollLineArray, TextScrollLineArray, M_POD_OPLIST)
+
+typedef struct {
+    TextScrollLineArray_t line_array;
+    uint8_t x;
+    uint8_t y;
+    uint8_t width;
+    uint8_t height;
+    string_t text;
+    uint8_t scroll_pos_total;
+    uint8_t scroll_pos_current;
+    bool text_formatted;
+} WidgetElementTextScrollModel;
+
+static bool
+    widget_element_text_scroll_process_ctrl_symbols(TextScrollLineArray* line, string_t text) {
+    bool processed = false;
+
+    do {
+        if(string_get_char(text, 0) != '\e') break;
+        char ctrl_symbol = string_get_char(text, 1);
+        if(ctrl_symbol == 'c') {
+            line->horizontal = AlignCenter;
+        } else if(ctrl_symbol == 'r') {
+            line->horizontal = AlignRight;
+        } else if(ctrl_symbol == '#') {
+            line->font = FontPrimary;
+        }
+        string_right(text, 2);
+        processed = true;
+    } while(false);
+
+    return processed;
+}
+
+void widget_element_text_scroll_add_line(WidgetElement* element, TextScrollLineArray* line) {
+    WidgetElementTextScrollModel* model = element->model;
+    TextScrollLineArray new_line;
+    new_line.font = line->font;
+    new_line.horizontal = line->horizontal;
+    string_init_set(new_line.text, line->text);
+    TextScrollLineArray_push_back(model->line_array, new_line);
+}
+
+static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* element) {
+    WidgetElementTextScrollModel* model = element->model;
+    TextScrollLineArray line_tmp;
+    bool all_text_processed = false;
+    string_init(line_tmp.text);
+    bool reached_new_line = true;
+    uint16_t total_height = 0;
+
+    while(!all_text_processed) {
+        if(reached_new_line) {
+            // Set default line properties
+            line_tmp.font = FontSecondary;
+            line_tmp.horizontal = AlignLeft;
+            string_reset(line_tmp.text);
+            // Process control symbols
+            while(widget_element_text_scroll_process_ctrl_symbols(&line_tmp, model->text))
+                ;
+        }
+        // Set canvas font
+        canvas_set_font(canvas, line_tmp.font);
+        CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font);
+        total_height += params->height;
+        if(total_height > model->height) {
+            model->scroll_pos_total++;
+        }
+
+        uint8_t line_width = 0;
+        uint16_t char_i = 0;
+        while(true) {
+            char next_char = string_get_char(model->text, char_i++);
+            if(next_char == '\0') {
+                string_push_back(line_tmp.text, '\0');
+                widget_element_text_scroll_add_line(element, &line_tmp);
+                total_height += params->leading_default - params->height;
+                all_text_processed = true;
+                break;
+            } else if(next_char == '\n') {
+                string_push_back(line_tmp.text, '\0');
+                widget_element_text_scroll_add_line(element, &line_tmp);
+                string_right(model->text, char_i);
+                total_height += params->leading_default - params->height;
+                reached_new_line = true;
+                break;
+            } else {
+                line_width += canvas_glyph_width(canvas, next_char);
+                if(line_width > model->width) {
+                    string_push_back(line_tmp.text, '\0');
+                    widget_element_text_scroll_add_line(element, &line_tmp);
+                    string_right(model->text, char_i - 1);
+                    string_reset(line_tmp.text);
+                    total_height += params->leading_default - params->height;
+                    reached_new_line = false;
+                    break;
+                } else {
+                    string_push_back(line_tmp.text, next_char);
+                }
+            }
+        }
+    }
+
+    string_clear(line_tmp.text);
+}
+
+static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* element) {
+    furi_assert(canvas);
+    furi_assert(element);
+
+    furi_mutex_acquire(element->model_mutex, FuriWaitForever);
+
+    WidgetElementTextScrollModel* model = element->model;
+    if(!model->text_formatted) {
+        widget_element_text_scroll_fill_lines(canvas, element);
+        model->text_formatted = true;
+    }
+
+    uint8_t y = model->y;
+    uint8_t x = model->x;
+    uint16_t curr_line = 0;
+    if(TextScrollLineArray_size(model->line_array)) {
+        TextScrollLineArray_it_t it;
+        for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
+            TextScrollLineArray_next(it), curr_line++) {
+            if(curr_line < model->scroll_pos_current) continue;
+            TextScrollLineArray* line = TextScrollLineArray_ref(it);
+            CanvasFontParameters* params = canvas_get_font_params(canvas, line->font);
+            if(y + params->descender > model->y + model->height) break;
+            canvas_set_font(canvas, line->font);
+            if(line->horizontal == AlignLeft) {
+                x = model->x;
+            } else if(line->horizontal == AlignCenter) {
+                x = (model->x + model->width) / 2;
+            } else if(line->horizontal == AlignRight) {
+                x = model->x + model->width;
+            }
+            canvas_draw_str_aligned(
+                canvas, x, y, line->horizontal, AlignTop, string_get_cstr(line->text));
+            y += params->leading_default;
+        }
+        // Draw scroll bar
+        if(model->scroll_pos_total > 1) {
+            elements_scrollbar_pos(
+                canvas,
+                model->x + model->width + WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET,
+                model->y,
+                model->height,
+                model->scroll_pos_current,
+                model->scroll_pos_total);
+        }
+    }
+
+    furi_mutex_release(element->model_mutex);
+}
+
+static bool widget_element_text_scroll_input(InputEvent* event, WidgetElement* element) {
+    furi_assert(event);
+    furi_assert(element);
+
+    furi_mutex_acquire(element->model_mutex, FuriWaitForever);
+
+    WidgetElementTextScrollModel* model = element->model;
+    bool consumed = false;
+
+    if((event->type == InputTypeShort) || (event->type == InputTypeRepeat)) {
+        if(event->key == InputKeyUp) {
+            if(model->scroll_pos_current > 0) {
+                model->scroll_pos_current--;
+            }
+            consumed = true;
+        } else if(event->key == InputKeyDown) {
+            if((model->scroll_pos_total > 1) &&
+               (model->scroll_pos_current < model->scroll_pos_total - 1)) {
+                model->scroll_pos_current++;
+            }
+            consumed = true;
+        }
+    }
+
+    furi_mutex_release(element->model_mutex);
+
+    return consumed;
+}
+
+static void widget_element_text_scroll_free(WidgetElement* text_scroll) {
+    furi_assert(text_scroll);
+
+    WidgetElementTextScrollModel* model = text_scroll->model;
+    TextScrollLineArray_it_t it;
+    for(TextScrollLineArray_it(it, model->line_array); !TextScrollLineArray_end_p(it);
+        TextScrollLineArray_next(it)) {
+        TextScrollLineArray* line = TextScrollLineArray_ref(it);
+        string_clear(line->text);
+    }
+    TextScrollLineArray_clear(model->line_array);
+    string_clear(model->text);
+    free(text_scroll->model);
+    furi_mutex_free(text_scroll->model_mutex);
+    free(text_scroll);
+}
+
+WidgetElement* widget_element_text_scroll_create(
+    uint8_t x,
+    uint8_t y,
+    uint8_t width,
+    uint8_t height,
+    const char* text) {
+    furi_assert(text);
+
+    // Allocate and init model
+    WidgetElementTextScrollModel* model = malloc(sizeof(WidgetElementTextScrollModel));
+    model->x = x;
+    model->y = y;
+    model->width = width - WIDGET_ELEMENT_TEXT_SCROLL_BAR_OFFSET;
+    model->height = height;
+    model->scroll_pos_current = 0;
+    model->scroll_pos_total = 1;
+    TextScrollLineArray_init(model->line_array);
+    string_init_set_str(model->text, text);
+
+    WidgetElement* text_scroll = malloc(sizeof(WidgetElement));
+    text_scroll->parent = NULL;
+    text_scroll->draw = widget_element_text_scroll_draw;
+    text_scroll->input = widget_element_text_scroll_input;
+    text_scroll->free = widget_element_text_scroll_free;
+    text_scroll->model = model;
+    text_scroll->model_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+
+    return text_scroll;
+}

+ 0 - 9
applications/nfc/nfc.c

@@ -89,11 +89,6 @@ Nfc* nfc_alloc() {
     nfc->widget = widget_alloc();
     nfc->widget = widget_alloc();
     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget));
     view_dispatcher_add_view(nfc->view_dispatcher, NfcViewWidget, widget_get_view(nfc->widget));
 
 
-    // Bank Card
-    nfc->bank_card = bank_card_alloc();
-    view_dispatcher_add_view(
-        nfc->view_dispatcher, NfcViewBankCard, bank_card_get_view(nfc->bank_card));
-
     // Mifare Classic Dict Attack
     // Mifare Classic Dict Attack
     nfc->dict_attack = dict_attack_alloc();
     nfc->dict_attack = dict_attack_alloc();
     view_dispatcher_add_view(
     view_dispatcher_add_view(
@@ -159,10 +154,6 @@ void nfc_free(Nfc* nfc) {
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewWidget);
     widget_free(nfc->widget);
     widget_free(nfc->widget);
 
 
-    // Bank Card
-    view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewBankCard);
-    bank_card_free(nfc->bank_card);
-
     // Mifare Classic Dict Attack
     // Mifare Classic Dict Attack
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
     view_dispatcher_remove_view(nfc->view_dispatcher, NfcViewDictAttack);
     dict_attack_free(nfc->dict_attack);
     dict_attack_free(nfc->dict_attack);

+ 1 - 3
applications/nfc/nfc_i.h

@@ -25,8 +25,8 @@
 #include <lib/nfc/nfc_worker.h>
 #include <lib/nfc/nfc_worker.h>
 #include <lib/nfc/nfc_device.h>
 #include <lib/nfc/nfc_device.h>
 #include <lib/nfc/helpers/mf_classic_dict.h>
 #include <lib/nfc/helpers/mf_classic_dict.h>
+#include <lib/nfc/parsers/nfc_supported_card.h>
 
 
-#include "views/bank_card.h"
 #include "views/dict_attack.h"
 #include "views/dict_attack.h"
 
 
 #include <nfc/scenes/nfc_scene.h>
 #include <nfc/scenes/nfc_scene.h>
@@ -70,7 +70,6 @@ struct Nfc {
     ByteInput* byte_input;
     ByteInput* byte_input;
     TextBox* text_box;
     TextBox* text_box;
     Widget* widget;
     Widget* widget;
-    BankCard* bank_card;
     DictAttack* dict_attack;
     DictAttack* dict_attack;
 
 
     const NfcGenerator* generator;
     const NfcGenerator* generator;
@@ -85,7 +84,6 @@ typedef enum {
     NfcViewByteInput,
     NfcViewByteInput,
     NfcViewTextBox,
     NfcViewTextBox,
     NfcViewWidget,
     NfcViewWidget,
-    NfcViewBankCard,
     NfcViewDictAttack,
     NfcViewDictAttack,
 } NfcView;
 } NfcView;
 
 

+ 5 - 1
applications/nfc/scenes/nfc_scene_config.h

@@ -12,7 +12,10 @@ ADD_SCENE(nfc, save_name, SaveName)
 ADD_SCENE(nfc, save_success, SaveSuccess)
 ADD_SCENE(nfc, save_success, SaveSuccess)
 ADD_SCENE(nfc, file_select, FileSelect)
 ADD_SCENE(nfc, file_select, FileSelect)
 ADD_SCENE(nfc, emulate_uid, EmulateUid)
 ADD_SCENE(nfc, emulate_uid, EmulateUid)
+ADD_SCENE(nfc, nfca_read_success, NfcaReadSuccess)
+ADD_SCENE(nfc, nfca_menu, NfcaMenu)
 ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
 ADD_SCENE(nfc, mf_ultralight_read_success, MfUltralightReadSuccess)
+ADD_SCENE(nfc, mf_ultralight_data, MfUltralightData)
 ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)
 ADD_SCENE(nfc, mf_ultralight_menu, MfUltralightMenu)
 ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate)
 ADD_SCENE(nfc, mf_ultralight_emulate, MfUltralightEmulate)
 ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
 ADD_SCENE(nfc, mf_ultralight_read_auth, MfUltralightReadAuth)
@@ -25,13 +28,13 @@ ADD_SCENE(nfc, mf_desfire_menu, MfDesfireMenu)
 ADD_SCENE(nfc, mf_desfire_data, MfDesfireData)
 ADD_SCENE(nfc, mf_desfire_data, MfDesfireData)
 ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
 ADD_SCENE(nfc, mf_desfire_app, MfDesfireApp)
 ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess)
 ADD_SCENE(nfc, mf_classic_read_success, MfClassicReadSuccess)
-ADD_SCENE(nfc, mf_classic_info, MfClassicInfo)
 ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
 ADD_SCENE(nfc, mf_classic_menu, MfClassicMenu)
 ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
 ADD_SCENE(nfc, mf_classic_emulate, MfClassicEmulate)
 ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
 ADD_SCENE(nfc, mf_classic_keys, MfClassicKeys)
 ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
 ADD_SCENE(nfc, mf_classic_keys_add, MfClassicKeysAdd)
 ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
 ADD_SCENE(nfc, mf_classic_dict_attack, MfClassicDictAttack)
 ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
 ADD_SCENE(nfc, emv_read_success, EmvReadSuccess)
+ADD_SCENE(nfc, emv_menu, EmvMenu)
 ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
 ADD_SCENE(nfc, emulate_apdu_sequence, EmulateApduSequence)
 ADD_SCENE(nfc, device_info, DeviceInfo)
 ADD_SCENE(nfc, device_info, DeviceInfo)
 ADD_SCENE(nfc, delete, Delete)
 ADD_SCENE(nfc, delete, Delete)
@@ -45,3 +48,4 @@ ADD_SCENE(nfc, rpc, Rpc)
 ADD_SCENE(nfc, exit_confirm, ExitConfirm)
 ADD_SCENE(nfc, exit_confirm, ExitConfirm)
 ADD_SCENE(nfc, retry_confirm, RetryConfirm)
 ADD_SCENE(nfc, retry_confirm, RetryConfirm)
 ADD_SCENE(nfc, detect_reader, DetectReader)
 ADD_SCENE(nfc, detect_reader, DetectReader)
+ADD_SCENE(nfc, nfc_data_info, NfcDataInfo)

+ 26 - 41
applications/nfc/scenes/nfc_scene_delete.c

@@ -9,58 +9,43 @@ void nfc_scene_delete_widget_callback(GuiButtonType result, InputType type, void
 
 
 void nfc_scene_delete_on_enter(void* context) {
 void nfc_scene_delete_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
+    FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
 
 
     // Setup Custom Widget view
     // Setup Custom Widget view
-    char temp_str[64];
-    snprintf(temp_str, sizeof(temp_str), "\e#Delete %s?\e#", nfc->dev->dev_name);
+    string_t temp_str;
+    string_init(temp_str);
+
+    string_printf(temp_str, "\e#Delete %s?\e#", nfc->dev->dev_name);
     widget_add_text_box_element(
     widget_add_text_box_element(
-        nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, temp_str, false);
+        nfc->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, string_get_cstr(temp_str), false);
     widget_add_button_element(
     widget_add_button_element(
-        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_delete_widget_callback, nfc);
+        nfc->widget, GuiButtonTypeLeft, "Cancel", nfc_scene_delete_widget_callback, nfc);
     widget_add_button_element(
     widget_add_button_element(
         nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
         nfc->widget, GuiButtonTypeRight, "Delete", nfc_scene_delete_widget_callback, nfc);
-    FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
-    if(data->uid_len == 4) {
-        snprintf(
-            temp_str,
-            sizeof(temp_str),
-            "UID: %02X %02X %02X %02X",
-            data->uid[0],
-            data->uid[1],
-            data->uid[2],
-            data->uid[3]);
-    } else if(data->uid_len == 7) {
-        snprintf(
-            temp_str,
-            sizeof(temp_str),
-            "UID: %02X %02X %02X %02X %02X %02X %02X",
-            data->uid[0],
-            data->uid[1],
-            data->uid[2],
-            data->uid[3],
-            data->uid[4],
-            data->uid[5],
-            data->uid[6]);
+
+    string_set_str(temp_str, "UID:");
+    for(size_t i = 0; i < nfc_data->uid_len; i++) {
+        string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
     }
     }
-    widget_add_string_element(nfc->widget, 64, 23, AlignCenter, AlignTop, FontSecondary, temp_str);
+    widget_add_string_element(
+        nfc->widget, 64, 24, AlignCenter, AlignTop, FontSecondary, string_get_cstr(temp_str));
 
 
-    const char* protocol_name = NULL;
     NfcProtocol protocol = nfc->dev->dev_data.protocol;
     NfcProtocol protocol = nfc->dev->dev_data.protocol;
     if(protocol == NfcDeviceProtocolEMV) {
     if(protocol == NfcDeviceProtocolEMV) {
-        protocol_name = nfc_guess_protocol(protocol);
+        string_set_str(temp_str, "EMV bank card");
     } else if(protocol == NfcDeviceProtocolMifareUl) {
     } else if(protocol == NfcDeviceProtocolMifareUl) {
-        protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false);
-    }
-    if(protocol_name) {
-        widget_add_string_element(
-            nfc->widget, 10, 33, AlignLeft, AlignTop, FontSecondary, protocol_name);
+        string_set_str(temp_str, nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, true));
+    } else if(protocol == NfcDeviceProtocolMifareClassic) {
+        string_set_str(temp_str, nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type));
+    } else if(protocol == NfcDeviceProtocolMifareDesfire) {
+        string_set_str(temp_str, "MIFARE DESFire");
+    } else {
+        string_set_str(temp_str, "Unknown ISO tag");
     }
     }
-    // TODO change dinamically
-    widget_add_string_element(nfc->widget, 118, 33, AlignRight, AlignTop, FontSecondary, "NFC-A");
-    snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak);
-    widget_add_string_element(nfc->widget, 10, 43, AlignLeft, AlignTop, FontSecondary, temp_str);
-    snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
-    widget_add_string_element(nfc->widget, 118, 43, AlignRight, AlignTop, FontSecondary, temp_str);
+    widget_add_string_element(
+        nfc->widget, 64, 34, AlignCenter, AlignTop, FontSecondary, string_get_cstr(temp_str));
+    widget_add_string_element(nfc->widget, 64, 44, AlignCenter, AlignTop, FontSecondary, "NFC-A");
+    string_clear(temp_str);
 
 
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
@@ -71,7 +56,7 @@ bool nfc_scene_delete_on_event(void* context, SceneManagerEvent event) {
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == GuiButtonTypeLeft) {
         if(event.event == GuiButtonTypeLeft) {
-            return scene_manager_previous_scene(nfc->scene_manager);
+            consumed = scene_manager_previous_scene(nfc->scene_manager);
         } else if(event.event == GuiButtonTypeRight) {
         } else if(event.event == GuiButtonTypeRight) {
             if(nfc_device_delete(nfc->dev, true)) {
             if(nfc_device_delete(nfc->dev, true)) {
                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);
                 scene_manager_next_scene(nfc->scene_manager, NfcSceneDeleteSuccess);

+ 1 - 1
applications/nfc/scenes/nfc_scene_delete_success.c

@@ -26,7 +26,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) {
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == NfcCustomEventViewExit) {
         if(event.event == NfcCustomEventViewExit) {
             consumed = scene_manager_search_and_switch_to_previous_scene(
             consumed = scene_manager_search_and_switch_to_previous_scene(
-                nfc->scene_manager, NfcSceneStart);
+                nfc->scene_manager, NfcSceneFileSelect);
         }
         }
     }
     }
     return consumed;
     return consumed;

+ 35 - 180
applications/nfc/scenes/nfc_scene_device_info.c

@@ -1,11 +1,6 @@
 #include "../nfc_i.h"
 #include "../nfc_i.h"
 #include "../helpers/nfc_emv_parser.h"
 #include "../helpers/nfc_emv_parser.h"
 
 
-enum {
-    NfcSceneDeviceInfoUid,
-    NfcSceneDeviceInfoData,
-};
-
 void nfc_scene_device_info_widget_callback(GuiButtonType result, InputType type, void* context) {
 void nfc_scene_device_info_widget_callback(GuiButtonType result, InputType type, void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     if(type == InputTypeShort) {
     if(type == InputTypeShort) {
@@ -13,197 +8,65 @@ void nfc_scene_device_info_widget_callback(GuiButtonType result, InputType type,
     }
     }
 }
 }
 
 
-void nfc_scene_device_info_dialog_callback(DialogExResult result, void* context) {
-    Nfc* nfc = context;
-    if(result == DialogExResultLeft) {
-        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
-    }
-}
-
-void nfc_scene_device_info_bank_card_callback(GuiButtonType result, InputType type, void* context) {
-    UNUSED(result);
-    Nfc* nfc = context;
-    if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit);
-    }
-}
-
 void nfc_scene_device_info_on_enter(void* context) {
 void nfc_scene_device_info_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
+    NfcDeviceData* dev_data = &nfc->dev->dev_data;
 
 
-    bool data_display_supported = (nfc->dev->format == NfcDeviceSaveFormatUid) ||
-                                  (nfc->dev->format == NfcDeviceSaveFormatMifareUl) ||
-                                  (nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) ||
-                                  (nfc->dev->format == NfcDeviceSaveFormatBankCard);
-    // Setup Custom Widget view
-    widget_add_text_box_element(
-        nfc->widget, 0, 0, 128, 22, AlignCenter, AlignTop, nfc->dev->dev_name, false);
-    widget_add_button_element(
-        nfc->widget, GuiButtonTypeLeft, "Back", nfc_scene_device_info_widget_callback, nfc);
-    if(data_display_supported) {
-        widget_add_button_element(
-            nfc->widget, GuiButtonTypeRight, "Data", nfc_scene_device_info_widget_callback, nfc);
-    }
-    char temp_str[32];
-    FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
-    if(data->uid_len == 4) {
-        snprintf(
-            temp_str,
-            sizeof(temp_str),
-            "UID: %02X %02X %02X %02X",
-            data->uid[0],
-            data->uid[1],
-            data->uid[2],
-            data->uid[3]);
-    } else if(data->uid_len == 7) {
-        snprintf(
-            temp_str,
-            sizeof(temp_str),
-            "UID: %02X %02X %02X %02X %02X %02X %02X",
-            data->uid[0],
-            data->uid[1],
-            data->uid[2],
-            data->uid[3],
-            data->uid[4],
-            data->uid[5],
-            data->uid[6]);
-    }
-    widget_add_string_element(nfc->widget, 64, 21, AlignCenter, AlignTop, FontSecondary, temp_str);
+    string_t temp_str;
+    string_init(temp_str);
 
 
-    const char* protocol_name = NULL;
-    NfcProtocol protocol = nfc->dev->dev_data.protocol;
-    if(protocol == NfcDeviceProtocolEMV || protocol == NfcDeviceProtocolMifareDesfire) {
-        protocol_name = nfc_guess_protocol(protocol);
-    } else if(protocol == NfcDeviceProtocolMifareUl) {
-        protocol_name = nfc_mf_ul_type(nfc->dev->dev_data.mf_ul_data.type, false);
-    } else if(protocol == NfcDeviceProtocolMifareClassic) {
-        protocol_name = nfc_mf_classic_type(nfc->dev->dev_data.mf_classic_data.type);
-    }
-    if(protocol_name) {
-        widget_add_string_element(
-            nfc->widget, 10, 32, AlignLeft, AlignTop, FontSecondary, protocol_name);
-    }
-    // TODO change dinamically
-    widget_add_string_element(nfc->widget, 118, 32, AlignRight, AlignTop, FontSecondary, "NFC-A");
-    snprintf(temp_str, sizeof(temp_str), "SAK: %02X", data->sak);
-    widget_add_string_element(nfc->widget, 10, 42, AlignLeft, AlignTop, FontSecondary, temp_str);
-    snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", data->atqa[0], data->atqa[1]);
-    widget_add_string_element(nfc->widget, 118, 42, AlignRight, AlignTop, FontSecondary, temp_str);
+    if(dev_data->protocol == NfcDeviceProtocolEMV) {
+        EmvData* emv_data = &dev_data->emv_data;
+        string_printf(temp_str, "\e#%s\n", emv_data->name);
+        for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
+            string_cat_printf(temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
+        }
+        string_strim(temp_str);
 
 
-    // Setup Data View
-    if(nfc->dev->format == NfcDeviceSaveFormatUid) {
-        DialogEx* dialog_ex = nfc->dialog_ex;
-        dialog_ex_set_left_button_text(dialog_ex, "Back");
-        dialog_ex_set_text(dialog_ex, "No data", 64, 32, AlignCenter, AlignCenter);
-        dialog_ex_set_context(dialog_ex, nfc);
-        dialog_ex_set_result_callback(dialog_ex, nfc_scene_device_info_dialog_callback);
-    } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
-        MfUltralightData* mf_ul_data = &nfc->dev->dev_data.mf_ul_data;
-        TextBox* text_box = nfc->text_box;
-        text_box_set_font(text_box, TextBoxFontHex);
-        for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
-            if(!(i % 8) && i) {
-                string_push_back(nfc->text_box_store, '\n');
-            }
-            string_cat_printf(
-                nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
+        // Add expiration date
+        if(emv_data->exp_mon) {
+            string_cat_printf(temp_str, "\nExp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year);
         }
         }
-        text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
-    } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) {
-        MifareDesfireData* mf_df_data = &nfc->dev->dev_data.mf_df_data;
-        uint16_t n_apps = 0;
-        uint16_t n_files = 0;
-        for(MifareDesfireApplication* app = mf_df_data->app_head; app; app = app->next) {
-            n_apps++;
-            for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
-                n_files++;
+        // Parse currency code
+        if((emv_data->currency_code)) {
+            string_t currency_name;
+            string_init(currency_name);
+            if(nfc_emv_parser_get_currency_name(
+                   nfc->dev->storage, emv_data->currency_code, currency_name)) {
+                string_cat_printf(temp_str, "\nCur: %s  ", string_get_cstr(currency_name));
             }
             }
+            string_clear(currency_name);
         }
         }
-        nfc_text_store_set(
-            nfc,
-            "%d application%s, %d file%s",
-            n_apps,
-            n_apps == 1 ? "" : "s",
-            n_files,
-            n_files == 1 ? "" : "s");
-        widget_add_string_element(
-            nfc->widget, 64, 17, AlignCenter, AlignBottom, FontSecondary, nfc->text_store);
-    } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
-        EmvData* emv_data = &nfc->dev->dev_data.emv_data;
-        BankCard* bank_card = nfc->bank_card;
-        bank_card_set_name(bank_card, emv_data->name);
-        bank_card_set_number(bank_card, emv_data->number, emv_data->number_len);
-        bank_card_set_back_callback(bank_card, nfc_scene_device_info_bank_card_callback, nfc);
-        if(emv_data->exp_mon) {
-            bank_card_set_exp_date(bank_card, emv_data->exp_mon, emv_data->exp_year);
-        }
-        string_t display_str;
-        string_init(display_str);
-        if(emv_data->country_code) {
+        // Parse country code
+        if((emv_data->country_code)) {
             string_t country_name;
             string_t country_name;
             string_init(country_name);
             string_init(country_name);
             if(nfc_emv_parser_get_country_name(
             if(nfc_emv_parser_get_country_name(
                    nfc->dev->storage, emv_data->country_code, country_name)) {
                    nfc->dev->storage, emv_data->country_code, country_name)) {
-                string_printf(display_str, "Reg:%s", string_get_cstr(country_name));
-                bank_card_set_country_name(bank_card, string_get_cstr(display_str));
+                string_cat_printf(temp_str, "Reg: %s", string_get_cstr(country_name));
             }
             }
             string_clear(country_name);
             string_clear(country_name);
         }
         }
-        if(emv_data->currency_code) {
-            string_t currency_name;
-            string_init(currency_name);
-            if(nfc_emv_parser_get_currency_name(
-                   nfc->dev->storage, emv_data->country_code, currency_name)) {
-                string_printf(display_str, "Cur:%s", string_get_cstr(currency_name));
-                bank_card_set_currency_name(bank_card, string_get_cstr(display_str));
-            }
-            string_clear(currency_name);
-        }
-        string_clear(display_str);
+    } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
+        string_set(temp_str, nfc->dev->dev_data.parsed_data);
     }
     }
-    scene_manager_set_scene_state(nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
+
+    widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
+
+    widget_add_button_element(
+        nfc->widget, GuiButtonTypeRight, "More", nfc_scene_device_info_widget_callback, nfc);
+
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
 
 
 bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
 bool nfc_scene_device_info_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     bool consumed = false;
     bool consumed = false;
-    uint32_t state = scene_manager_get_scene_state(nfc->scene_manager, NfcSceneDeviceInfo);
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
-        if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeLeft)) {
-            consumed = scene_manager_previous_scene(nfc->scene_manager);
-        } else if((state == NfcSceneDeviceInfoUid) && (event.event == GuiButtonTypeRight)) {
-            if(nfc->dev->format == NfcDeviceSaveFormatUid) {
-                scene_manager_set_scene_state(
-                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
-                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
-                consumed = true;
-            } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
-                scene_manager_set_scene_state(
-                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
-                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
-                consumed = true;
-            } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
-                scene_manager_set_scene_state(
-                    nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoData);
-                view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewBankCard);
-                consumed = true;
-            } else if(nfc->dev->format == NfcDeviceSaveFormatMifareDesfire) {
-                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData);
-                consumed = true;
-            }
-        } else if(state == NfcSceneDeviceInfoData && event.event == NfcCustomEventViewExit) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
-            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
-            consumed = true;
-        }
-    } else if(event.type == SceneManagerEventTypeBack) {
-        if(state == NfcSceneDeviceInfoData) {
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneDeviceInfo, NfcSceneDeviceInfoUid);
-            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+        if(event.event == GuiButtonTypeRight) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
             consumed = true;
             consumed = true;
         }
         }
     }
     }
@@ -215,12 +78,4 @@ void nfc_scene_device_info_on_exit(void* context) {
 
 
     // Clear views
     // Clear views
     widget_reset(nfc->widget);
     widget_reset(nfc->widget);
-    if(nfc->dev->format == NfcDeviceSaveFormatUid) {
-        dialog_ex_reset(nfc->dialog_ex);
-    } else if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) {
-        text_box_reset(nfc->text_box);
-        string_reset(nfc->text_box_store);
-    } else if(nfc->dev->format == NfcDeviceSaveFormatBankCard) {
-        bank_card_clear(nfc->bank_card);
-    }
 }
 }

+ 54 - 0
applications/nfc/scenes/nfc_scene_emv_menu.c

@@ -0,0 +1,54 @@
+#include "../nfc_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexSave,
+    SubmenuIndexInfo,
+};
+
+void nfc_scene_emv_menu_submenu_callback(void* context, uint32_t index) {
+    Nfc* nfc = context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_emv_menu_on_enter(void* context) {
+    Nfc* nfc = context;
+    Submenu* submenu = nfc->submenu;
+
+    submenu_add_item(submenu, "Save", SubmenuIndexSave, nfc_scene_emv_menu_submenu_callback, nfc);
+    submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_emv_menu_submenu_callback, nfc);
+    submenu_set_selected_item(
+        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneEmvMenu));
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
+}
+
+bool nfc_scene_emv_menu_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexSave) {
+            nfc->dev->format = NfcDeviceSaveFormatBankCard;
+            // Clear device name
+            nfc_device_set_name(nfc->dev, "");
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+            consumed = true;
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneEmvMenu, event.event);
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_previous_scene(nfc->scene_manager);
+    }
+
+    return consumed;
+}
+
+void nfc_scene_emv_menu_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    // Clear view
+    submenu_reset(nfc->submenu);
+}

+ 31 - 71
applications/nfc/scenes/nfc_scene_emv_read_success.c

@@ -15,86 +15,49 @@ void nfc_scene_emv_read_success_widget_callback(
 void nfc_scene_emv_read_success_on_enter(void* context) {
 void nfc_scene_emv_read_success_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     EmvData* emv_data = &nfc->dev->dev_data.emv_data;
     EmvData* emv_data = &nfc->dev->dev_data.emv_data;
-    FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 
 
     // Setup Custom Widget view
     // Setup Custom Widget view
-    // Add frame
-    widget_add_frame_element(nfc->widget, 0, 0, 128, 64, 6);
-    // Add buttons
     widget_add_button_element(
     widget_add_button_element(
         nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_emv_read_success_widget_callback, nfc);
         nfc->widget, GuiButtonTypeLeft, "Retry", nfc_scene_emv_read_success_widget_callback, nfc);
     widget_add_button_element(
     widget_add_button_element(
-        nfc->widget, GuiButtonTypeRight, "Save", nfc_scene_emv_read_success_widget_callback, nfc);
-    // Add card name
-    widget_add_string_element(
-        nfc->widget, 64, 3, AlignCenter, AlignTop, FontSecondary, nfc->dev->dev_data.emv_data.name);
-    // Add card number
-    string_t pan_str;
-    string_init(pan_str);
+        nfc->widget, GuiButtonTypeRight, "More", nfc_scene_emv_read_success_widget_callback, nfc);
+
+    string_t temp_str;
+    string_init_printf(temp_str, "\e#%s\n", emv_data->name);
     for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
     for(uint8_t i = 0; i < emv_data->number_len; i += 2) {
-        string_cat_printf(pan_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
+        string_cat_printf(temp_str, "%02X%02X ", emv_data->number[i], emv_data->number[i + 1]);
     }
     }
-    string_strim(pan_str);
-    widget_add_string_element(
-        nfc->widget, 64, 13, AlignCenter, AlignTop, FontSecondary, string_get_cstr(pan_str));
-    string_clear(pan_str);
-    // Parse country code
-    string_t country_name;
-    string_init(country_name);
-    if((emv_data->country_code) &&
-       nfc_emv_parser_get_country_name(nfc->dev->storage, emv_data->country_code, country_name)) {
-        string_t disp_country;
-        string_init_printf(disp_country, "Reg:%s", country_name);
-        widget_add_string_element(
-            nfc->widget, 7, 23, AlignLeft, AlignTop, FontSecondary, string_get_cstr(disp_country));
-        string_clear(disp_country);
+    string_strim(temp_str);
+
+    // Add expiration date
+    if(emv_data->exp_mon) {
+        string_cat_printf(temp_str, "\nExp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year);
     }
     }
-    string_clear(country_name);
     // Parse currency code
     // Parse currency code
-    string_t currency_name;
-    string_init(currency_name);
-    if((emv_data->currency_code) &&
-       nfc_emv_parser_get_currency_name(
-           nfc->dev->storage, emv_data->currency_code, currency_name)) {
-        string_t disp_currency;
-        string_init_printf(disp_currency, "Cur:%s", currency_name);
-        widget_add_string_element(
-            nfc->widget,
-            121,
-            23,
-            AlignRight,
-            AlignTop,
-            FontSecondary,
-            string_get_cstr(disp_currency));
-        string_clear(disp_currency);
+    if((emv_data->currency_code)) {
+        string_t currency_name;
+        string_init(currency_name);
+        if(nfc_emv_parser_get_currency_name(
+               nfc->dev->storage, emv_data->currency_code, currency_name)) {
+            string_cat_printf(temp_str, "\nCur: %s  ", string_get_cstr(currency_name));
+        }
+        string_clear(currency_name);
     }
     }
-    string_clear(currency_name);
-    char temp_str[32];
-    // Add ATQA
-    snprintf(temp_str, sizeof(temp_str), "ATQA: %02X%02X", nfc_data->atqa[0], nfc_data->atqa[1]);
-    widget_add_string_element(nfc->widget, 121, 32, AlignRight, AlignTop, FontSecondary, temp_str);
-    // Add UID
-    snprintf(
-        temp_str,
-        sizeof(temp_str),
-        "UID: %02X %02X %02X %02X",
-        nfc_data->uid[0],
-        nfc_data->uid[1],
-        nfc_data->uid[2],
-        nfc_data->uid[3]);
-    widget_add_string_element(nfc->widget, 7, 42, AlignLeft, AlignTop, FontSecondary, temp_str);
-    // Add SAK
-    snprintf(temp_str, sizeof(temp_str), "SAK: %02X", nfc_data->sak);
-    widget_add_string_element(nfc->widget, 121, 42, AlignRight, AlignTop, FontSecondary, temp_str);
-    // Add expiration date
-    if(emv_data->exp_mon) {
-        char exp_str[16];
-        snprintf(
-            exp_str, sizeof(exp_str), "Exp: %02X/%02X", emv_data->exp_mon, emv_data->exp_year);
-        widget_add_string_element(nfc->widget, 7, 32, AlignLeft, AlignTop, FontSecondary, exp_str);
+    // Parse country code
+    if((emv_data->country_code)) {
+        string_t country_name;
+        string_init(country_name);
+        if(nfc_emv_parser_get_country_name(
+               nfc->dev->storage, emv_data->country_code, country_name)) {
+            string_cat_printf(temp_str, "Reg: %s", string_get_cstr(country_name));
+        }
+        string_clear(country_name);
     }
     }
 
 
+    widget_add_text_scroll_element(nfc->widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
+
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
 
 
@@ -107,10 +70,7 @@ bool nfc_scene_emv_read_success_on_event(void* context, SceneManagerEvent event)
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             consumed = true;
             consumed = true;
         } else if(event.event == GuiButtonTypeRight) {
         } else if(event.event == GuiButtonTypeRight) {
-            // Clear device name
-            nfc_device_set_name(nfc->dev, "");
-            nfc->dev->format = NfcDeviceSaveFormatBankCard;
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmvMenu);
             consumed = true;
             consumed = true;
         }
         }
     } else if(event.type == SceneManagerEventTypeBack) {
     } else if(event.type == SceneManagerEventTypeBack) {

+ 1 - 0
applications/nfc/scenes/nfc_scene_file_select.c

@@ -6,6 +6,7 @@ void nfc_scene_file_select_on_enter(void* context) {
     // Process file_select return
     // Process file_select return
     nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc);
     nfc_device_set_loading_callback(nfc->dev, nfc_show_loading_popup, nfc);
     if(nfc_file_select(nfc->dev)) {
     if(nfc_file_select(nfc->dev)) {
+        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneSavedMenu, 0);
         scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
         scene_manager_next_scene(nfc->scene_manager, NfcSceneSavedMenu);
     } else {
     } else {
         scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);
         scene_manager_search_and_switch_to_previous_scene(nfc->scene_manager, NfcSceneStart);

+ 0 - 72
applications/nfc/scenes/nfc_scene_mf_classic_info.c

@@ -1,72 +0,0 @@
-#include "../nfc_i.h"
-
-void nfc_scene_mf_classic_info_widget_callback(GuiButtonType result, InputType type, void* context) {
-    furi_assert(context);
-    Nfc* nfc = context;
-
-    if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
-    }
-}
-
-void nfc_scene_mf_classic_info_on_enter(void* context) {
-    Nfc* nfc = context;
-    NfcDeviceData* dev_data = &nfc->dev->dev_data;
-    MfClassicData* mf_data = &dev_data->mf_classic_data;
-    string_t str_tmp;
-    string_init(str_tmp);
-
-    // Setup view
-    Widget* widget = nfc->widget;
-
-    widget_add_string_element(
-        widget, 0, 0, AlignLeft, AlignTop, FontSecondary, mf_classic_get_type_str(mf_data->type));
-    widget_add_string_element(
-        widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)");
-    string_printf(str_tmp, "UID:");
-    for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) {
-        string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]);
-    }
-    widget_add_string_element(
-        widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
-    string_printf(
-        str_tmp,
-        "ATQA: %02X %02X   SAK: %02X",
-        dev_data->nfc_data.atqa[0],
-        dev_data->nfc_data.atqa[1],
-        dev_data->nfc_data.sak);
-    widget_add_string_element(
-        widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
-    uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type);
-    uint8_t keys_total = sectors_total * 2;
-    uint8_t keys_found = 0;
-    uint8_t sectors_read = 0;
-    mf_classic_get_read_sectors_and_keys(mf_data, &sectors_read, &keys_found);
-    string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total);
-    widget_add_string_element(
-        widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
-    string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total);
-    widget_add_string_element(
-        widget, 0, 55, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
-
-    string_clear(str_tmp);
-    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
-}
-
-bool nfc_scene_mf_classic_info_on_event(void* context, SceneManagerEvent event) {
-    Nfc* nfc = context;
-    bool consumed = false;
-
-    if(event.type == SceneManagerEventTypeBack) {
-        consumed = scene_manager_previous_scene(nfc->scene_manager);
-    }
-
-    return consumed;
-}
-
-void nfc_scene_mf_classic_info_on_exit(void* context) {
-    Nfc* nfc = context;
-
-    // Clear view
-    widget_reset(nfc->widget);
-}

+ 1 - 0
applications/nfc/scenes/nfc_scene_mf_classic_keys.c

@@ -34,6 +34,7 @@ void nfc_scene_mf_classic_keys_on_enter(void* context) {
     widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
     widget_add_string_element(nfc->widget, 0, 32, AlignLeft, AlignTop, FontSecondary, temp_str);
     widget_add_button_element(
     widget_add_button_element(
         nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
         nfc->widget, GuiButtonTypeCenter, "Add", nfc_scene_mf_classic_keys_widget_callback, nfc);
+    widget_add_icon_element(nfc->widget, 90, 12, &I_Keychain);
 
 
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }

+ 1 - 1
applications/nfc/scenes/nfc_scene_mf_classic_menu.c

@@ -50,7 +50,7 @@ bool nfc_scene_mf_classic_menu_on_event(void* context, SceneManagerEvent event)
         } else if(event.event == SubmenuIndexInfo) {
         } else if(event.event == SubmenuIndexInfo) {
             scene_manager_set_scene_state(
             scene_manager_set_scene_state(
                 nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo);
                 nfc->scene_manager, NfcSceneMfClassicMenu, SubmenuIndexInfo);
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfClassicInfo);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
             consumed = true;
             consumed = true;
         }
         }
     } else if(event.type == SceneManagerEventTypeBack) {
     } else if(event.type == SceneManagerEventTypeBack) {

+ 10 - 33
applications/nfc/scenes/nfc_scene_mf_classic_read_success.c

@@ -17,8 +17,6 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     NfcDeviceData* dev_data = &nfc->dev->dev_data;
     NfcDeviceData* dev_data = &nfc->dev->dev_data;
     MfClassicData* mf_data = &dev_data->mf_classic_data;
     MfClassicData* mf_data = &dev_data->mf_classic_data;
-    string_t str_tmp;
-    string_init(str_tmp);
 
 
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 
 
@@ -29,48 +27,27 @@ void nfc_scene_mf_classic_read_success_on_enter(void* context) {
     widget_add_button_element(
     widget_add_button_element(
         widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
         widget, GuiButtonTypeRight, "More", nfc_scene_mf_classic_read_success_widget_callback, nfc);
 
 
+    string_t temp_str;
     if(string_size(nfc->dev->dev_data.parsed_data)) {
     if(string_size(nfc->dev->dev_data.parsed_data)) {
-        widget_add_text_box_element(
-            nfc->widget,
-            0,
-            0,
-            128,
-            32,
-            AlignLeft,
-            AlignTop,
-            string_get_cstr(nfc->dev->dev_data.parsed_data),
-            true);
+        string_init_set(temp_str, nfc->dev->dev_data.parsed_data);
     } else {
     } else {
-        widget_add_string_element(
-            widget,
-            0,
-            0,
-            AlignLeft,
-            AlignTop,
-            FontSecondary,
-            mf_classic_get_type_str(mf_data->type));
-        widget_add_string_element(
-            widget, 0, 11, AlignLeft, AlignTop, FontSecondary, "ISO 14443-3 (Type A)");
-        string_printf(str_tmp, "UID:");
+        string_init_printf(temp_str, "\e#%s\n", nfc_mf_classic_type(mf_data->type));
+        string_cat_printf(temp_str, "UID:");
         for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) {
         for(size_t i = 0; i < dev_data->nfc_data.uid_len; i++) {
-            string_cat_printf(str_tmp, " %02X", dev_data->nfc_data.uid[i]);
+            string_cat_printf(temp_str, " %02X", dev_data->nfc_data.uid[i]);
         }
         }
-        widget_add_string_element(
-            widget, 0, 22, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
         uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type);
         uint8_t sectors_total = mf_classic_get_total_sectors_num(mf_data->type);
         uint8_t keys_total = sectors_total * 2;
         uint8_t keys_total = sectors_total * 2;
         uint8_t keys_found = 0;
         uint8_t keys_found = 0;
         uint8_t sectors_read = 0;
         uint8_t sectors_read = 0;
         mf_classic_get_read_sectors_and_keys(mf_data, &sectors_read, &keys_found);
         mf_classic_get_read_sectors_and_keys(mf_data, &sectors_read, &keys_found);
-        string_printf(str_tmp, "Keys Found: %d/%d", keys_found, keys_total);
-        widget_add_string_element(
-            widget, 0, 33, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
-        string_printf(str_tmp, "Sectors Read: %d/%d", sectors_read, sectors_total);
-        widget_add_string_element(
-            widget, 0, 44, AlignLeft, AlignTop, FontSecondary, string_get_cstr(str_tmp));
+        string_cat_printf(temp_str, "\nKeys Found: %d/%d", keys_found, keys_total);
+        string_cat_printf(temp_str, "\nSectors Read: %d/%d", sectors_read, sectors_total);
     }
     }
 
 
-    string_clear(str_tmp);
+    widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
+
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
 
 

+ 17 - 0
applications/nfc/scenes/nfc_scene_mf_desfire_menu.c

@@ -2,6 +2,8 @@
 
 
 enum SubmenuIndex {
 enum SubmenuIndex {
     SubmenuIndexSave,
     SubmenuIndexSave,
+    SubmenuIndexEmulateUid,
+    SubmenuIndexInfo,
 };
 };
 
 
 void nfc_scene_mf_desfire_menu_submenu_callback(void* context, uint32_t index) {
 void nfc_scene_mf_desfire_menu_submenu_callback(void* context, uint32_t index) {
@@ -16,6 +18,15 @@ void nfc_scene_mf_desfire_menu_on_enter(void* context) {
 
 
     submenu_add_item(
     submenu_add_item(
         submenu, "Save", SubmenuIndexSave, nfc_scene_mf_desfire_menu_submenu_callback, nfc);
         submenu, "Save", SubmenuIndexSave, nfc_scene_mf_desfire_menu_submenu_callback, nfc);
+    submenu_add_item(
+        submenu,
+        "Emulate UID",
+        SubmenuIndexEmulateUid,
+        nfc_scene_mf_desfire_menu_submenu_callback,
+        nfc);
+    submenu_add_item(
+        submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_desfire_menu_submenu_callback, nfc);
+
     submenu_set_selected_item(
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMenu));
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireMenu));
 
 
@@ -35,6 +46,12 @@ bool nfc_scene_mf_desfire_menu_on_event(void* context, SceneManagerEvent event)
             nfc_device_set_name(nfc->dev, "");
             nfc_device_set_name(nfc->dev, "");
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
             consumed = true;
             consumed = true;
+        } else if(event.event == SubmenuIndexEmulateUid) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
+            consumed = true;
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+            consumed = true;
         }
         }
     }
     }
 
 

+ 48 - 53
applications/nfc/scenes/nfc_scene_mf_desfire_read_success.c

@@ -1,90 +1,85 @@
 #include "../nfc_i.h"
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
-#define NFC_SCENE_READ_SUCCESS_SHIFT "              "
-
-enum {
-    MfDesfireReadSuccessStateShowUID,
-    MfDesfireReadSuccessStateShowData,
-};
-
-void nfc_scene_mf_desfire_read_success_dialog_callback(DialogExResult result, void* context) {
+void nfc_scene_mf_desfire_read_success_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
 
 
-    view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    }
 }
 }
 
 
 void nfc_scene_mf_desfire_read_success_on_enter(void* context) {
 void nfc_scene_mf_desfire_read_success_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
 
 
+    FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data;
     MifareDesfireData* data = &nfc->dev->dev_data.mf_df_data;
-    DialogEx* dialog_ex = nfc->dialog_ex;
-    dialog_ex_set_left_button_text(dialog_ex, "Retry");
-    dialog_ex_set_center_button_text(dialog_ex, "Data");
-    dialog_ex_set_right_button_text(dialog_ex, "More");
-    dialog_ex_set_icon(dialog_ex, 8, 16, &I_Medium_chip_22x21);
+    Widget* widget = nfc->widget;
+
+    // Prepare string for data display
+    string_t temp_str;
+    string_init_printf(temp_str, "\e#MIFARE DESfire\n");
+    string_cat_printf(temp_str, "UID:");
+    for(size_t i = 0; i < nfc_data->uid_len; i++) {
+        string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
+    }
+
+    uint32_t bytes_total = 1 << (data->version.sw_storage >> 1);
+    uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0;
+    string_cat_printf(temp_str, "\n%d", bytes_total);
+    if(data->version.sw_storage & 1) {
+        string_push_back(temp_str, '+');
+    }
+    string_cat_printf(temp_str, " bytes, %d bytes free\n", bytes_free);
 
 
     uint16_t n_apps = 0;
     uint16_t n_apps = 0;
     uint16_t n_files = 0;
     uint16_t n_files = 0;
-
     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
     for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
         n_apps++;
         n_apps++;
         for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
         for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
             n_files++;
             n_files++;
         }
         }
     }
     }
+    string_cat_printf(temp_str, "%d Application", n_apps);
+    if(n_apps != 1) {
+        string_push_back(temp_str, 's');
+    }
+    string_cat_printf(temp_str, ", %d file", n_files);
+    if(n_files != 1) {
+        string_push_back(temp_str, 's');
+    }
 
 
-    // TODO rework info view
-    nfc_text_store_set(
-        nfc,
-        NFC_SCENE_READ_SUCCESS_SHIFT "Mifare DESFire\n" NFC_SCENE_READ_SUCCESS_SHIFT
-                                     "%d%s bytes\n" NFC_SCENE_READ_SUCCESS_SHIFT "%d bytes free\n"
-                                     "%d application%s, %d file%s",
-        1 << (data->version.sw_storage >> 1),
-        (data->version.sw_storage & 1) ? "+" : "",
-        data->free_memory ? data->free_memory->bytes : 0,
-        n_apps,
-        n_apps == 1 ? "" : "s",
-        n_files,
-        n_files == 1 ? "" : "s");
-    dialog_ex_set_text(dialog_ex, nfc->text_store, 8, 6, AlignLeft, AlignTop);
-    dialog_ex_set_context(dialog_ex, nfc);
-    dialog_ex_set_result_callback(dialog_ex, nfc_scene_mf_desfire_read_success_dialog_callback);
+    // Add text scroll element
+    widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
 
 
-    scene_manager_set_scene_state(
-        nfc->scene_manager, NfcSceneMfDesfireReadSuccess, MfDesfireReadSuccessStateShowUID);
-    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
+    // Add button elements
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Retry", nfc_scene_mf_desfire_read_success_widget_callback, nfc);
+    widget_add_button_element(
+        widget, GuiButtonTypeRight, "More", nfc_scene_mf_desfire_read_success_widget_callback, nfc);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
 
 
 bool nfc_scene_mf_desfire_read_success_on_event(void* context, SceneManagerEvent event) {
 bool nfc_scene_mf_desfire_read_success_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     bool consumed = false;
     bool consumed = false;
-    uint32_t state =
-        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfDesfireReadSuccess);
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
-        if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultLeft) {
+        if(event.event == GuiButtonTypeLeft) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             consumed = true;
             consumed = true;
-        } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultCenter) {
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireData);
-            consumed = true;
-        } else if(state == MfDesfireReadSuccessStateShowUID && event.event == DialogExResultRight) {
+        } else if(event.event == GuiButtonTypeRight) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireMenu);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireMenu);
             consumed = true;
             consumed = true;
         }
         }
     } else if(event.type == SceneManagerEventTypeBack) {
     } else if(event.type == SceneManagerEventTypeBack) {
-        if(state == MfDesfireReadSuccessStateShowData) {
-            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewDialogEx);
-            scene_manager_set_scene_state(
-                nfc->scene_manager,
-                NfcSceneMfDesfireReadSuccess,
-                MfDesfireReadSuccessStateShowUID);
-            consumed = true;
-        } else {
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
-            consumed = true;
-        }
+        scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
+        consumed = true;
     }
     }
 
 
     return consumed;
     return consumed;
@@ -94,5 +89,5 @@ void nfc_scene_mf_desfire_read_success_on_exit(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
 
 
     // Clean dialog
     // Clean dialog
-    dialog_ex_reset(nfc->dialog_ex);
+    widget_reset(nfc->widget);
 }
 }

+ 32 - 0
applications/nfc/scenes/nfc_scene_mf_ultralight_data.c

@@ -0,0 +1,32 @@
+#include "../nfc_i.h"
+
+void nfc_scene_mf_ultralight_data_on_enter(void* context) {
+    Nfc* nfc = context;
+    MfUltralightData* data = &nfc->dev->dev_data.mf_ul_data;
+    TextBox* text_box = nfc->text_box;
+
+    text_box_set_font(text_box, TextBoxFontHex);
+    for(uint16_t i = 0; i < data->data_size; i += 2) {
+        if(!(i % 8) && i) {
+            string_push_back(nfc->text_box_store, '\n');
+        }
+        string_cat_printf(nfc->text_box_store, "%02X%02X ", data->data[i], data->data[i + 1]);
+    }
+    text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
+}
+
+bool nfc_scene_mf_ultralight_data_on_event(void* context, SceneManagerEvent event) {
+    UNUSED(context);
+    UNUSED(event);
+    return false;
+}
+
+void nfc_scene_mf_ultralight_data_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    // Clean view
+    text_box_reset(nfc->text_box);
+    string_reset(nfc->text_box_store);
+}

+ 7 - 0
applications/nfc/scenes/nfc_scene_mf_ultralight_menu.c

@@ -4,6 +4,7 @@ enum SubmenuIndex {
     SubmenuIndexUnlock,
     SubmenuIndexUnlock,
     SubmenuIndexSave,
     SubmenuIndexSave,
     SubmenuIndexEmulate,
     SubmenuIndexEmulate,
+    SubmenuIndexInfo,
 };
 };
 
 
 void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index) {
 void nfc_scene_mf_ultralight_menu_submenu_callback(void* context, uint32_t index) {
@@ -33,6 +34,9 @@ void nfc_scene_mf_ultralight_menu_on_enter(void* context) {
         SubmenuIndexEmulate,
         SubmenuIndexEmulate,
         nfc_scene_mf_ultralight_menu_submenu_callback,
         nfc_scene_mf_ultralight_menu_submenu_callback,
         nfc);
         nfc);
+    submenu_add_item(
+        submenu, "Info", SubmenuIndexInfo, nfc_scene_mf_ultralight_menu_submenu_callback, nfc);
+
     submenu_set_selected_item(
     submenu_set_selected_item(
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu));
         nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu));
 
 
@@ -56,6 +60,9 @@ bool nfc_scene_mf_ultralight_menu_on_event(void* context, SceneManagerEvent even
         } else if(event.event == SubmenuIndexUnlock) {
         } else if(event.event == SubmenuIndexUnlock) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightUnlockMenu);
             consumed = true;
             consumed = true;
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+            consumed = true;
         }
         }
         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event);
         scene_manager_set_scene_state(nfc->scene_manager, NfcSceneMfUltralightMenu, event.event);
 
 

+ 14 - 60
applications/nfc/scenes/nfc_scene_mf_ultralight_read_success.c

@@ -1,11 +1,6 @@
 #include "../nfc_i.h"
 #include "../nfc_i.h"
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
-enum {
-    ReadMifareUlStateShowInfo,
-    ReadMifareUlStateShowData,
-};
-
 void nfc_scene_mf_ultralight_read_success_widget_callback(
 void nfc_scene_mf_ultralight_read_success_widget_callback(
     GuiButtonType result,
     GuiButtonType result,
     InputType type,
     InputType type,
@@ -31,12 +26,6 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
         "Retry",
         "Retry",
         nfc_scene_mf_ultralight_read_success_widget_callback,
         nfc_scene_mf_ultralight_read_success_widget_callback,
         nfc);
         nfc);
-    widget_add_button_element(
-        widget,
-        GuiButtonTypeCenter,
-        "Data",
-        nfc_scene_mf_ultralight_read_success_widget_callback,
-        nfc);
     widget_add_button_element(
     widget_add_button_element(
         widget,
         widget,
         GuiButtonTypeRight,
         GuiButtonTypeRight,
@@ -44,71 +33,38 @@ void nfc_scene_mf_ultralight_read_success_on_enter(void* context) {
         nfc_scene_mf_ultralight_read_success_widget_callback,
         nfc_scene_mf_ultralight_read_success_widget_callback,
         nfc);
         nfc);
 
 
-    widget_add_string_element(
-        widget, 0, 0, AlignLeft, AlignTop, FontSecondary, nfc_mf_ul_type(mf_ul_data->type, true));
-    string_t data_str;
-    string_init_printf(data_str, "UID:");
+    string_t temp_str;
+    string_init_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(mf_ul_data->type, true));
+    string_cat_printf(temp_str, "UID:");
     for(size_t i = 0; i < data->uid_len; i++) {
     for(size_t i = 0; i < data->uid_len; i++) {
-        string_cat_printf(data_str, " %02X", data->uid[i]);
+        string_cat_printf(temp_str, " %02X", data->uid[i]);
     }
     }
-    widget_add_string_element(
-        widget, 0, 13, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str));
-    string_printf(
-        data_str, "Pages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
-    widget_add_string_element(
-        widget, 0, 24, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str));
+    string_cat_printf(
+        temp_str, "\nPages Read: %d/%d", mf_ul_data->data_read / 4, mf_ul_data->data_size / 4);
     if(mf_ul_data->data_read != mf_ul_data->data_size) {
     if(mf_ul_data->data_read != mf_ul_data->data_size) {
-        widget_add_string_element(
-            widget, 0, 35, AlignLeft, AlignTop, FontSecondary, "Password-protected pages!");
-    }
-    string_clear(data_str);
-
-    // Setup TextBox view
-    TextBox* text_box = nfc->text_box;
-    text_box_set_font(text_box, TextBoxFontHex);
-    for(uint16_t i = 0; i < mf_ul_data->data_size; i += 2) {
-        if(!(i % 8) && i) {
-            string_push_back(nfc->text_box_store, '\n');
-        }
-        string_cat_printf(
-            nfc->text_box_store, "%02X%02X ", mf_ul_data->data[i], mf_ul_data->data[i + 1]);
+        string_cat_printf(temp_str, "\nPassword-protected pages!");
     }
     }
-    text_box_set_text(text_box, string_get_cstr(nfc->text_box_store));
+    widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
 
 
-    scene_manager_set_scene_state(
-        nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
 
 
 bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) {
 bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     Nfc* nfc = context;
     bool consumed = false;
     bool consumed = false;
-    uint32_t state =
-        scene_manager_get_scene_state(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
-        if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeLeft) {
+        if(event.event == GuiButtonTypeLeft) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
             consumed = true;
             consumed = true;
-        } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeRight) {
+        } else if(event.event == GuiButtonTypeRight) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightMenu);
             consumed = true;
             consumed = true;
-        } else if(state == ReadMifareUlStateShowInfo && event.event == GuiButtonTypeCenter) {
-            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewTextBox);
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowData);
-            consumed = true;
         }
         }
     } else if(event.type == SceneManagerEventTypeBack) {
     } else if(event.type == SceneManagerEventTypeBack) {
-        if(state == ReadMifareUlStateShowData) {
-            view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
-            scene_manager_set_scene_state(
-                nfc->scene_manager, NfcSceneMfUltralightReadSuccess, ReadMifareUlStateShowInfo);
-            consumed = true;
-        } else {
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
-            consumed = true;
-        }
+        scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
+        consumed = true;
     }
     }
 
 
     return consumed;
     return consumed;
@@ -117,8 +73,6 @@ bool nfc_scene_mf_ultralight_read_success_on_event(void* context, SceneManagerEv
 void nfc_scene_mf_ultralight_read_success_on_exit(void* context) {
 void nfc_scene_mf_ultralight_read_success_on_exit(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
 
 
-    // Clean views
+    // Clean view
     widget_reset(nfc->widget);
     widget_reset(nfc->widget);
-    text_box_reset(nfc->text_box);
-    string_reset(nfc->text_box_store);
 }
 }

+ 133 - 0
applications/nfc/scenes/nfc_scene_nfc_data_info.c

@@ -0,0 +1,133 @@
+#include "../nfc_i.h"
+
+void nfc_scene_nfc_data_info_widget_callback(GuiButtonType result, InputType type, void* context) {
+    Nfc* nfc = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    }
+}
+
+void nfc_scene_nfc_data_info_on_enter(void* context) {
+    Nfc* nfc = context;
+    Widget* widget = nfc->widget;
+    FuriHalNfcDevData* nfc_data = &nfc->dev->dev_data.nfc_data;
+    NfcDeviceData* dev_data = &nfc->dev->dev_data;
+    NfcProtocol protocol = dev_data->protocol;
+    uint8_t text_scroll_height = 0;
+    if((protocol == NfcDeviceProtocolMifareDesfire) || (protocol == NfcDeviceProtocolMifareUl)) {
+        widget_add_button_element(
+            widget, GuiButtonTypeRight, "More", nfc_scene_nfc_data_info_widget_callback, nfc);
+        text_scroll_height = 52;
+    } else {
+        text_scroll_height = 64;
+    }
+
+    string_t temp_str;
+    string_init(temp_str);
+    // Set name if present
+    if(nfc->dev->dev_name[0] != '\0') {
+        string_printf(temp_str, "\ec%s\n", nfc->dev->dev_name);
+    }
+
+    // Set tag type
+    if(protocol == NfcDeviceProtocolEMV) {
+        string_cat_printf(temp_str, "\e#EMV Bank Card\n");
+    } else if(protocol == NfcDeviceProtocolMifareUl) {
+        string_cat_printf(temp_str, "\e#%s\n", nfc_mf_ul_type(dev_data->mf_ul_data.type, true));
+    } else if(protocol == NfcDeviceProtocolMifareClassic) {
+        string_cat_printf(
+            temp_str, "\e#%s\n", nfc_mf_classic_type(dev_data->mf_classic_data.type));
+    } else if(protocol == NfcDeviceProtocolMifareDesfire) {
+        string_cat_printf(temp_str, "\e#MIFARE DESfire\n");
+    } else {
+        string_cat_printf(temp_str, "\e#Unknown ISO tag\n");
+    }
+
+    // Set tag iso data
+    char iso_type = FURI_BIT(nfc_data->sak, 5) ? '4' : '3';
+    string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
+    string_cat_printf(temp_str, "UID:");
+    for(size_t i = 0; i < nfc_data->uid_len; i++) {
+        string_cat_printf(temp_str, " %02X", nfc_data->uid[i]);
+    }
+    string_cat_printf(temp_str, "\nATQA: %02X %02X ", nfc_data->atqa[1], nfc_data->atqa[0]);
+    string_cat_printf(temp_str, " SAK: %02X", nfc_data->sak);
+
+    // Set application specific data
+    if(protocol == NfcDeviceProtocolMifareDesfire) {
+        MifareDesfireData* data = &dev_data->mf_df_data;
+        uint32_t bytes_total = 1 << (data->version.sw_storage >> 1);
+        uint32_t bytes_free = data->free_memory ? data->free_memory->bytes : 0;
+        string_cat_printf(temp_str, "\n%d", bytes_total);
+        if(data->version.sw_storage & 1) {
+            string_push_back(temp_str, '+');
+        }
+        string_cat_printf(temp_str, " bytes, %d bytes free\n", bytes_free);
+
+        uint16_t n_apps = 0;
+        uint16_t n_files = 0;
+        for(MifareDesfireApplication* app = data->app_head; app; app = app->next) {
+            n_apps++;
+            for(MifareDesfireFile* file = app->file_head; file; file = file->next) {
+                n_files++;
+            }
+        }
+        string_cat_printf(temp_str, "%d Application", n_apps);
+        if(n_apps != 1) {
+            string_push_back(temp_str, 's');
+        }
+        string_cat_printf(temp_str, ", %d file", n_files);
+        if(n_files != 1) {
+            string_push_back(temp_str, 's');
+        }
+    } else if(protocol == NfcDeviceProtocolMifareUl) {
+        MfUltralightData* data = &dev_data->mf_ul_data;
+        string_cat_printf(
+            temp_str, "\nPages Read %d/%d", data->data_read / 4, data->data_size / 4);
+        if(data->data_size > data->data_read) {
+            string_cat_printf(temp_str, "\nPassword-protected");
+        }
+    } else if(protocol == NfcDeviceProtocolMifareClassic) {
+        MfClassicData* data = &dev_data->mf_classic_data;
+        uint8_t sectors_total = mf_classic_get_total_sectors_num(data->type);
+        uint8_t keys_total = sectors_total * 2;
+        uint8_t keys_found = 0;
+        uint8_t sectors_read = 0;
+        mf_classic_get_read_sectors_and_keys(data, &sectors_read, &keys_found);
+        string_cat_printf(temp_str, "\nKeys Found %d/%d", keys_found, keys_total);
+        string_cat_printf(temp_str, "\nSectors Read %d/%d", sectors_read, sectors_total);
+    }
+
+    // Add text scroll widget
+    widget_add_text_scroll_element(
+        widget, 0, 0, 128, text_scroll_height, string_get_cstr(temp_str));
+    string_clear(temp_str);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_nfc_data_info_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    NfcProtocol protocol = nfc->dev->dev_data.protocol;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeRight) {
+            if(protocol == NfcDeviceProtocolMifareDesfire) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfDesfireApp);
+                consumed = true;
+            } else if(protocol == NfcDeviceProtocolMifareUl) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightData);
+                consumed = true;
+            }
+        }
+    }
+
+    return consumed;
+}
+
+void nfc_scene_nfc_data_info_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    widget_reset(nfc->widget);
+}

+ 62 - 0
applications/nfc/scenes/nfc_scene_nfca_menu.c

@@ -0,0 +1,62 @@
+#include "../nfc_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexSaveUid,
+    SubmenuIndexEmulateUid,
+    SubmenuIndexInfo,
+};
+
+void nfc_scene_nfca_menu_submenu_callback(void* context, uint32_t index) {
+    Nfc* nfc = context;
+
+    view_dispatcher_send_custom_event(nfc->view_dispatcher, index);
+}
+
+void nfc_scene_nfca_menu_on_enter(void* context) {
+    Nfc* nfc = context;
+    Submenu* submenu = nfc->submenu;
+
+    submenu_add_item(
+        submenu, "Save UID", SubmenuIndexSaveUid, nfc_scene_nfca_menu_submenu_callback, nfc);
+    submenu_add_item(
+        submenu, "Emulate UID", SubmenuIndexEmulateUid, nfc_scene_nfca_menu_submenu_callback, nfc);
+    submenu_add_item(submenu, "Info", SubmenuIndexInfo, nfc_scene_nfca_menu_submenu_callback, nfc);
+
+    submenu_set_selected_item(
+        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneNfcaMenu));
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
+}
+
+bool nfc_scene_nfca_menu_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexSaveUid) {
+            nfc->dev->format = NfcDeviceSaveFormatUid;
+            // Clear device name
+            nfc_device_set_name(nfc->dev, "");
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
+            consumed = true;
+        } else if(event.event == SubmenuIndexEmulateUid) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneEmulateUid);
+            consumed = true;
+        } else if(event.event == SubmenuIndexInfo) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(nfc->scene_manager, NfcSceneNfcaMenu, event.event);
+    } else if(event.type == SceneManagerEventTypeBack) {
+        consumed = scene_manager_previous_scene(nfc->scene_manager);
+    }
+
+    return consumed;
+}
+
+void nfc_scene_nfca_menu_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    // Clear view
+    submenu_reset(nfc->submenu);
+}

+ 72 - 0
applications/nfc/scenes/nfc_scene_nfca_read_success.c

@@ -0,0 +1,72 @@
+#include "../nfc_i.h"
+#include <dolphin/dolphin.h>
+
+void nfc_scene_nfca_read_success_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    furi_assert(context);
+    Nfc* nfc = context;
+
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(nfc->view_dispatcher, result);
+    }
+}
+
+void nfc_scene_nfca_read_success_on_enter(void* context) {
+    Nfc* nfc = context;
+
+    DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
+
+    // Setup view
+    FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
+    Widget* widget = nfc->widget;
+
+    string_t temp_str;
+    string_init_set_str(temp_str, "\e#Unknown ISO tag\n");
+
+    char iso_type = FURI_BIT(data->sak, 5) ? '4' : '3';
+    string_cat_printf(temp_str, "ISO 14443-%c (NFC-A)\n", iso_type);
+    string_cat_printf(temp_str, "UID:");
+    for(size_t i = 0; i < data->uid_len; i++) {
+        string_cat_printf(temp_str, " %02X", data->uid[i]);
+    }
+    string_cat_printf(temp_str, "\nATQA: %02X %02X ", data->atqa[1], data->atqa[0]);
+    string_cat_printf(temp_str, " SAK: %02X", data->sak);
+
+    widget_add_text_scroll_element(widget, 0, 0, 128, 52, string_get_cstr(temp_str));
+    string_clear(temp_str);
+
+    widget_add_button_element(
+        widget, GuiButtonTypeLeft, "Retry", nfc_scene_nfca_read_success_widget_callback, nfc);
+    widget_add_button_element(
+        widget, GuiButtonTypeRight, "More", nfc_scene_nfca_read_success_widget_callback, nfc);
+
+    view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
+}
+
+bool nfc_scene_nfca_read_success_on_event(void* context, SceneManagerEvent event) {
+    Nfc* nfc = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == GuiButtonTypeLeft) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneRetryConfirm);
+            consumed = true;
+        } else if(event.event == GuiButtonTypeRight) {
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaMenu);
+            consumed = true;
+        }
+    } else if(event.type == SceneManagerEventTypeBack) {
+        scene_manager_next_scene(nfc->scene_manager, NfcSceneExitConfirm);
+        consumed = true;
+    }
+    return consumed;
+}
+
+void nfc_scene_nfca_read_success_on_exit(void* context) {
+    Nfc* nfc = context;
+
+    // Clear view
+    widget_reset(nfc->widget);
+}

+ 5 - 2
applications/nfc/scenes/nfc_scene_read.c

@@ -59,11 +59,14 @@ bool nfc_scene_read_on_event(void* context, SceneManagerEvent event) {
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if((event.event == NfcWorkerEventReadUidNfcB) ||
         if((event.event == NfcWorkerEventReadUidNfcB) ||
            (event.event == NfcWorkerEventReadUidNfcF) ||
            (event.event == NfcWorkerEventReadUidNfcF) ||
-           (event.event == NfcWorkerEventReadUidNfcV) ||
-           (event.event == NfcWorkerEventReadUidNfcA)) {
+           (event.event == NfcWorkerEventReadUidNfcV)) {
             notification_message(nfc->notifications, &sequence_success);
             notification_message(nfc->notifications, &sequence_success);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneReadCardSuccess);
             consumed = true;
             consumed = true;
+        } else if(event.event == NfcWorkerEventReadUidNfcA) {
+            notification_message(nfc->notifications, &sequence_success);
+            scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcaReadSuccess);
+            consumed = true;
         } else if(event.event == NfcWorkerEventReadMfUltralight) {
         } else if(event.event == NfcWorkerEventReadMfUltralight) {
             notification_message(nfc->notifications, &sequence_success);
             notification_message(nfc->notifications, &sequence_success);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneMfUltralightReadSuccess);

+ 10 - 33
applications/nfc/scenes/nfc_scene_read_card_success.c

@@ -16,44 +16,26 @@ void nfc_scene_read_card_success_widget_callback(
 void nfc_scene_read_card_success_on_enter(void* context) {
 void nfc_scene_read_card_success_on_enter(void* context) {
     Nfc* nfc = context;
     Nfc* nfc = context;
 
 
-    string_t data_str;
-    string_t uid_str;
-    string_init(data_str);
-    string_init(uid_str);
+    string_t temp_str;
+    string_init(temp_str);
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
     DOLPHIN_DEED(DolphinDeedNfcReadSuccess);
 
 
     // Setup view
     // Setup view
     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
     FuriHalNfcDevData* data = &nfc->dev->dev_data.nfc_data;
     Widget* widget = nfc->widget;
     Widget* widget = nfc->widget;
-    string_set_str(data_str, nfc_get_dev_type(data->type));
-    string_set_str(uid_str, "UID:");
+    string_set_str(temp_str, nfc_get_dev_type(data->type));
+    widget_add_string_element(
+        widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, string_get_cstr(temp_str));
+    string_set_str(temp_str, "UID:");
     for(uint8_t i = 0; i < data->uid_len; i++) {
     for(uint8_t i = 0; i < data->uid_len; i++) {
-        string_cat_printf(uid_str, " %02X", data->uid[i]);
+        string_cat_printf(temp_str, " %02X", data->uid[i]);
     }
     }
-
+    widget_add_string_element(
+        widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(temp_str));
     widget_add_button_element(
     widget_add_button_element(
         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc);
         widget, GuiButtonTypeLeft, "Retry", nfc_scene_read_card_success_widget_callback, nfc);
-    if(data->type == FuriHalNfcTypeA) {
-        widget_add_button_element(
-            widget, GuiButtonTypeRight, "Save", nfc_scene_read_card_success_widget_callback, nfc);
-        widget_add_icon_element(widget, 8, 13, &I_Medium_chip_22x21);
-        widget_add_string_element(
-            widget, 37, 12, AlignLeft, AlignBottom, FontPrimary, string_get_cstr(data_str));
-        string_printf(
-            data_str, "ATQA: %02X%02X\nSAK: %02X", data->atqa[0], data->atqa[1], data->sak);
-        widget_add_string_multiline_element(
-            widget, 37, 16, AlignLeft, AlignTop, FontSecondary, string_get_cstr(data_str));
-        widget_add_string_element(
-            widget, 64, 46, AlignCenter, AlignBottom, FontSecondary, string_get_cstr(uid_str));
-    } else {
-        widget_add_string_element(
-            widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, string_get_cstr(data_str));
-        widget_add_string_element(
-            widget, 64, 32, AlignCenter, AlignCenter, FontSecondary, string_get_cstr(uid_str));
-    }
 
 
-    string_clear(data_str);
-    string_clear(uid_str);
+    string_clear(temp_str);
 
 
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewWidget);
 }
 }
@@ -65,11 +47,6 @@ bool nfc_scene_read_card_success_on_event(void* context, SceneManagerEvent event
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == GuiButtonTypeLeft) {
         if(event.event == GuiButtonTypeLeft) {
             consumed = scene_manager_previous_scene(nfc->scene_manager);
             consumed = scene_manager_previous_scene(nfc->scene_manager);
-        } else if(event.event == GuiButtonTypeRight) {
-            nfc->dev->format = NfcDeviceSaveFormatUid;
-            nfc_device_set_name(nfc->dev, "");
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneSaveName);
-            consumed = true;
         }
         }
     }
     }
     return consumed;
     return consumed;

+ 15 - 3
applications/nfc/scenes/nfc_scene_saved_menu.c

@@ -44,8 +44,6 @@ void nfc_scene_saved_menu_on_enter(void* context) {
     }
     }
     submenu_add_item(
     submenu_add_item(
         submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
         submenu, "Info", SubmenuIndexInfo, nfc_scene_saved_menu_submenu_callback, nfc);
-    submenu_set_selected_item(
-        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
     if(nfc->dev->shadow_file_exist) {
     if(nfc->dev->shadow_file_exist) {
         submenu_add_item(
         submenu_add_item(
             submenu,
             submenu,
@@ -58,12 +56,15 @@ void nfc_scene_saved_menu_on_enter(void* context) {
         submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc);
         submenu, "Rename", SubmenuIndexRename, nfc_scene_saved_menu_submenu_callback, nfc);
     submenu_add_item(
     submenu_add_item(
         submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc);
         submenu, "Delete", SubmenuIndexDelete, nfc_scene_saved_menu_submenu_callback, nfc);
+    submenu_set_selected_item(
+        nfc->submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneSavedMenu));
 
 
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
     view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu);
 }
 }
 
 
 bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
 bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
     Nfc* nfc = context;
     Nfc* nfc = context;
+    NfcDeviceData* dev_data = &nfc->dev->dev_data;
     bool consumed = false;
     bool consumed = false;
 
 
     if(event.type == SceneManagerEventTypeCustom) {
     if(event.type == SceneManagerEventTypeCustom) {
@@ -87,7 +88,18 @@ bool nfc_scene_saved_menu_on_event(void* context, SceneManagerEvent event) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneDelete);
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexInfo) {
         } else if(event.event == SubmenuIndexInfo) {
-            scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
+            bool application_info_present = false;
+            if(dev_data->protocol == NfcDeviceProtocolEMV) {
+                application_info_present = true;
+            } else if(dev_data->protocol == NfcDeviceProtocolMifareClassic) {
+                application_info_present = nfc_supported_card_verify_and_parse(dev_data);
+            }
+
+            if(application_info_present) {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneDeviceInfo);
+            } else {
+                scene_manager_next_scene(nfc->scene_manager, NfcSceneNfcDataInfo);
+            }
             consumed = true;
             consumed = true;
         } else if(event.event == SubmenuIndexRestoreOriginal) {
         } else if(event.event == SubmenuIndexRestoreOriginal) {
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);
             scene_manager_next_scene(nfc->scene_manager, NfcSceneRestoreOriginalConfirm);

+ 0 - 80
applications/nfc/views/bank_card.c

@@ -1,80 +0,0 @@
-#include "bank_card.h"
-#include "../helpers/nfc_emv_parser.h"
-#include <m-string.h>
-
-struct BankCard {
-    Widget* widget;
-};
-
-BankCard* bank_card_alloc() {
-    BankCard* bank_card = malloc(sizeof(BankCard));
-    bank_card->widget = widget_alloc();
-    return bank_card;
-}
-
-void bank_card_free(BankCard* bank_card) {
-    furi_assert(bank_card);
-    widget_free(bank_card->widget);
-    free(bank_card);
-}
-
-View* bank_card_get_view(BankCard* bank_card) {
-    furi_assert(bank_card);
-    return widget_get_view(bank_card->widget);
-}
-
-void bank_card_clear(BankCard* bank_card) {
-    furi_assert(bank_card);
-    widget_reset(bank_card->widget);
-}
-
-void bank_card_set_name(BankCard* bank_card, char* name) {
-    furi_assert(bank_card);
-    furi_assert(name);
-    widget_add_string_element(
-        bank_card->widget, 64, 6, AlignCenter, AlignTop, FontSecondary, name);
-}
-
-void bank_card_set_number(BankCard* bank_card, uint8_t* number, uint8_t len) {
-    furi_assert(bank_card);
-    furi_assert(number);
-    string_t num_str;
-    string_init(num_str);
-    for(uint8_t i = 0; i < len; i += 2) {
-        string_cat_printf(num_str, "%02X%02X ", number[i], number[i + 1]);
-    }
-    // Add number
-    widget_add_string_element(
-        bank_card->widget, 64, 32, AlignCenter, AlignTop, FontSecondary, string_get_cstr(num_str));
-    string_clear(num_str);
-    // Add icon
-    widget_add_icon_element(bank_card->widget, 8, 15, &I_Detailed_chip_17x13);
-    // Add frame
-    widget_add_frame_element(bank_card->widget, 0, 0, 128, 64, 6);
-}
-
-void bank_card_set_back_callback(BankCard* bank_card, ButtonCallback callback, void* context) {
-    furi_assert(bank_card);
-    furi_assert(callback);
-    widget_add_button_element(bank_card->widget, GuiButtonTypeLeft, "Back", callback, context);
-}
-
-void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year) {
-    furi_assert(bank_card);
-    char exp_date_str[16];
-    snprintf(exp_date_str, sizeof(exp_date_str), "Exp: %02X/%02X", mon, year);
-    widget_add_string_element(
-        bank_card->widget, 122, 54, AlignRight, AlignBottom, FontSecondary, exp_date_str);
-}
-
-void bank_card_set_country_name(BankCard* bank_card, const char* country_name) {
-    furi_assert(bank_card);
-    widget_add_string_element(
-        bank_card->widget, 120, 18, AlignRight, AlignTop, FontSecondary, country_name);
-}
-
-void bank_card_set_currency_name(BankCard* bank_card, const char* currency_name) {
-    furi_assert(bank_card);
-    widget_add_string_element(
-        bank_card->widget, 31, 18, AlignLeft, AlignTop, FontSecondary, currency_name);
-}

+ 0 - 26
applications/nfc/views/bank_card.h

@@ -1,26 +0,0 @@
-#pragma once
-#include <stdint.h>
-#include <gui/view.h>
-#include <gui/modules/widget.h>
-
-typedef struct BankCard BankCard;
-
-BankCard* bank_card_alloc();
-
-void bank_card_free(BankCard* bank_card);
-
-void bank_card_clear(BankCard* bank_card);
-
-View* bank_card_get_view(BankCard* bank_card);
-
-void bank_card_set_back_callback(BankCard* bank_card, ButtonCallback callback, void* context);
-
-void bank_card_set_name(BankCard* bank_card, char* name);
-
-void bank_card_set_number(BankCard* bank_card, uint8_t* number, uint8_t len);
-
-void bank_card_set_exp_date(BankCard* bank_card, uint8_t mon, uint8_t year);
-
-void bank_card_set_country_name(BankCard* bank_card, const char* country_name);
-
-void bank_card_set_currency_name(BankCard* bank_card, const char* currency_name);

BIN
assets/icons/NFC/Keychain.png


+ 1 - 0
lib/nfc/nfc_device.c

@@ -1217,6 +1217,7 @@ void nfc_device_data_clear(NfcDeviceData* dev_data) {
 void nfc_device_clear(NfcDevice* dev) {
 void nfc_device_clear(NfcDevice* dev) {
     furi_assert(dev);
     furi_assert(dev);
 
 
+    nfc_device_set_name(dev, "");
     nfc_device_data_clear(&dev->dev_data);
     nfc_device_data_clear(&dev->dev_data);
     dev->format = NfcDeviceSaveFormatUid;
     dev->format = NfcDeviceSaveFormatUid;
     string_reset(dev->load_path);
     string_reset(dev->load_path);

+ 1 - 1
lib/nfc/nfc_worker.c

@@ -143,7 +143,7 @@ static bool nfc_worker_read_mf_classic(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) {
                 if(nfc_supported_card[i].verify(nfc_worker, tx_rx)) {
                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) {
                     if(nfc_supported_card[i].read(nfc_worker, tx_rx)) {
                         read_success = true;
                         read_success = true;
-                        nfc_supported_card[i].parse(nfc_worker);
+                        nfc_supported_card[i].parse(nfc_worker->dev_data);
                     }
                     }
                 }
                 }
             }
             }

+ 14 - 0
lib/nfc/parsers/nfc_supported_card.c

@@ -11,3 +11,17 @@ NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd] = {
             .parse = troyka_parser_parse,
             .parse = troyka_parser_parse,
         },
         },
 };
 };
+
+bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data) {
+    furi_assert(dev_data);
+
+    bool card_parsed = false;
+    for(size_t i = 0; i < COUNT_OF(nfc_supported_card); i++) {
+        if(nfc_supported_card[i].parse(dev_data)) {
+            card_parsed = true;
+            break;
+        }
+    }
+
+    return card_parsed;
+}

+ 4 - 1
lib/nfc/parsers/nfc_supported_card.h

@@ -2,6 +2,7 @@
 
 
 #include <furi_hal_nfc.h>
 #include <furi_hal_nfc.h>
 #include "../nfc_worker.h"
 #include "../nfc_worker.h"
+#include "../nfc_device.h"
 
 
 #include <m-string.h>
 #include <m-string.h>
 
 
@@ -15,7 +16,7 @@ typedef bool (*NfcSupportedCardVerify)(NfcWorker* nfc_worker, FuriHalNfcTxRxCont
 
 
 typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
 typedef bool (*NfcSupportedCardRead)(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
 
 
-typedef bool (*NfcSupportedCardParse)(NfcWorker* nfc_worker);
+typedef bool (*NfcSupportedCardParse)(NfcDeviceData* dev_data);
 
 
 typedef struct {
 typedef struct {
     NfcProtocol protocol;
     NfcProtocol protocol;
@@ -25,3 +26,5 @@ typedef struct {
 } NfcSupportedCard;
 } NfcSupportedCard;
 
 
 extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd];
 extern NfcSupportedCard nfc_supported_card[NfcSupportedCardTypeEnd];
+
+bool nfc_supported_card_verify_and_parse(NfcDeviceData* dev_data);

+ 25 - 17
lib/nfc/parsers/troyka_parser.c

@@ -49,23 +49,31 @@ bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx) {
     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
     return mf_classic_read_card(tx_rx, &reader, &nfc_worker->dev_data->mf_classic_data) == 16;
 }
 }
 
 
-bool troyka_parser_parse(NfcWorker* nfc_worker) {
-    MfClassicData* data = &nfc_worker->dev_data->mf_classic_data;
-    uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
-    uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
-    temp_ptr = &data->block[8 * 4].value[3];
-    uint32_t number = 0;
-    for(size_t i = 0; i < 4; i++) {
-        number <<= 8;
-        number |= temp_ptr[i];
-    }
-    number >>= 4;
+bool troyka_parser_parse(NfcDeviceData* dev_data) {
+    MfClassicData* data = &dev_data->mf_classic_data;
+    bool troyka_parsed = false;
+
+    do {
+        // Verify key
+        MfClassicSectorTrailer* sec_tr = mf_classic_get_sector_trailer_by_sector(data, 8);
+        uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6);
+        if(key != troyka_keys[8].key_a) break;
+
+        // Parse data
+        uint8_t* temp_ptr = &data->block[8 * 4 + 1].value[5];
+        uint16_t balance = ((temp_ptr[0] << 8) | temp_ptr[1]) / 25;
+        temp_ptr = &data->block[8 * 4].value[3];
+        uint32_t number = 0;
+        for(size_t i = 0; i < 4; i++) {
+            number <<= 8;
+            number |= temp_ptr[i];
+        }
+        number >>= 4;
 
 
-    string_printf(
-        nfc_worker->dev_data->parsed_data,
-        "Troyka Transport card\nNumber: %ld\nBalance: %d rub",
-        number,
-        balance);
+        string_printf(
+            dev_data->parsed_data, "\e#Troyka\nNum: %ld\nBalance: %d rur.", number, balance);
+        troyka_parsed = true;
+    } while(false);
 
 
-    return true;
+    return troyka_parsed;
 }
 }

+ 1 - 1
lib/nfc/parsers/troyka_parser.h

@@ -6,4 +6,4 @@ bool troyka_parser_verify(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
 
 
 bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
 bool troyka_parser_read(NfcWorker* nfc_worker, FuriHalNfcTxRxContext* tx_rx);
 
 
-bool troyka_parser_parse(NfcWorker* nfc_worker);
+bool troyka_parser_parse(NfcDeviceData* dev_data);

+ 1 - 43
lib/nfc/protocols/mifare_ultralight.c

@@ -191,7 +191,7 @@ bool mf_ultralight_authenticate(FuriHalNfcTxRxContext* tx_rx, uint32_t key, uint
         }
         }
 
 
         if(pack != NULL) {
         if(pack != NULL) {
-            *pack = (tx_rx->rx_data[0] << 8) | tx_rx->rx_data[1];
+            *pack = (tx_rx->rx_data[1] << 8) | tx_rx->rx_data[0];
         }
         }
 
 
         FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack);
         FURI_LOG_I(TAG, "Auth success. Password: %08X. PACK: %04X", key, *pack);
@@ -697,48 +697,6 @@ bool mf_ultralight_read_counters(FuriHalNfcTxRxContext* tx_rx, MfUltralightData*
     return counter_read == (is_single_counter ? 1 : 3);
     return counter_read == (is_single_counter ? 1 : 3);
 }
 }
 
 
-int16_t mf_ultralight_get_authlim(
-    FuriHalNfcTxRxContext* tx_rx,
-    MfUltralightReader* reader,
-    MfUltralightData* data) {
-    mf_ultralight_read_version(tx_rx, reader, data);
-    if(!(reader->supported_features & MfUltralightSupportAuth)) {
-        // No authentication
-        return -2;
-    }
-
-    uint8_t config_pages_index;
-    if(data->type >= MfUltralightTypeUL11 && data->type <= MfUltralightTypeNTAG216) {
-        config_pages_index = reader->pages_to_read - 4;
-    } else if(
-        data->type >= MfUltralightTypeNTAGI2CPlus1K &&
-        data->type <= MfUltralightTypeNTAGI2CPlus1K) {
-        config_pages_index = 0xe3;
-    } else {
-        // No config pages
-        return -2;
-    }
-
-    if(!mf_ultralight_read_pages_direct(tx_rx, config_pages_index, data->data)) {
-        // Config pages are not readable due to protection
-        return -1;
-    }
-
-    MfUltralightConfigPages* config_pages = (MfUltralightConfigPages*)&data->data;
-    if(config_pages->auth0 >= reader->pages_to_read) {
-        // Authentication is not configured
-        return -2;
-    }
-
-    int16_t authlim = config_pages->access.authlim;
-    if(authlim > 0 && data->type >= MfUltralightTypeNTAGI2CPlus1K &&
-       data->type <= MfUltralightTypeNTAGI2CPlus2K) {
-        authlim = 1 << authlim;
-    }
-
-    return authlim;
-}
-
 bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) {
 bool mf_ultralight_read_tearing_flags(FuriHalNfcTxRxContext* tx_rx, MfUltralightData* data) {
     uint8_t flag_read = 0;
     uint8_t flag_read = 0;
 
 

+ 0 - 12
lib/nfc/protocols/mifare_ultralight.h

@@ -56,13 +56,6 @@ typedef enum {
     MfUltralightTypeNum,
     MfUltralightTypeNum,
 } MfUltralightType;
 } MfUltralightType;
 
 
-typedef enum {
-    MfUltralightAuthLimitUnknown,
-    MfUltralightAuthLimitNotSupported,
-    MfUltralightAuthLimitConfigured,
-    MfUltralightAuthLimitNotConfigured,
-} MfUltralightAuthLimit;
-
 typedef enum {
 typedef enum {
     MfUltralightSupportNone = 0,
     MfUltralightSupportNone = 0,
     MfUltralightSupportFastRead = 1 << 0,
     MfUltralightSupportFastRead = 1 << 0,
@@ -245,11 +238,6 @@ bool mf_ul_prepare_emulation_response(
     uint32_t* data_type,
     uint32_t* data_type,
     void* context);
     void* context);
 
 
-int16_t mf_ultralight_get_authlim(
-    FuriHalNfcTxRxContext* tx_rx,
-    MfUltralightReader* reader,
-    MfUltralightData* data);
-
 uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data);
 uint32_t mf_ul_pwdgen_amiibo(FuriHalNfcDevData* data);
 
 
 uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data);
 uint32_t mf_ul_pwdgen_xiaomi(FuriHalNfcDevData* data);