Explorar el Código

[FL-2139] SubGhz: refactoring GUI SubGhz (#910)

* SubGhz: refactoring GUI ReadRAW
* SubGhz: replacing memcpy with strcpy
* SubGhz: fix button name "Mode",  fix delete scene, fix show name when loading a key
* SubGhz: refactoring GUI SubGhz
* scene_manager: add exit from last scene API
* subghz: stop scene manager before stopping view dispatcher

Co-authored-by: gornekich <n.gorbadey@gmail.com>
Co-authored-by: あく <alleteam@gmail.com>
Skorpionm hace 4 años
padre
commit
3a86da5526

+ 9 - 0
applications/gui/scene_manager.c

@@ -181,3 +181,12 @@ bool scene_manager_search_and_switch_to_another_scene(
 
     return true;
 }
+
+void scene_manager_stop(SceneManager* scene_manager) {
+    furi_assert(scene_manager);
+
+    if(SceneManagerIdStack_size(scene_manager->scene_id_stack)) {
+        uint32_t cur_scene_id = *SceneManagerIdStack_back(scene_manager->scene_id_stack);
+        scene_manager->scene_handlers->on_exit_handlers[cur_scene_id](scene_manager->context);
+    }
+}

+ 6 - 0
applications/gui/scene_manager.h

@@ -157,6 +157,12 @@ bool scene_manager_search_and_switch_to_another_scene(
     SceneManager* scene_manager,
     uint32_t scene_id);
 
+/** Exit from current scene
+ *
+ * @param      scene_manager  SceneManager instance
+ */
+void scene_manager_stop(SceneManager* scene_manager);
+
 #ifdef __cplusplus
 }
 #endif

+ 8 - 1
applications/subghz/helpers/subghz_custom_event.h

@@ -6,12 +6,17 @@ typedef enum {
 
     SubghzCustomEventSceneDeleteSuccess = 100,
     SubghzCustomEventSceneDelete,
+    SubghzCustomEventSceneDeleteRAW,
+    SubghzCustomEventSceneDeleteRAWBack,
+
     SubghzCustomEventSceneReceiverInfoTxStart,
     SubghzCustomEventSceneReceiverInfoTxStop,
     SubghzCustomEventSceneReceiverInfoSave,
     SubghzCustomEventSceneSaveName,
     SubghzCustomEventSceneSaveSuccess,
-    SubghzCustomEventSceneShowError,
+    SubghzCustomEventSceneShowErrorBack,
+    SubghzCustomEventSceneShowErrorOk,
+    SubghzCustomEventSceneShowErrorSub,
     SubghzCustomEventSceneShowOnlyRX,
 
     SubghzCustomEventSceneExit,
@@ -30,6 +35,8 @@ typedef enum {
     SubghzCustomEventViewReadRAWSendStop,
     SubghzCustomEventViewReadRAWSave,
     SubghzCustomEventViewReadRAWVibro,
+    SubghzCustomEventViewReadRAWTXRXStop,
+    SubghzCustomEventViewReadRAWMore,
 
     SubghzCustomEventViewTransmitterBack,
     SubghzCustomEventViewTransmitterSendStart,

+ 3 - 0
applications/subghz/scenes/subghz_scene_config.h

@@ -7,6 +7,7 @@ ADD_SCENE(subghz, save_success, SaveSuccess)
 ADD_SCENE(subghz, saved, Saved)
 ADD_SCENE(subghz, transmitter, Transmitter)
 ADD_SCENE(subghz, show_error, ShowError)
+ADD_SCENE(subghz, show_error_sub, ShowErrorSub)
 ADD_SCENE(subghz, show_only_rx, ShowOnlyRx)
 ADD_SCENE(subghz, saved_menu, SavedMenu)
 ADD_SCENE(subghz, delete, Delete)
@@ -18,4 +19,6 @@ ADD_SCENE(subghz, test_packet, TestPacket)
 ADD_SCENE(subghz, set_type, SetType)
 ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer)
 ADD_SCENE(subghz, read_raw, ReadRAW)
+ADD_SCENE(subghz, more_raw, MoreRAW)
+ADD_SCENE(subghz, delete_raw, DeleteRAW)
 ADD_SCENE(subghz, need_saving, NeedSaving)

+ 1 - 1
applications/subghz/scenes/subghz_scene_delete.c

@@ -50,7 +50,7 @@ bool subghz_scene_delete_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubghzCustomEventSceneDelete) {
-            memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name) + 1);
+            strcpy(subghz->file_name_tmp, subghz->file_name);
             if(subghz_delete_file(subghz)) {
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
             } else {

+ 79 - 0
applications/subghz/scenes/subghz_scene_delete_raw.c

@@ -0,0 +1,79 @@
+#include "../subghz_i.h"
+#include "../helpers/subghz_custom_event.h"
+
+void subghz_scene_delete_raw_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
+    SubGhz* subghz = context;
+    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
+        view_dispatcher_send_custom_event(
+            subghz->view_dispatcher, SubghzCustomEventSceneDeleteRAW);
+    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
+        view_dispatcher_send_custom_event(
+            subghz->view_dispatcher, SubghzCustomEventSceneDeleteRAWBack);
+    }
+}
+
+void subghz_scene_delete_raw_on_enter(void* context) {
+    SubGhz* subghz = context;
+    string_t frequency_str;
+    string_t modulation_str;
+    string_t text;
+
+    string_init(frequency_str);
+    string_init(modulation_str);
+    string_init(text);
+
+    string_printf(text, "Delete\n%s?", subghz->file_name);
+    widget_add_string_multiline_element(
+        subghz->widget, 64, 0, AlignCenter, AlignTop, FontPrimary, string_get_cstr(text));
+
+    widget_add_string_element(
+        subghz->widget, 38, 25, AlignLeft, AlignTop, FontSecondary, "RAW signal");
+    subghz_get_frequency_modulation(subghz, frequency_str, modulation_str);
+    widget_add_string_element(
+        subghz->widget, 35, 37, AlignLeft, AlignTop, FontSecondary, string_get_cstr(frequency_str));
+
+    widget_add_string_element(
+        subghz->widget,
+        72,
+        37,
+        AlignLeft,
+        AlignTop,
+        FontSecondary,
+        string_get_cstr(modulation_str));
+
+    string_clear(frequency_str);
+    string_clear(modulation_str);
+    string_clear(text);
+
+    widget_add_button_element(
+        subghz->widget, GuiButtonTypeRight, "Delete", subghz_scene_delete_raw_callback, subghz);
+    widget_add_button_element(
+        subghz->widget, GuiButtonTypeLeft, "Back", subghz_scene_delete_raw_callback, subghz);
+
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
+}
+
+bool subghz_scene_delete_raw_on_event(void* context, SceneManagerEvent event) {
+    SubGhz* subghz = context;
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubghzCustomEventSceneDeleteRAW) {
+            strcpy(subghz->file_name_tmp, subghz->file_name);
+            if(subghz_delete_file(subghz)) {
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteSuccess);
+            } else {
+                scene_manager_search_and_switch_to_previous_scene(
+                    subghz->scene_manager, SubGhzSceneStart);
+            }
+            return true;
+        } else if(event.event == SubghzCustomEventSceneDeleteRAWBack) {
+            return scene_manager_previous_scene(subghz->scene_manager);
+        }
+    }
+    return false;
+}
+
+void subghz_scene_delete_raw_on_exit(void* context) {
+    SubGhz* subghz = context;
+    widget_clear(subghz->widget);
+}

+ 5 - 2
applications/subghz/scenes/subghz_scene_delete_success.c

@@ -26,8 +26,11 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event
 
     if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubghzCustomEventSceneDeleteSuccess) {
-            return scene_manager_search_and_switch_to_previous_scene(
-                subghz->scene_manager, SubGhzSceneStart);
+            if(!scene_manager_search_and_switch_to_previous_scene(
+                   subghz->scene_manager, SubGhzSceneSaved)) {
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
+            }
+            return true;
         }
     }
     return false;

+ 60 - 0
applications/subghz/scenes/subghz_scene_more_raw.c

@@ -0,0 +1,60 @@
+#include "../subghz_i.h"
+
+enum SubmenuIndex {
+    SubmenuIndexEdit,
+    SubmenuIndexDelete,
+};
+
+void subghz_scene_more_raw_submenu_callback(void* context, uint32_t index) {
+    SubGhz* subghz = context;
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, index);
+}
+
+void subghz_scene_more_raw_on_enter(void* context) {
+    SubGhz* subghz = context;
+
+    submenu_add_item(
+        subghz->submenu,
+        "Edit name",
+        SubmenuIndexEdit,
+        subghz_scene_more_raw_submenu_callback,
+        subghz);
+
+    submenu_add_item(
+        subghz->submenu,
+        "Delete",
+        SubmenuIndexDelete,
+        subghz_scene_more_raw_submenu_callback,
+        subghz);
+
+    submenu_set_selected_item(
+        subghz->submenu, scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneMoreRAW));
+
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu);
+}
+
+bool subghz_scene_more_raw_on_event(void* context, SceneManagerEvent event) {
+    SubGhz* subghz = context;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexDelete) {
+            scene_manager_set_scene_state(
+                subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerNoSet);
+            scene_manager_set_scene_state(
+                subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexDelete);
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneDeleteRAW);
+            return true;
+        } else if(event.event == SubmenuIndexEdit) {
+            scene_manager_set_scene_state(
+                subghz->scene_manager, SubGhzSceneMoreRAW, SubmenuIndexEdit);
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveName);
+            return true;
+        }
+    }
+    return false;
+}
+
+void subghz_scene_more_raw_on_exit(void* context) {
+    SubGhz* subghz = context;
+    submenu_clean(subghz->submenu);
+}

+ 86 - 64
applications/subghz/scenes/subghz_scene_read_raw.c

@@ -6,6 +6,32 @@
 
 #define RAW_FILE_NAME "Raw_temp"
 
+bool subghz_scene_read_raw_update_filename(SubGhz* subghz) {
+    bool ret = false;
+    //set the path to read the file
+    if(strcmp(
+           subghz_protocol_raw_get_last_file_name(
+               (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
+           "")) {
+        string_t temp_str;
+        string_init_printf(
+            temp_str,
+            "%s",
+            subghz_protocol_raw_get_last_file_name(
+                (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
+        path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str);
+        strcpy(subghz->file_name, string_get_cstr(temp_str));
+        string_printf(
+            temp_str, "%s/%s%s", SUBGHZ_APP_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION);
+
+        subghz_protocol_raw_set_last_file_name(
+            (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
+        string_clear(temp_str);
+        ret = true;
+    }
+    return ret;
+}
+
 static void subghz_scene_read_raw_update_statusbar(void* context) {
     furi_assert(context);
     SubGhz* subghz = context;
@@ -40,15 +66,24 @@ void subghz_scene_read_raw_callback_end_tx(void* context) {
 void subghz_scene_read_raw_on_enter(void* context) {
     SubGhz* subghz = context;
 
-    if(subghz->txrx->rx_key_state == SubGhzRxKeyStateBack) {
+    switch(subghz->txrx->rx_key_state) {
+    case SubGhzRxKeyStateBack:
         subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusIDLE, "");
-    } else if(subghz->txrx->rx_key_state == SubGhzRxKeyStateRAWLoad) {
+        break;
+    case SubGhzRxKeyStateRAWLoad:
+        subghz_read_raw_set_status(
+            subghz->subghz_read_raw, SubghzReadRAWStatusLoadKeyTX, subghz->file_name);
+        subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+        break;
+    case SubGhzRxKeyStateRAWSave:
         subghz_read_raw_set_status(
-            subghz->subghz_read_raw, SubghzReadRAWStatusTX, subghz->file_name);
-        subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
-    } else {
+            subghz->subghz_read_raw, SubghzReadRAWStatusSaveKey, subghz->file_name);
+        subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+        break;
+    default:
         subghz_read_raw_set_status(subghz->subghz_read_raw, SubghzReadRAWStatusStart, "");
         subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
+        break;
     }
 
     subghz_scene_read_raw_update_statusbar(subghz);
@@ -96,53 +131,64 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                 //Restore default setting
                 subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
                 subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
-                scene_manager_search_and_switch_to_previous_scene(
-                    subghz->scene_manager, SubGhzSceneStart);
+                if(!scene_manager_search_and_switch_to_previous_scene(
+                       subghz->scene_manager, SubGhzSceneSaved)) {
+                    if(!scene_manager_search_and_switch_to_previous_scene(
+                           subghz->scene_manager, SubGhzSceneStart)) {
+                        scene_manager_stop(subghz->scene_manager);
+                        view_dispatcher_stop(subghz->view_dispatcher);
+                    }
+                }
             }
+            return true;
+            break;
 
+        case SubghzCustomEventViewReadRAWTXRXStop:
+            //Stop TX
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
+                subghz_tx_stop(subghz);
+                subghz_sleep(subghz);
+            }
+            //Stop RX
+            if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
+                subghz_rx_end(subghz);
+                subghz_sleep(subghz);
+            };
+            subghz->state_notifications = SubGhzNotificationStateIDLE;
             return true;
             break;
+
         case SubghzCustomEventViewReadRAWConfig:
             scene_manager_set_scene_state(
                 subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet);
             scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiverConfig);
             return true;
             break;
+
         case SubghzCustomEventViewReadRAWErase:
             subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE;
             return true;
             break;
+
         case SubghzCustomEventViewReadRAWVibro:
             notification_message(subghz->notifications, &sequence_single_vibro);
             return true;
             break;
+
+        case SubghzCustomEventViewReadRAWMore:
+            if(subghz_scene_read_raw_update_filename(subghz)) {
+                scene_manager_set_scene_state(
+                    subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet);
+                subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneMoreRAW);
+                return true;
+            } else {
+                furi_crash(NULL);
+            }
+            break;
+
         case SubghzCustomEventViewReadRAWSendStart:
-            //set the path to read the file
-            if(strcmp(
-                   subghz_protocol_raw_get_last_file_name(
-                       (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
-                   "")) {
-                string_t temp_str;
-                string_init_printf(
-                    temp_str,
-                    "%s",
-                    subghz_protocol_raw_get_last_file_name(
-                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
-                path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str);
-                strlcpy(
-                    subghz->file_name,
-                    string_get_cstr(temp_str),
-                    strlen(string_get_cstr(temp_str)) + 1);
-                string_printf(
-                    temp_str,
-                    "%s/%s%s",
-                    SUBGHZ_APP_PATH_FOLDER,
-                    subghz->file_name,
-                    SUBGHZ_APP_EXTENSION);
-
-                subghz_protocol_raw_set_last_file_name(
-                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
-                string_clear(temp_str);
+            if(subghz_scene_read_raw_update_filename(subghz)) {
                 //start send
                 subghz->state_notifications = SubGhzNotificationStateIDLE;
                 if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
@@ -154,12 +200,12 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                         scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx);
                     } else {
                         subghz->state_notifications = SubGhzNotificationStateTX;
-                        subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
                     }
                 }
             }
             return true;
             break;
+
         case SubghzCustomEventViewReadRAWSendStop:
             subghz->state_notifications = SubGhzNotificationStateIDLE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) {
@@ -169,6 +215,7 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
             subghz_read_raw_stop_send(subghz->subghz_read_raw);
             return true;
             break;
+
         case SubghzCustomEventViewReadRAWIDLE:
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
@@ -182,8 +229,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
 
             return true;
             break;
-        case SubghzCustomEventViewReadRAWREC:
 
+        case SubghzCustomEventViewReadRAWREC:
             if(subghz->txrx->rx_key_state != SubGhzRxKeyStateIDLE) {
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
             } else {
@@ -199,42 +246,17 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) {
                         subghz_rx(subghz, subghz->txrx->frequency);
                     }
                     subghz->state_notifications = SubGhzNotificationStateRX;
+                    subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
                 } else {
-                    string_set(subghz->error_str, "No SD card");
+                    string_set(subghz->error_str, "Function requires\nan SD card.");
                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
                 }
             }
-
             return true;
             break;
-        case SubghzCustomEventViewReadRAWSave:
-            if(strcmp(
-                   subghz_protocol_raw_get_last_file_name(
-                       (SubGhzProtocolRAW*)subghz->txrx->protocol_result),
-                   "")) {
-                string_t temp_str;
-                string_init_printf(
-                    temp_str,
-                    "%s",
-                    subghz_protocol_raw_get_last_file_name(
-                        (SubGhzProtocolRAW*)subghz->txrx->protocol_result));
-                path_extract_filename_no_ext(string_get_cstr(temp_str), temp_str);
-                strlcpy(
-                    subghz->file_name,
-                    string_get_cstr(temp_str),
-                    strlen(string_get_cstr(temp_str)) + 1);
-
-                //set the path to read the file
-                string_printf(
-                    temp_str,
-                    "%s/%s%s",
-                    SUBGHZ_APP_PATH_FOLDER,
-                    subghz->file_name,
-                    SUBGHZ_APP_EXTENSION);
-                subghz_protocol_raw_set_last_file_name(
-                    (SubGhzProtocolRAW*)subghz->txrx->protocol_result, string_get_cstr(temp_str));
-                string_clear(temp_str);
 
+        case SubghzCustomEventViewReadRAWSave:
+            if(subghz_scene_read_raw_update_filename(subghz)) {
                 scene_manager_set_scene_state(
                     subghz->scene_manager, SubGhzSceneReadRAW, SubghzCustomEventManagerSet);
                 subghz->txrx->rx_key_state = SubGhzRxKeyStateBack;

+ 15 - 3
applications/subghz/scenes/subghz_scene_receiver.c

@@ -56,6 +56,7 @@ void subghz_scene_add_to_history_callback(SubGhzProtocolCommon* parser, void* co
         subghz_scene_receiver_update_statusbar(subghz);
     }
     string_clear(str_buff);
+    subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
 }
 
 void subghz_scene_receiver_on_enter(void* context) {
@@ -64,6 +65,10 @@ void subghz_scene_receiver_on_enter(void* context) {
     string_t str_buff;
     string_init(str_buff);
 
+    if(subghz->txrx->rx_key_state == SubGhzRxKeyStateIDLE) {
+        subghz_history_clean(subghz->txrx->history);
+    }
+
     //Load history to receiver
     subghz_receiver_exit(subghz->subghz_receiver);
     for(uint8_t i = 0; i < subghz_history_get_item(subghz->txrx->history); i++) {
@@ -73,6 +78,7 @@ void subghz_scene_receiver_on_enter(void* context) {
             subghz->subghz_receiver,
             string_get_cstr(str_buff),
             subghz_history_get_type_protocol(subghz->txrx->history, i));
+        subghz->txrx->rx_key_state = SubGhzRxKeyStateAddKey;
     }
     string_clear(str_buff);
     subghz_scene_receiver_update_statusbar(subghz);
@@ -99,20 +105,26 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) {
     if(event.type == SceneManagerEventTypeCustom) {
         switch(event.event) {
         case SubghzCustomEventViewReceverBack:
+
             // Stop CC1101 Rx
             subghz->state_notifications = SubGhzNotificationStateIDLE;
             if(subghz->txrx->txrx_state == SubGhzTxRxStateRx) {
                 subghz_rx_end(subghz);
                 subghz_sleep(subghz);
             };
-            subghz_history_clean(subghz->txrx->history);
             subghz->txrx->hopper_state = SubGhzHopperStateOFF;
             subghz->txrx->frequency = subghz_frequencies[subghz_frequencies_433_92];
             subghz->txrx->preset = FuriHalSubGhzPresetOok650Async;
             subghz->txrx->idx_menu_chosen = 0;
             subghz_parser_enable_dump(subghz->txrx->parser, NULL, subghz);
-            scene_manager_search_and_switch_to_previous_scene(
-                subghz->scene_manager, SubGhzSceneStart);
+
+            if(subghz->txrx->rx_key_state == SubGhzRxKeyStateAddKey) {
+                subghz->txrx->rx_key_state = SubGhzRxKeyStateExit;
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneNeedSaving);
+            } else {
+                scene_manager_search_and_switch_to_previous_scene(
+                    subghz->scene_manager, SubGhzSceneStart);
+            }
             return true;
             break;
         case SubghzCustomEventViewReceverOK:

+ 10 - 5
applications/subghz/scenes/subghz_scene_save_name.c

@@ -20,7 +20,7 @@ void subghz_scene_save_name_on_enter(void* context) {
         set_random_name(subghz->file_name, sizeof(subghz->file_name));
 
     } else {
-        memcpy(subghz->file_name_tmp, subghz->file_name, strlen(subghz->file_name) + 1);
+        strcpy(subghz->file_name_tmp, subghz->file_name);
         if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) ==
            SubghzCustomEventManagerSet) {
             subghz_get_next_name_file(subghz);
@@ -42,8 +42,11 @@ void subghz_scene_save_name_on_enter(void* context) {
 
 bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
-
-    if(event.type == SceneManagerEventTypeCustom) {
+    if(event.type == SceneManagerEventTypeBack) {
+        strcpy(subghz->file_name, subghz->file_name_tmp);
+        scene_manager_previous_scene(subghz->scene_manager);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubghzCustomEventSceneSaveName) {
             if(strcmp(subghz->file_name, "")) {
                 if(strcmp(subghz->file_name_tmp, "")) {
@@ -56,13 +59,15 @@ bool subghz_scene_save_name_on_event(void* context, SceneManagerEvent event) {
                    SubghzCustomEventManagerSet) {
                     subghz_protocol_raw_set_last_file_name(
                         (SubGhzProtocolRAW*)subghz->txrx->protocol_result, subghz->file_name);
+                } else {
+                    subghz_file_name_clear(subghz);
                 }
-                subghz_file_name_clear(subghz);
+
                 scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaveSuccess);
                 return true;
             } else {
                 string_set(subghz->error_str, "No name file");
-                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
                 return true;
             }
         }

+ 5 - 2
applications/subghz/scenes/subghz_scene_save_success.c

@@ -26,10 +26,13 @@ bool subghz_scene_save_success_on_event(void* context, SceneManagerEvent event)
         if(event.event == SubghzCustomEventSceneSaveSuccess) {
             if(!scene_manager_search_and_switch_to_previous_scene(
                    subghz->scene_manager, SubGhzSceneReceiver)) {
+                subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWSave;
                 if(!scene_manager_search_and_switch_to_previous_scene(
                        subghz->scene_manager, SubGhzSceneReadRAW)) {
-                    scene_manager_search_and_switch_to_previous_scene(
-                        subghz->scene_manager, SubGhzSceneStart);
+                    if(!scene_manager_search_and_switch_to_previous_scene(
+                           subghz->scene_manager, SubGhzSceneSaved)) {
+                        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved);
+                    }
                 }
             }
             return true;

+ 3 - 2
applications/subghz/scenes/subghz_scene_set_type.c

@@ -19,7 +19,7 @@ bool subghz_scene_set_type_submenu_to_find_protocol(void* context, const char* p
     subghz->txrx->protocol_result = subghz_parser_get_by_name(subghz->txrx->parser, protocol_name);
     if(subghz->txrx->protocol_result == NULL) {
         string_set(subghz->error_str, "Protocol not found");
-        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
         return false;
     }
     return true;
@@ -177,7 +177,8 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) {
                     generated_protocol = true;
                 } else {
                     generated_protocol = false;
-                    string_set(subghz->error_str, "No manufactory key");
+                    string_set(
+                        subghz->error_str, "Function requires\nan SD card with\nfresh databases.");
                     scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
                 }
             }

+ 59 - 23
applications/subghz/scenes/subghz_scene_show_error.c

@@ -1,31 +1,74 @@
 #include "../subghz_i.h"
 #include "../helpers/subghz_custom_event.h"
 
-void subghz_scene_show_error_popup_callback(void* context) {
+void subghz_scene_show_error_callback(GuiButtonType result, InputType type, void* context) {
+    furi_assert(context);
     SubGhz* subghz = context;
-    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowError);
+
+    if((result == GuiButtonTypeRight) && (type == InputTypeShort)) {
+        view_dispatcher_send_custom_event(
+            subghz->view_dispatcher, SubghzCustomEventSceneShowErrorOk);
+    } else if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) {
+        view_dispatcher_send_custom_event(
+            subghz->view_dispatcher, SubghzCustomEventSceneShowErrorBack);
+    }
 }
 
 void subghz_scene_show_error_on_enter(void* context) {
     SubGhz* subghz = context;
 
-    // Setup view
-    Popup* popup = subghz->popup;
-    popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51);
-    popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom);
-    popup_set_timeout(popup, 1500);
-    popup_set_context(popup, subghz);
-    popup_set_callback(popup, subghz_scene_show_error_popup_callback);
-    popup_enable_timeout(popup);
-    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup);
+    widget_add_icon_element(subghz->widget, 0, 0, &I_SDQuestion_35x43);
+
+    widget_add_string_multiline_element(
+        subghz->widget,
+        81,
+        24,
+        AlignCenter,
+        AlignCenter,
+        FontSecondary,
+        string_get_cstr(subghz->error_str));
+    if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) ==
+       SubghzCustomEventManagerSet) {
+        widget_add_button_element(
+            subghz->widget, GuiButtonTypeRight, "Ok", subghz_scene_show_error_callback, subghz);
+    }
+
+    widget_add_button_element(
+        subghz->widget, GuiButtonTypeLeft, "Back", subghz_scene_show_error_callback, subghz);
+
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewWidget);
 }
 
 bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
-    if(event.type == SceneManagerEventTypeCustom) {
-        if(event.event == SubghzCustomEventSceneShowError) {
+    if(event.type == SceneManagerEventTypeBack) {
+        if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) ==
+           SubghzCustomEventManagerSet) {
+            return false;
+        } else {
             scene_manager_search_and_switch_to_previous_scene(
                 subghz->scene_manager, SubGhzSceneStart);
+        }
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubghzCustomEventSceneShowErrorOk) {
+            if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) ==
+               SubghzCustomEventManagerSet) {
+                scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
+            }
+            return true;
+        } else if(event.event == SubghzCustomEventSceneShowErrorBack) {
+            if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowError) ==
+               SubghzCustomEventManagerSet) {
+                //exit app
+                if(!scene_manager_previous_scene(subghz->scene_manager)) {
+                    scene_manager_stop(subghz->scene_manager);
+                    view_dispatcher_stop(subghz->view_dispatcher);
+                }
+            } else {
+                scene_manager_search_and_switch_to_previous_scene(
+                    subghz->scene_manager, SubGhzSceneStart);
+            }
             return true;
         }
     }
@@ -34,15 +77,8 @@ bool subghz_scene_show_error_on_event(void* context, SceneManagerEvent event) {
 
 void subghz_scene_show_error_on_exit(void* context) {
     SubGhz* subghz = context;
-
-    // Clear view
-    Popup* popup = subghz->popup;
-    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
-    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
-    popup_set_icon(popup, 0, 0, NULL);
-    popup_set_callback(popup, NULL);
-    popup_set_context(popup, NULL);
-    popup_set_timeout(popup, 0);
-    popup_disable_timeout(popup);
+    scene_manager_set_scene_state(
+        subghz->scene_manager, SubGhzSceneShowError, SubghzCustomEventManagerNoSet);
+    widget_clear(subghz->widget);
     string_reset(subghz->error_str);
 }

+ 48 - 0
applications/subghz/scenes/subghz_scene_show_error_sub.c

@@ -0,0 +1,48 @@
+#include "../subghz_i.h"
+#include "../helpers/subghz_custom_event.h"
+
+void subghz_scene_show_error_sub_popup_callback(void* context) {
+    SubGhz* subghz = context;
+    view_dispatcher_send_custom_event(subghz->view_dispatcher, SubghzCustomEventSceneShowErrorSub);
+}
+
+void subghz_scene_show_error_sub_on_enter(void* context) {
+    SubGhz* subghz = context;
+
+    // Setup view
+    Popup* popup = subghz->popup;
+    popup_set_icon(popup, 32, 12, &I_DolphinFirstStart7_61x51);
+    popup_set_header(popup, string_get_cstr(subghz->error_str), 64, 8, AlignCenter, AlignBottom);
+    popup_set_timeout(popup, 1500);
+    popup_set_context(popup, subghz);
+    popup_set_callback(popup, subghz_scene_show_error_sub_popup_callback);
+    popup_enable_timeout(popup);
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewPopup);
+}
+
+bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event) {
+    SubGhz* subghz = context;
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubghzCustomEventSceneShowErrorSub) {
+            scene_manager_search_and_switch_to_previous_scene(
+                subghz->scene_manager, SubGhzSceneStart);
+            return true;
+        }
+    }
+    return false;
+}
+
+void subghz_scene_show_error_sub_on_exit(void* context) {
+    SubGhz* subghz = context;
+
+    // Clear view
+    Popup* popup = subghz->popup;
+    popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom);
+    popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop);
+    popup_set_icon(popup, 0, 0, NULL);
+    popup_set_callback(popup, NULL);
+    popup_set_context(popup, NULL);
+    popup_set_timeout(popup, 0);
+    popup_disable_timeout(popup);
+    string_reset(subghz->error_str);
+}

+ 6 - 2
applications/subghz/scenes/subghz_scene_start.c

@@ -53,8 +53,12 @@ void subghz_scene_start_on_enter(void* context) {
 
 bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) {
     SubGhz* subghz = context;
-
-    if(event.type == SceneManagerEventTypeCustom) {
+    if(event.type == SceneManagerEventTypeBack) {
+        //exit app
+        scene_manager_stop(subghz->scene_manager);
+        view_dispatcher_stop(subghz->view_dispatcher);
+        return true;
+    } else if(event.type == SceneManagerEventTypeCustom) {
         if(event.event == SubmenuIndexReadRAW) {
             scene_manager_set_scene_state(
                 subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW);

+ 1 - 1
applications/subghz/scenes/subghz_scene_transmitter.c

@@ -94,7 +94,7 @@ bool subghz_scene_transmitter_on_event(void* context, SceneManagerEvent event) {
             return true;
         } else if(event.event == SubghzCustomEventViewTransmitterError) {
             string_set(subghz->error_str, "Protocol not found");
-            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub);
         }
     } else if(event.type == SceneManagerEventTypeTick) {
         if(subghz->state_notifications == SubGhzNotificationStateTX) {

+ 31 - 11
applications/subghz/subghz.c

@@ -1,3 +1,8 @@
+/*
+*    Give up hope, everyone who enters here!!!
+*      Оставь надежду, всяк сюда входящий!!!
+*/
+
 #include "subghz_i.h"
 #include <lib/toolbox/path.h>
 
@@ -191,13 +196,6 @@ SubGhz* subghz_alloc() {
     //Init Error_str
     string_init(subghz->error_str);
 
-    subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes");
-    subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes_user");
-    subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_flor_s_rx");
-    subghz_parser_load_came_atomo_file(subghz->txrx->parser, "/ext/subghz/came_atomo");
-
-    //subghz_parser_enable_dump_text(subghz->protocol, subghz_text_callback, subghz);
-
     return subghz;
 }
 
@@ -285,19 +283,41 @@ void subghz_free(SubGhz* subghz) {
 int32_t subghz_app(void* p) {
     SubGhz* subghz = subghz_alloc();
 
+    //Load database
+    bool load_database =
+        subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes");
+    subghz_parser_load_keeloq_file(subghz->txrx->parser, "/ext/subghz/keeloq_mfcodes_user");
+    subghz_parser_load_nice_flor_s_file(subghz->txrx->parser, "/ext/subghz/nice_flor_s_rx");
+    subghz_parser_load_came_atomo_file(subghz->txrx->parser, "/ext/subghz/came_atomo");
+
     // Check argument and run corresponding scene
     if(p && subghz_key_load(subghz, p)) {
         string_t filename;
         string_init(filename);
 
         path_extract_filename_no_ext(p, filename);
-        strlcpy(
-            subghz->file_name, string_get_cstr(filename), strlen(string_get_cstr(filename)) + 1);
+        strcpy(subghz->file_name, string_get_cstr(filename));
         string_clear(filename);
 
-        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
+        if((!strcmp(subghz->txrx->protocol_result->name, "RAW"))) {
+            //Load Raw TX
+            subghz->txrx->rx_key_state = SubGhzRxKeyStateRAWLoad;
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW);
+        } else {
+            //Load transmitter TX
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneTransmitter);
+        }
     } else {
-        scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
+        if(load_database) {
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneStart);
+        } else {
+            scene_manager_set_scene_state(
+                subghz->scene_manager, SubGhzSceneShowError, SubghzCustomEventManagerSet);
+            string_set(
+                subghz->error_str,
+                "No SD card or\ndatabase found.\nSome app function\nmay be reduced.");
+            scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowError);
+        }
     }
 
     furi_hal_power_suppress_charge_enter();

+ 1 - 1
applications/subghz/subghz_i.c

@@ -286,7 +286,7 @@ bool subghz_get_next_name_file(SubGhz* subghz) {
         storage_get_next_filename(
             storage, SUBGHZ_RAW_PATH_FOLDER, subghz->file_name, SUBGHZ_APP_EXTENSION, temp_str);
 
-        memcpy(subghz->file_name, string_get_cstr(temp_str), strlen(string_get_cstr(temp_str)));
+        strcpy(subghz->file_name, string_get_cstr(temp_str));
         res = true;
     }
 

+ 1 - 0
applications/subghz/subghz_i.h

@@ -74,6 +74,7 @@ typedef enum {
     SubGhzRxKeyStateAddKey,
     SubGhzRxKeyStateExit,
     SubGhzRxKeyStateRAWLoad,
+    SubGhzRxKeyStateRAWSave,
 } SubGhzRxKeyState;
 
 struct SubGhzTxRx {

+ 126 - 33
applications/subghz/views/subghz_read_raw.c

@@ -10,6 +10,7 @@
 
 #include <assets_icons.h>
 #define SUBGHZ_READ_RAW_RSSI_HISTORY_SIZE 100
+#define TAG "SubghzReadRAW"
 
 struct SubghzReadRAW {
     View* view;
@@ -89,11 +90,22 @@ void subghz_read_raw_stop_send(SubghzReadRAW* instance) {
 
     with_view_model(
         instance->view, (SubghzReadRAWModel * model) {
-            if(model->satus == SubghzReadRAWStatusTXRepeat) {
-                // Start TX
+            switch(model->satus) {
+            case SubghzReadRAWStatusTXRepeat:
+            case SubghzReadRAWStatusLoadKeyTXRepeat:
                 instance->callback(SubghzCustomEventViewReadRAWSendStart, instance->context);
-            } else {
+                break;
+            case SubghzReadRAWStatusTX:
                 model->satus = SubghzReadRAWStatusIDLE;
+                break;
+            case SubghzReadRAWStatusLoadKeyTX:
+                model->satus = SubghzReadRAWStatusLoadKeyIDLE;
+                break;
+
+            default:
+                FURI_LOG_W(TAG, "unknown status");
+                model->satus = SubghzReadRAWStatusIDLE;
+                break;
             }
             return true;
         });
@@ -203,6 +215,7 @@ void subghz_read_raw_draw_rssi(Canvas* canvas, SubghzReadRAWModel* model) {
 }
 
 void subghz_read_raw_draw(Canvas* canvas, SubghzReadRAWModel* model) {
+    uint8_t graphics_mode = 1;
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontSecondary);
     canvas_draw_str(canvas, 5, 8, string_get_cstr(model->frequency_str));
@@ -214,32 +227,47 @@ void subghz_read_raw_draw(Canvas* canvas, SubghzReadRAWModel* model) {
     canvas_draw_line(canvas, 0, 48, 115, 48);
     canvas_draw_line(canvas, 115, 14, 115, 48);
 
-    if((model->satus == SubghzReadRAWStatusTX) || (model->satus == SubghzReadRAWStatusTXRepeat)) {
-        subghz_read_raw_draw_sin(canvas, model);
-    } else {
-        subghz_read_raw_draw_rssi(canvas, model);
-        subghz_read_raw_draw_scale(canvas, model);
-    }
-
-    if(model->satus == SubghzReadRAWStatusIDLE) {
+    switch(model->satus) {
+    case SubghzReadRAWStatusIDLE:
         elements_button_left(canvas, "Erase");
         elements_button_center(canvas, "Send");
         elements_button_right(canvas, "Save");
+        break;
+    case SubghzReadRAWStatusLoadKeyIDLE:
+        elements_button_left(canvas, "New");
+        elements_button_center(canvas, "Send");
+        elements_button_right(canvas, "More");
         canvas_draw_str_aligned(
             canvas, 58, 28, AlignCenter, AlignTop, string_get_cstr(model->file_name));
-    } else if(model->satus == SubghzReadRAWStatusStart) {
+        break;
+
+    case SubghzReadRAWStatusTX:
+    case SubghzReadRAWStatusTXRepeat:
+    case SubghzReadRAWStatusLoadKeyTX:
+    case SubghzReadRAWStatusLoadKeyTXRepeat:
+        graphics_mode = 0;
+        elements_button_center(canvas, "Send");
+        break;
+
+    case SubghzReadRAWStatusStart:
         elements_button_left(canvas, "Config");
         elements_button_center(canvas, "REC");
-    } else if(
-        (model->satus == SubghzReadRAWStatusTX) || (model->satus == SubghzReadRAWStatusTXRepeat)) {
-        elements_button_center(canvas, "Send");
-    } else {
+        break;
+
+    default:
         elements_button_center(canvas, "Stop");
+        break;
     }
 
-    canvas_set_font_direction(canvas, 3);
-    canvas_draw_str(canvas, 126, 40, "RSSI");
-    canvas_set_font_direction(canvas, 0);
+    if(graphics_mode == 0) {
+        subghz_read_raw_draw_sin(canvas, model);
+    } else {
+        subghz_read_raw_draw_rssi(canvas, model);
+        subghz_read_raw_draw_scale(canvas, model);
+        canvas_set_font_direction(canvas, CanvasDirectionBottomToTop);
+        canvas_draw_str(canvas, 126, 40, "RSSI");
+        canvas_set_font_direction(canvas, CanvasDirectionLeftToRight);
+    }
 }
 
 bool subghz_read_raw_input(InputEvent* event, void* context) {
@@ -255,14 +283,32 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
                 uint8_t ret = false;
-                if(model->satus == SubghzReadRAWStatusIDLE) {
+                switch(model->satus) {
+                case SubghzReadRAWStatusIDLE:
                     // Start TX
                     instance->callback(SubghzCustomEventViewReadRAWSendStart, instance->context);
                     instance->callback(SubghzCustomEventViewReadRAWVibro, instance->context);
                     model->satus = SubghzReadRAWStatusTXRepeat;
                     ret = true;
-                } else if(model->satus == SubghzReadRAWStatusTX) {
+                    break;
+                case SubghzReadRAWStatusTX:
+                    // Start TXRepeat
                     model->satus = SubghzReadRAWStatusTXRepeat;
+                    break;
+                case SubghzReadRAWStatusLoadKeyIDLE:
+                    // Start Load Key TX
+                    instance->callback(SubghzCustomEventViewReadRAWSendStart, instance->context);
+                    instance->callback(SubghzCustomEventViewReadRAWVibro, instance->context);
+                    model->satus = SubghzReadRAWStatusLoadKeyTXRepeat;
+                    ret = true;
+                    break;
+                case SubghzReadRAWStatusLoadKeyTX:
+                    // Start Load Key TXRepeat
+                    model->satus = SubghzReadRAWStatusLoadKeyTXRepeat;
+                    break;
+
+                default:
+                    break;
                 }
                 return ret;
             });
@@ -272,19 +318,40 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
                 if(model->satus == SubghzReadRAWStatusTXRepeat) {
                     // Stop repeat TX
                     model->satus = SubghzReadRAWStatusTX;
+                } else if(model->satus == SubghzReadRAWStatusLoadKeyTXRepeat) {
+                    // Stop repeat TX
+                    model->satus = SubghzReadRAWStatusLoadKeyTX;
                 }
                 return false;
             });
     } else if(event->key == InputKeyBack && event->type == InputTypeShort) {
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
-                if(model->satus == SubghzReadRAWStatusREC) {
+                switch(model->satus) {
+                case SubghzReadRAWStatusREC:
                     //Stop REC
                     instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                     model->satus = SubghzReadRAWStatusIDLE;
-                } else {
+                    break;
+                case SubghzReadRAWStatusLoadKeyTX:
+                    //Stop TxRx
+                    instance->callback(SubghzCustomEventViewReadRAWTXRXStop, instance->context);
+                    model->satus = SubghzReadRAWStatusLoadKeyIDLE;
+                    break;
+                case SubghzReadRAWStatusTX:
+                    //Stop TxRx
+                    instance->callback(SubghzCustomEventViewReadRAWTXRXStop, instance->context);
+                    model->satus = SubghzReadRAWStatusIDLE;
+                    break;
+                case SubghzReadRAWStatusLoadKeyIDLE:
                     //Exit
                     instance->callback(SubghzCustomEventViewReadRAWBack, instance->context);
+                    break;
+
+                default:
+                    //Exit
+                    instance->callback(SubghzCustomEventViewReadRAWBack, instance->context);
+                    break;
                 }
                 return true;
             });
@@ -294,7 +361,9 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
                 if(model->satus == SubghzReadRAWStatusStart) {
                     //Config
                     instance->callback(SubghzCustomEventViewReadRAWConfig, instance->context);
-                } else if(model->satus == SubghzReadRAWStatusIDLE) {
+                } else if(
+                    (model->satus == SubghzReadRAWStatusIDLE) ||
+                    (model->satus == SubghzReadRAWStatusLoadKeyIDLE)) {
                     //Erase
                     model->satus = SubghzReadRAWStatusStart;
                     model->rssi_history_end = false;
@@ -308,9 +377,12 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
     } else if(event->key == InputKeyRight && event->type == InputTypeShort) {
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
-                //Save
                 if(model->satus == SubghzReadRAWStatusIDLE) {
+                    //Save
                     instance->callback(SubghzCustomEventViewReadRAWSave, instance->context);
+                } else if(model->satus == SubghzReadRAWStatusLoadKeyIDLE) {
+                    //More
+                    instance->callback(SubghzCustomEventViewReadRAWMore, instance->context);
                 }
                 return true;
             });
@@ -323,9 +395,7 @@ bool subghz_read_raw_input(InputEvent* event, void* context) {
                     model->satus = SubghzReadRAWStatusREC;
                     model->ind_write = 0;
                     model->rssi_history_end = false;
-                } else if(
-                    (model->satus != SubghzReadRAWStatusTX) &&
-                    (model->satus != SubghzReadRAWStatusTXRepeat)) {
+                } else if(model->satus == SubghzReadRAWStatusREC) {
                     //Stop
                     instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                     model->satus = SubghzReadRAWStatusIDLE;
@@ -341,7 +411,9 @@ void subghz_read_raw_set_status(
     SubghzReadRAWStatus satus,
     const char* file_name) {
     furi_assert(instance);
-    if(satus == SubghzReadRAWStatusStart) {
+
+    switch(satus) {
+    case SubghzReadRAWStatusStart:
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
                 model->satus = SubghzReadRAWStatusStart;
@@ -351,22 +423,42 @@ void subghz_read_raw_set_status(
                 string_set(model->sample_write, "0 spl.");
                 return true;
             });
-    } else if(satus == SubghzReadRAWStatusIDLE) {
+        break;
+    case SubghzReadRAWStatusIDLE:
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
                 model->satus = SubghzReadRAWStatusIDLE;
                 return true;
             });
-    } else if(satus == SubghzReadRAWStatusTX) {
+        break;
+    case SubghzReadRAWStatusLoadKeyTX:
         with_view_model(
             instance->view, (SubghzReadRAWModel * model) {
-                model->satus = SubghzReadRAWStatusIDLE;
+                model->satus = SubghzReadRAWStatusLoadKeyIDLE;
                 model->rssi_history_end = false;
                 model->ind_write = 0;
                 string_set(model->file_name, file_name);
                 string_set(model->sample_write, "RAW");
                 return true;
             });
+        break;
+    case SubghzReadRAWStatusSaveKey:
+        with_view_model(
+            instance->view, (SubghzReadRAWModel * model) {
+                model->satus = SubghzReadRAWStatusLoadKeyIDLE;
+                if(!model->ind_write) {
+                    string_set(model->file_name, file_name);
+                    string_set(model->sample_write, "RAW");
+                } else {
+                    string_reset(model->file_name);
+                }
+                return true;
+            });
+        break;
+
+    default:
+        FURI_LOG_W(TAG, "unknown status");
+        break;
     }
 }
 
@@ -382,7 +474,8 @@ void subghz_read_raw_exit(void* context) {
     with_view_model(
         instance->view, (SubghzReadRAWModel * model) {
             if(model->satus != SubghzReadRAWStatusIDLE &&
-               model->satus != SubghzReadRAWStatusStart) {
+               model->satus != SubghzReadRAWStatusStart &&
+               model->satus != SubghzReadRAWStatusLoadKeyIDLE) {
                 instance->callback(SubghzCustomEventViewReadRAWIDLE, instance->context);
                 model->satus = SubghzReadRAWStatusStart;
             }

+ 5 - 0
applications/subghz/views/subghz_read_raw.h

@@ -13,6 +13,11 @@ typedef enum {
     SubghzReadRAWStatusREC,
     SubghzReadRAWStatusTX,
     SubghzReadRAWStatusTXRepeat,
+
+    SubghzReadRAWStatusLoadKeyIDLE,
+    SubghzReadRAWStatusLoadKeyTX,
+    SubghzReadRAWStatusLoadKeyTXRepeat,
+    SubghzReadRAWStatusSaveKey,
 } SubghzReadRAWStatus;
 
 void subghz_read_raw_set_callback(

+ 4 - 1
lib/subghz/subghz_parser.c

@@ -215,12 +215,15 @@ void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file
         (SubGhzProtocolCameAtomo*)instance->protocols[SubGhzProtocolTypeCameAtomo], file_name);
 }
 
-void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name) {
+bool subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name) {
+    bool ret = false;
     if (subghz_keystore_load(instance->keystore, file_name)) {
         FURI_LOG_I(SUBGHZ_PARSER_TAG, "Successfully loaded keeloq keys from %s", file_name);
+        ret = true;
     } else {
         FURI_LOG_W(SUBGHZ_PARSER_TAG, "Failed to load keeloq keysfrom %s", file_name);
     }
+    return ret;
 }
 
 void subghz_parser_reset(SubGhzParser* instance) {

+ 2 - 1
lib/subghz/subghz_parser.h

@@ -67,8 +67,9 @@ void subghz_parser_load_came_atomo_file(SubGhzParser* instance, const char* file
  * 
  * @param instance - SubGhzParser instance
  * @param file_name - "path/file_name"
+ * @return bool 
  */
-void subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name);
+bool subghz_parser_load_keeloq_file(SubGhzParser* instance, const char* file_name);
 
 /** Restarting all parsers
  *