[FL-2705] App RPC Bug Fixes and redesign (#1491)
* Rpc: remove callback timer * Rpc: simplify rpc event callback * Rpc: migrate to new confirmation schema * Rpc: migrate to new confirmation schema part2: finalize ibutton and rfid * Rpc: migrate to new confirmation schema part3: finallize nfc and fix id in load * Rpc: hardened sequencing check * Rpc: migrate to new confirmation schema part4: finalize subghz * iButton: properly handle exit * Nfc: correct sequence for rpc exit send * Rpc: fix review issues and nfc exit message * Rpc: more logging and condition race fix in confirmation * Rpc: migrate to new confirmation schema part5: finalize infrared * Rpc: more logging
This commit is contained in:
		
							parent
							
								
									f9386b2649
								
							
						
					
					
						commit
						f9745b4141
					
				| @ -5,7 +5,7 @@ | ||||
| #include "m-string.h" | ||||
| #include <toolbox/path.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| #include "rpc/rpc_app.h" | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| #define TAG "iButtonApp" | ||||
| 
 | ||||
| @ -58,7 +58,7 @@ static void ibutton_make_app_folder(iButton* ibutton) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { | ||||
| bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog) { | ||||
|     FlipperFormat* file = flipper_format_file_alloc(ibutton->storage); | ||||
|     bool result = false; | ||||
|     string_t data; | ||||
| @ -99,33 +99,20 @@ static bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static bool ibutton_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||
| static void ibutton_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     iButton* ibutton = context; | ||||
| 
 | ||||
|     bool result = false; | ||||
| 
 | ||||
|     if(event == RpcAppEventSessionClose) { | ||||
|         rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); | ||||
|         ibutton->rpc_ctx = NULL; | ||||
|         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); | ||||
|         result = true; | ||||
|         view_dispatcher_send_custom_event( | ||||
|             ibutton->view_dispatcher, iButtonCustomEventRpcSessionClose); | ||||
|     } else if(event == RpcAppEventAppExit) { | ||||
|         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcExit); | ||||
|         result = true; | ||||
|     } else if(event == RpcAppEventLoadFile) { | ||||
|         if(arg) { | ||||
|             string_set_str(ibutton->file_path, arg); | ||||
|             if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { | ||||
|                 ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); | ||||
|                 view_dispatcher_send_custom_event( | ||||
|                     ibutton->view_dispatcher, iButtonCustomEventRpcLoad); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|         view_dispatcher_send_custom_event(ibutton->view_dispatcher, iButtonCustomEventRpcLoad); | ||||
|     } else { | ||||
|         rpc_system_app_confirm(ibutton->rpc_ctx, event, false); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| bool ibutton_custom_event_callback(void* context, uint32_t event) { | ||||
|  | ||||
| @ -12,4 +12,5 @@ enum iButtonCustomEvent { | ||||
| 
 | ||||
|     iButtonCustomEventRpcLoad, | ||||
|     iButtonCustomEventRpcExit, | ||||
|     iButtonCustomEventRpcSessionClose, | ||||
| }; | ||||
|  | ||||
| @ -78,6 +78,7 @@ typedef enum { | ||||
| } iButtonNotificationMessage; | ||||
| 
 | ||||
| bool ibutton_file_select(iButton* ibutton); | ||||
| bool ibutton_load_key_data(iButton* ibutton, string_t key_path, bool show_dialog); | ||||
| bool ibutton_save_key(iButton* ibutton, const char* key_name); | ||||
| bool ibutton_delete_key(iButton* ibutton); | ||||
| void ibutton_text_store_set(iButton* ibutton, const char* text, ...); | ||||
|  | ||||
| @ -1,5 +1,6 @@ | ||||
| #include "../ibutton_i.h" | ||||
| #include <toolbox/path.h> | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| void ibutton_scene_rpc_on_enter(void* context) { | ||||
|     iButton* ibutton = context; | ||||
| @ -26,23 +27,40 @@ bool ibutton_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         consumed = true; | ||||
|         if(event.event == iButtonCustomEventRpcLoad) { | ||||
|             string_t key_name; | ||||
|             string_init(key_name); | ||||
|             if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { | ||||
|                 path_extract_filename(ibutton->file_path, key_name, true); | ||||
|             const char* arg = rpc_system_app_get_data(ibutton->rpc_ctx); | ||||
|             bool result = false; | ||||
|             if(arg) { | ||||
|                 string_set_str(ibutton->file_path, arg); | ||||
|                 if(ibutton_load_key_data(ibutton, ibutton->file_path, false)) { | ||||
|                     ibutton_worker_emulate_start(ibutton->key_worker, ibutton->key); | ||||
|                     string_t key_name; | ||||
|                     string_init(key_name); | ||||
|                     if(string_end_with_str_p(ibutton->file_path, IBUTTON_APP_EXTENSION)) { | ||||
|                         path_extract_filename(ibutton->file_path, key_name, true); | ||||
|                     } | ||||
| 
 | ||||
|                     if(!string_empty_p(key_name)) { | ||||
|                         ibutton_text_store_set( | ||||
|                             ibutton, "emulating\n%s", string_get_cstr(key_name)); | ||||
|                     } else { | ||||
|                         ibutton_text_store_set(ibutton, "emulating"); | ||||
|                     } | ||||
|                     popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); | ||||
| 
 | ||||
|                     ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); | ||||
| 
 | ||||
|                     string_clear(key_name); | ||||
|                     result = true; | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if(!string_empty_p(key_name)) { | ||||
|                 ibutton_text_store_set(ibutton, "emulating\n%s", string_get_cstr(key_name)); | ||||
|             } else { | ||||
|                 ibutton_text_store_set(ibutton, "emulating"); | ||||
|             } | ||||
|             popup_set_text(popup, ibutton->text_store, 82, 32, AlignCenter, AlignTop); | ||||
| 
 | ||||
|             ibutton_notification_message(ibutton, iButtonNotificationMessageEmulateStart); | ||||
| 
 | ||||
|             string_clear(key_name); | ||||
|             rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventLoadFile, result); | ||||
|         } else if(event.event == iButtonCustomEventRpcExit) { | ||||
|             rpc_system_app_confirm(ibutton->rpc_ctx, RpcAppEventAppExit, true); | ||||
|             ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); | ||||
|             view_dispatcher_stop(ibutton->view_dispatcher); | ||||
|         } else if(event.event == iButtonCustomEventRpcSessionClose) { | ||||
|             rpc_system_app_set_callback(ibutton->rpc_ctx, NULL, NULL); | ||||
|             ibutton->rpc_ctx = NULL; | ||||
|             ibutton_notification_message(ibutton, iButtonNotificationMessageBlinkStop); | ||||
|             view_dispatcher_stop(ibutton->view_dispatcher); | ||||
|         } | ||||
|  | ||||
| @ -36,52 +36,29 @@ static void infrared_tick_event_callback(void* context) { | ||||
|     scene_manager_handle_tick_event(infrared->scene_manager); | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|     infrared_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||
| static void infrared_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     Infrared* infrared = context; | ||||
| 
 | ||||
|     if(!infrared->rpc_ctx) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool result = false; | ||||
|     furi_assert(infrared->rpc_ctx); | ||||
| 
 | ||||
|     if(event == RpcAppEventSessionClose) { | ||||
|         rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); | ||||
|         infrared->rpc_ctx = NULL; | ||||
|         view_dispatcher_send_custom_event( | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); | ||||
|         result = true; | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeRpcSessionClose); | ||||
|     } else if(event == RpcAppEventAppExit) { | ||||
|         view_dispatcher_send_custom_event( | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeBackPressed); | ||||
|         result = true; | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeRpcExit); | ||||
|     } else if(event == RpcAppEventLoadFile) { | ||||
|         if(arg) { | ||||
|             string_set_str(infrared->file_path, arg); | ||||
|             result = infrared_remote_load(infrared->remote, infrared->file_path); | ||||
|             infrared_worker_tx_set_get_signal_callback( | ||||
|                 infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); | ||||
|             infrared_worker_tx_set_signal_sent_callback( | ||||
|                 infrared->worker, infrared_signal_sent_callback, infrared); | ||||
|             view_dispatcher_send_custom_event( | ||||
|                 infrared->view_dispatcher, InfraredCustomEventTypeRpcLoaded); | ||||
|         } | ||||
|         view_dispatcher_send_custom_event( | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeRpcLoad); | ||||
|     } else if(event == RpcAppEventButtonPress) { | ||||
|         if(arg) { | ||||
|             size_t button_index = 0; | ||||
|             if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { | ||||
|                 infrared_tx_start_button_index(infrared, button_index); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|         view_dispatcher_send_custom_event( | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonPress); | ||||
|     } else if(event == RpcAppEventButtonRelease) { | ||||
|         infrared_tx_stop(infrared); | ||||
|         result = true; | ||||
|         view_dispatcher_send_custom_event( | ||||
|             infrared->view_dispatcher, InfraredCustomEventTypeRpcButtonRelease); | ||||
|     } else { | ||||
|         rpc_system_app_confirm(infrared->rpc_ctx, event, false); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| static void infrared_find_vacant_remote_name(string_t name, const char* path) { | ||||
|  | ||||
| @ -14,7 +14,12 @@ enum InfraredCustomEventType { | ||||
|     InfraredCustomEventTypePopupClosed, | ||||
|     InfraredCustomEventTypeButtonSelected, | ||||
|     InfraredCustomEventTypeBackPressed, | ||||
|     InfraredCustomEventTypeRpcLoaded, | ||||
| 
 | ||||
|     InfraredCustomEventTypeRpcLoad, | ||||
|     InfraredCustomEventTypeRpcExit, | ||||
|     InfraredCustomEventTypeRpcButtonPress, | ||||
|     InfraredCustomEventTypeRpcButtonRelease, | ||||
|     InfraredCustomEventTypeRpcSessionClose, | ||||
| }; | ||||
| 
 | ||||
| #pragma pack(push, 1) | ||||
|  | ||||
| @ -28,12 +28,45 @@ bool infrared_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|             view_dispatcher_stop(infrared->view_dispatcher); | ||||
|         } else if(event.event == InfraredCustomEventTypePopupClosed) { | ||||
|             view_dispatcher_stop(infrared->view_dispatcher); | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcLoaded) { | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcLoad) { | ||||
|             bool result = false; | ||||
|             const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); | ||||
|             if(arg) { | ||||
|                 string_set_str(infrared->file_path, arg); | ||||
|                 result = infrared_remote_load(infrared->remote, infrared->file_path); | ||||
|                 infrared_worker_tx_set_get_signal_callback( | ||||
|                     infrared->worker, infrared_worker_tx_get_signal_steady_callback, infrared); | ||||
|                 infrared_worker_tx_set_signal_sent_callback( | ||||
|                     infrared->worker, infrared_signal_sent_callback, infrared); | ||||
|             } | ||||
|             const char* remote_name = infrared_remote_get_name(infrared->remote); | ||||
| 
 | ||||
|             infrared_text_store_set(infrared, 0, "loaded\n%s", remote_name); | ||||
|             popup_set_text( | ||||
|                 infrared->popup, infrared->text_store[0], 82, 32, AlignCenter, AlignTop); | ||||
| 
 | ||||
|             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventLoadFile, result); | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcButtonPress) { | ||||
|             bool result = false; | ||||
|             const char* arg = rpc_system_app_get_data(infrared->rpc_ctx); | ||||
|             if(arg) { | ||||
|                 size_t button_index = 0; | ||||
|                 if(infrared_remote_find_button_by_name(infrared->remote, arg, &button_index)) { | ||||
|                     infrared_tx_start_button_index(infrared, button_index); | ||||
|                     result = true; | ||||
|                 } | ||||
|             } | ||||
|             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, result); | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcButtonRelease) { | ||||
|             infrared_tx_stop(infrared); | ||||
|             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventButtonRelease, true); | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcExit) { | ||||
|             view_dispatcher_stop(infrared->view_dispatcher); | ||||
|             rpc_system_app_confirm(infrared->rpc_ctx, RpcAppEventAppExit, true); | ||||
|         } else if(event.event == InfraredCustomEventTypeRpcSessionClose) { | ||||
|             rpc_system_app_set_callback(infrared->rpc_ctx, NULL, NULL); | ||||
|             infrared->rpc_ctx = NULL; | ||||
|             view_dispatcher_stop(infrared->view_dispatcher); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
|  | ||||
| @ -25,7 +25,7 @@ | ||||
| #include <toolbox/path.h> | ||||
| #include <flipper_format/flipper_format.h> | ||||
| 
 | ||||
| #include "rpc/rpc_app.h" | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| const char* LfRfidApp::app_folder = ANY_PATH("lfrfid"); | ||||
| const char* LfRfidApp::app_extension = ".rfid"; | ||||
| @ -48,38 +48,25 @@ LfRfidApp::~LfRfidApp() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||
| static void rpc_command_callback(RpcAppSystemEvent rpc_event, void* context) { | ||||
|     furi_assert(context); | ||||
|     LfRfidApp* app = static_cast<LfRfidApp*>(context); | ||||
| 
 | ||||
|     bool result = false; | ||||
| 
 | ||||
|     if(event == RpcAppEventSessionClose) { | ||||
|         rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); | ||||
|         app->rpc_ctx = NULL; | ||||
|     if(rpc_event == RpcAppEventSessionClose) { | ||||
|         LfRfidApp::Event event; | ||||
|         event.type = LfRfidApp::EventType::RpcSessionClose; | ||||
|         app->view_controller.send_event(&event); | ||||
|     } else if(rpc_event == RpcAppEventAppExit) { | ||||
|         LfRfidApp::Event event; | ||||
|         event.type = LfRfidApp::EventType::Exit; | ||||
|         app->view_controller.send_event(&event); | ||||
|         result = true; | ||||
|     } else if(event == RpcAppEventAppExit) { | ||||
|     } else if(rpc_event == RpcAppEventLoadFile) { | ||||
|         LfRfidApp::Event event; | ||||
|         event.type = LfRfidApp::EventType::Exit; | ||||
|         event.type = LfRfidApp::EventType::RpcLoadFile; | ||||
|         app->view_controller.send_event(&event); | ||||
|         result = true; | ||||
|     } else if(event == RpcAppEventLoadFile) { | ||||
|         if(arg) { | ||||
|             string_set_str(app->file_path, arg); | ||||
|             if(app->load_key_data(app->file_path, &(app->worker.key), false)) { | ||||
|                 LfRfidApp::Event event; | ||||
|                 event.type = LfRfidApp::EventType::EmulateStart; | ||||
|                 app->view_controller.send_event(&event); | ||||
|                 app->worker.start_emulate(); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         rpc_system_app_confirm(app->rpc_ctx, rpc_event, false); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| void LfRfidApp::run(void* _args) { | ||||
|  | ||||
| @ -33,6 +33,8 @@ public: | ||||
|         Retry, | ||||
|         Exit, | ||||
|         EmulateStart, | ||||
|         RpcLoadFile, | ||||
|         RpcSessionClose, | ||||
|     }; | ||||
| 
 | ||||
|     enum class SceneType : uint8_t { | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| #include "lfrfid_app_scene_rpc.h" | ||||
| #include <core/common_defines.h> | ||||
| #include <dolphin/dolphin.h> | ||||
| #include <rpc/rpc_app.h> | ||||
| 
 | ||||
| static const NotificationSequence sequence_blink_start_magenta = { | ||||
|     &message_blink_start_10, | ||||
| @ -36,6 +37,16 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|         LfRfidApp::Event view_event; | ||||
|         view_event.type = LfRfidApp::EventType::Back; | ||||
|         app->view_controller.send_event(&view_event); | ||||
|         rpc_system_app_confirm(app->rpc_ctx, RpcAppEventAppExit, true); | ||||
|     } else if(event->type == LfRfidApp::EventType::RpcSessionClose) { | ||||
|         // Detach RPC
 | ||||
|         rpc_system_app_set_callback(app->rpc_ctx, NULL, NULL); | ||||
|         app->rpc_ctx = NULL; | ||||
| 
 | ||||
|         consumed = true; | ||||
|         LfRfidApp::Event view_event; | ||||
|         view_event.type = LfRfidApp::EventType::Back; | ||||
|         app->view_controller.send_event(&view_event); | ||||
|     } else if(event->type == LfRfidApp::EventType::EmulateStart) { | ||||
|         auto popup = app->view_controller.get<PopupVM>(); | ||||
|         consumed = true; | ||||
| @ -45,7 +56,22 @@ bool LfRfidAppSceneRpc::on_event(LfRfidApp* app, LfRfidApp::Event* event) { | ||||
|         popup->set_text(app->text_store.text, 89, 43, AlignCenter, AlignTop); | ||||
| 
 | ||||
|         notification_message(app->notification, &sequence_blink_start_magenta); | ||||
|     } else if(event->type == LfRfidApp::EventType::RpcLoadFile) { | ||||
|         const char* arg = rpc_system_app_get_data(app->rpc_ctx); | ||||
|         bool result = false; | ||||
|         if(arg) { | ||||
|             string_set_str(app->file_path, arg); | ||||
|             if(app->load_key_data(app->file_path, &(app->worker.key), false)) { | ||||
|                 LfRfidApp::Event event; | ||||
|                 event.type = LfRfidApp::EventType::EmulateStart; | ||||
|                 app->view_controller.send_event(&event); | ||||
|                 app->worker.start_emulate(); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|         rpc_system_app_confirm(app->rpc_ctx, RpcAppEventLoadFile, result); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -11,4 +11,5 @@ enum NfcCustomEvent { | ||||
|     NfcCustomEventDictAttackDone, | ||||
|     NfcCustomEventDictAttackSkip, | ||||
|     NfcCustomEventRpcLoad, | ||||
|     NfcCustomEventRpcSessionClose, | ||||
| }; | ||||
|  | ||||
| @ -13,78 +13,21 @@ bool nfc_back_event_callback(void* context) { | ||||
|     return scene_manager_handle_back_event(nfc->scene_manager); | ||||
| } | ||||
| 
 | ||||
| void nfc_rpc_exit_callback(Nfc* nfc) { | ||||
|     if(nfc->rpc_state == NfcRpcStateEmulating) { | ||||
|         // Stop worker
 | ||||
|         nfc_worker_stop(nfc->worker); | ||||
|     } else if(nfc->rpc_state == NfcRpcStateEmulated) { | ||||
|         // Stop worker
 | ||||
|         nfc_worker_stop(nfc->worker); | ||||
|         // Save data in shadow file
 | ||||
|         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||
|     } | ||||
|     if(nfc->rpc_ctx) { | ||||
|         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||
|         rpc_system_app_send_exited(nfc->rpc_ctx); | ||||
|         nfc->rpc_ctx = NULL; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool nfc_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | ||||
|     UNUSED(event); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     nfc->rpc_state = NfcRpcStateEmulated; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool nfc_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||
| static void nfc_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     if(!nfc->rpc_ctx) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool result = false; | ||||
|     furi_assert(nfc->rpc_ctx); | ||||
| 
 | ||||
|     if(event == RpcAppEventSessionClose) { | ||||
|         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||
|         nfc->rpc_ctx = NULL; | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
|         result = true; | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcSessionClose); | ||||
|     } else if(event == RpcAppEventAppExit) { | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventViewExit); | ||||
|         result = true; | ||||
|     } else if(event == RpcAppEventLoadFile) { | ||||
|         if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { | ||||
|             if(nfc_device_load(nfc->dev, arg, false)) { | ||||
|                 if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|                     nfc_worker_start( | ||||
|                         nfc->worker, | ||||
|                         NfcWorkerStateMfUltralightEmulate, | ||||
|                         &nfc->dev->dev_data, | ||||
|                         nfc_rpc_emulate_callback, | ||||
|                         nfc); | ||||
|                 } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|                     nfc_worker_start( | ||||
|                         nfc->worker, | ||||
|                         NfcWorkerStateMfClassicEmulate, | ||||
|                         &nfc->dev->dev_data, | ||||
|                         nfc_rpc_emulate_callback, | ||||
|                         nfc); | ||||
|                 } else { | ||||
|                     nfc_worker_start( | ||||
|                         nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||
|                 } | ||||
|                 nfc->rpc_state = NfcRpcStateEmulating; | ||||
|                 view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|         view_dispatcher_send_custom_event(nfc->view_dispatcher, NfcCustomEventRpcLoad); | ||||
|     } else { | ||||
|         rpc_system_app_confirm(nfc->rpc_ctx, event, false); | ||||
|     } | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| Nfc* nfc_alloc() { | ||||
| @ -163,6 +106,21 @@ Nfc* nfc_alloc() { | ||||
| void nfc_free(Nfc* nfc) { | ||||
|     furi_assert(nfc); | ||||
| 
 | ||||
|     if(nfc->rpc_state == NfcRpcStateEmulating) { | ||||
|         // Stop worker
 | ||||
|         nfc_worker_stop(nfc->worker); | ||||
|     } else if(nfc->rpc_state == NfcRpcStateEmulated) { | ||||
|         // Stop worker
 | ||||
|         nfc_worker_stop(nfc->worker); | ||||
|         // Save data in shadow file
 | ||||
|         nfc_device_save_shadow(nfc->dev, nfc->dev->dev_name); | ||||
|     } | ||||
|     if(nfc->rpc_ctx) { | ||||
|         rpc_system_app_send_exited(nfc->rpc_ctx); | ||||
|         rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||
|         nfc->rpc_ctx = NULL; | ||||
|     } | ||||
| 
 | ||||
|     // Nfc device
 | ||||
|     nfc_device_free(nfc->dev); | ||||
| 
 | ||||
|  | ||||
| @ -102,5 +102,3 @@ void nfc_blink_start(Nfc* nfc); | ||||
| void nfc_blink_stop(Nfc* nfc); | ||||
| 
 | ||||
| void nfc_show_loading_popup(void* context, bool show); | ||||
| 
 | ||||
| void nfc_rpc_exit_callback(Nfc* nfc); | ||||
|  | ||||
| @ -14,6 +14,14 @@ void nfc_scene_rpc_on_enter(void* context) { | ||||
|     notification_message(nfc->notifications, &sequence_display_backlight_on); | ||||
| } | ||||
| 
 | ||||
| static bool nfc_scene_rpc_emulate_callback(NfcWorkerEvent event, void* context) { | ||||
|     UNUSED(event); | ||||
|     Nfc* nfc = context; | ||||
| 
 | ||||
|     nfc->rpc_state = NfcRpcStateEmulated; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|     Nfc* nfc = context; | ||||
|     Popup* popup = nfc->popup; | ||||
| @ -22,13 +30,47 @@ bool nfc_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         consumed = true; | ||||
|         if(event.event == NfcCustomEventViewExit) { | ||||
|             rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventAppExit, true); | ||||
|             view_dispatcher_stop(nfc->view_dispatcher); | ||||
|             nfc_blink_stop(nfc); | ||||
|         } else if(event.event == NfcCustomEventRpcSessionClose) { | ||||
|             rpc_system_app_set_callback(nfc->rpc_ctx, NULL, NULL); | ||||
|             nfc->rpc_ctx = NULL; | ||||
|             view_dispatcher_stop(nfc->view_dispatcher); | ||||
|             nfc_blink_stop(nfc); | ||||
|         } else if(event.event == NfcCustomEventRpcLoad) { | ||||
|             nfc_blink_start(nfc); | ||||
|             bool result = false; | ||||
|             const char* arg = rpc_system_app_get_data(nfc->rpc_ctx); | ||||
|             if((arg) && (nfc->rpc_state == NfcRpcStateIdle)) { | ||||
|                 if(nfc_device_load(nfc->dev, arg, false)) { | ||||
|                     if(nfc->dev->format == NfcDeviceSaveFormatMifareUl) { | ||||
|                         nfc_worker_start( | ||||
|                             nfc->worker, | ||||
|                             NfcWorkerStateMfUltralightEmulate, | ||||
|                             &nfc->dev->dev_data, | ||||
|                             nfc_scene_rpc_emulate_callback, | ||||
|                             nfc); | ||||
|                     } else if(nfc->dev->format == NfcDeviceSaveFormatMifareClassic) { | ||||
|                         nfc_worker_start( | ||||
|                             nfc->worker, | ||||
|                             NfcWorkerStateMfClassicEmulate, | ||||
|                             &nfc->dev->dev_data, | ||||
|                             nfc_scene_rpc_emulate_callback, | ||||
|                             nfc); | ||||
|                     } else { | ||||
|                         nfc_worker_start( | ||||
|                             nfc->worker, NfcWorkerStateUidEmulate, &nfc->dev->dev_data, NULL, nfc); | ||||
|                     } | ||||
|                     nfc->rpc_state = NfcRpcStateEmulating; | ||||
|                     result = true; | ||||
| 
 | ||||
|             nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); | ||||
|             popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); | ||||
|                     nfc_blink_start(nfc); | ||||
|                     nfc_text_store_set(nfc, "emulating\n%s", nfc->dev->dev_name); | ||||
|                     popup_set_text(popup, nfc->text_store, 82, 32, AlignCenter, AlignTop); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             rpc_system_app_confirm(nfc->rpc_ctx, RpcAppEventLoadFile, result); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
| @ -38,7 +80,6 @@ void nfc_scene_rpc_on_exit(void* context) { | ||||
|     Nfc* nfc = context; | ||||
|     Popup* popup = nfc->popup; | ||||
| 
 | ||||
|     nfc_rpc_exit_callback(nfc); | ||||
|     nfc_blink_stop(nfc); | ||||
| 
 | ||||
|     popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); | ||||
|  | ||||
| @ -6,24 +6,18 @@ | ||||
| #include "rpc_app.h" | ||||
| 
 | ||||
| #define TAG "RpcSystemApp" | ||||
| #define APP_BUTTON_TIMEOUT 1000 | ||||
| 
 | ||||
| struct RpcAppSystem { | ||||
|     RpcSession* session; | ||||
|     RpcAppSystemCallback app_callback; | ||||
|     void* app_context; | ||||
|     PB_Main* state_msg; | ||||
|     FuriTimer* timer; | ||||
| 
 | ||||
|     uint32_t last_id; | ||||
|     char* last_data; | ||||
| }; | ||||
| 
 | ||||
| static void rpc_system_app_timer_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     RpcAppSystem* rpc_app = context; | ||||
| 
 | ||||
|     if(rpc_app->app_callback) { | ||||
|         rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context); | ||||
|     } | ||||
| } | ||||
| #define RPC_SYSTEM_APP_TEMP_ARGS_SIZE 16 | ||||
| 
 | ||||
| static void rpc_system_app_start_process(const PB_Main* request, void* context) { | ||||
|     furi_assert(request); | ||||
| @ -33,9 +27,12 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | ||||
|     RpcAppSystem* rpc_app = context; | ||||
|     RpcSession* session = rpc_app->session; | ||||
|     furi_assert(session); | ||||
|     char args_temp[16]; | ||||
|     char args_temp[RPC_SYSTEM_APP_TEMP_ARGS_SIZE]; | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Start"); | ||||
|     furi_assert(!rpc_app->last_id); | ||||
|     furi_assert(!rpc_app->last_data); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "StartProcess: id %d", request->command_id); | ||||
| 
 | ||||
|     PB_CommandStatus result = PB_CommandStatus_ERROR_APP_CANT_START; | ||||
| 
 | ||||
| @ -43,9 +40,9 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | ||||
|     const char* app_name = request->content.app_start_request.name; | ||||
|     if(app_name) { | ||||
|         const char* app_args = request->content.app_start_request.args; | ||||
|         if(strcmp(app_args, "RPC") == 0) { | ||||
|         if(app_args && strcmp(app_args, "RPC") == 0) { | ||||
|             // If app is being started in RPC mode - pass RPC context via args string
 | ||||
|             snprintf(args_temp, 16, "RPC %08lX", (uint32_t)rpc_app); | ||||
|             snprintf(args_temp, RPC_SYSTEM_APP_TEMP_ARGS_SIZE, "RPC %08lX", (uint32_t)rpc_app); | ||||
|             app_args = args_temp; | ||||
|         } | ||||
|         LoaderStatus status = loader_start(loader, app_name, app_args); | ||||
| @ -58,7 +55,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | ||||
|         } else if(status == LoaderStatusOk) { | ||||
|             result = PB_CommandStatus_OK; | ||||
|         } else { | ||||
|             furi_assert(0); | ||||
|             furi_crash("Programming Error"); | ||||
|         } | ||||
|     } else { | ||||
|         result = PB_CommandStatus_ERROR_INVALID_PARAMETERS; | ||||
| @ -66,6 +63,7 @@ static void rpc_system_app_start_process(const PB_Main* request, void* context) | ||||
| 
 | ||||
|     furi_record_close(RECORD_LOADER); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "StartProcess: response id %d, result %d", request->command_id, result); | ||||
|     rpc_send_and_release_empty(session, request->command_id, result); | ||||
| } | ||||
| 
 | ||||
| @ -93,6 +91,7 @@ static void rpc_system_app_lock_status_process(const PB_Main* request, void* con | ||||
| 
 | ||||
|     furi_record_close(RECORD_LOADER); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "LockStatus: response"); | ||||
|     rpc_send_and_release(session, &response); | ||||
|     pb_release(&PB_Main_msg, &response); | ||||
| } | ||||
| @ -109,17 +108,17 @@ static void rpc_system_app_exit_request(const PB_Main* request, void* context) { | ||||
|     PB_CommandStatus status; | ||||
| 
 | ||||
|     if(rpc_app->app_callback) { | ||||
|         if(rpc_app->app_callback(RpcAppEventAppExit, NULL, rpc_app->app_context)) { | ||||
|             status = PB_CommandStatus_OK; | ||||
|             furi_timer_stop(rpc_app->timer); | ||||
|         } else { | ||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||
|         } | ||||
|         FURI_LOG_D(TAG, "ExitRequest: id %d", request->command_id); | ||||
|         furi_assert(!rpc_app->last_id); | ||||
|         furi_assert(!rpc_app->last_data); | ||||
|         rpc_app->last_id = request->command_id; | ||||
|         rpc_app->app_callback(RpcAppEventAppExit, rpc_app->app_context); | ||||
|     } else { | ||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||
|         FURI_LOG_E( | ||||
|             TAG, "ExitRequest: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||
|         rpc_send_and_release_empty(session, request->command_id, status); | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_app_load_file(const PB_Main* request, void* context) { | ||||
| @ -133,17 +132,18 @@ static void rpc_system_app_load_file(const PB_Main* request, void* context) { | ||||
| 
 | ||||
|     PB_CommandStatus status; | ||||
|     if(rpc_app->app_callback) { | ||||
|         const char* file_path = request->content.app_load_file_request.path; | ||||
|         if(rpc_app->app_callback(RpcAppEventLoadFile, file_path, rpc_app->app_context)) { | ||||
|             status = PB_CommandStatus_OK; | ||||
|         } else { | ||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||
|         } | ||||
|         FURI_LOG_D(TAG, "LoadFile: id %d", request->command_id); | ||||
|         furi_assert(!rpc_app->last_id); | ||||
|         furi_assert(!rpc_app->last_data); | ||||
|         rpc_app->last_id = request->command_id; | ||||
|         rpc_app->last_data = strdup(request->content.app_load_file_request.path); | ||||
|         rpc_app->app_callback(RpcAppEventLoadFile, rpc_app->app_context); | ||||
|     } else { | ||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||
|         FURI_LOG_E( | ||||
|             TAG, "LoadFile: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||
|         rpc_send_and_release_empty(session, request->command_id, status); | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_app_button_press(const PB_Main* request, void* context) { | ||||
| @ -157,18 +157,18 @@ static void rpc_system_app_button_press(const PB_Main* request, void* context) { | ||||
| 
 | ||||
|     PB_CommandStatus status; | ||||
|     if(rpc_app->app_callback) { | ||||
|         const char* args = request->content.app_button_press_request.args; | ||||
|         if(rpc_app->app_callback(RpcAppEventButtonPress, args, rpc_app->app_context)) { | ||||
|             status = PB_CommandStatus_OK; | ||||
|             furi_timer_start(rpc_app->timer, APP_BUTTON_TIMEOUT); | ||||
|         } else { | ||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||
|         } | ||||
|         FURI_LOG_D(TAG, "ButtonPress"); | ||||
|         furi_assert(!rpc_app->last_id); | ||||
|         furi_assert(!rpc_app->last_data); | ||||
|         rpc_app->last_id = request->command_id; | ||||
|         rpc_app->last_data = strdup(request->content.app_button_press_request.args); | ||||
|         rpc_app->app_callback(RpcAppEventButtonPress, rpc_app->app_context); | ||||
|     } else { | ||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||
|         FURI_LOG_E( | ||||
|             TAG, "ButtonPress: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||
|         rpc_send_and_release_empty(session, request->command_id, status); | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| static void rpc_system_app_button_release(const PB_Main* request, void* context) { | ||||
| @ -182,17 +182,17 @@ static void rpc_system_app_button_release(const PB_Main* request, void* context) | ||||
| 
 | ||||
|     PB_CommandStatus status; | ||||
|     if(rpc_app->app_callback) { | ||||
|         if(rpc_app->app_callback(RpcAppEventButtonRelease, NULL, rpc_app->app_context)) { | ||||
|             status = PB_CommandStatus_OK; | ||||
|             furi_timer_stop(rpc_app->timer); | ||||
|         } else { | ||||
|             status = PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||
|         } | ||||
|         FURI_LOG_D(TAG, "ButtonRelease"); | ||||
|         furi_assert(!rpc_app->last_id); | ||||
|         furi_assert(!rpc_app->last_data); | ||||
|         rpc_app->last_id = request->command_id; | ||||
|         rpc_app->app_callback(RpcAppEventButtonRelease, rpc_app->app_context); | ||||
|     } else { | ||||
|         status = PB_CommandStatus_ERROR_APP_NOT_RUNNING; | ||||
|         FURI_LOG_E( | ||||
|             TAG, "ButtonRelease: APP_NOT_RUNNING, id %d, status: %d", request->command_id, status); | ||||
|         rpc_send_and_release_empty(session, request->command_id, status); | ||||
|     } | ||||
| 
 | ||||
|     rpc_send_and_release_empty(session, request->command_id, status); | ||||
| } | ||||
| 
 | ||||
| void rpc_system_app_send_started(RpcAppSystem* rpc_app) { | ||||
| @ -201,6 +201,8 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_STARTED; | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "SendStarted"); | ||||
|     rpc_send(session, rpc_app->state_msg); | ||||
| } | ||||
| 
 | ||||
| @ -210,9 +212,46 @@ void rpc_system_app_send_exited(RpcAppSystem* rpc_app) { | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     rpc_app->state_msg->content.app_state_response.state = PB_App_AppState_APP_CLOSED; | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "SendExit"); | ||||
|     rpc_send(session, rpc_app->state_msg); | ||||
| } | ||||
| 
 | ||||
| const char* rpc_system_app_get_data(RpcAppSystem* rpc_app) { | ||||
|     furi_assert(rpc_app); | ||||
|     furi_assert(rpc_app->last_data); | ||||
|     return rpc_app->last_data; | ||||
| } | ||||
| 
 | ||||
| void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result) { | ||||
|     furi_assert(rpc_app); | ||||
|     RpcSession* session = rpc_app->session; | ||||
|     furi_assert(session); | ||||
|     furi_assert(rpc_app->last_id); | ||||
| 
 | ||||
|     PB_CommandStatus status = result ? PB_CommandStatus_OK : PB_CommandStatus_ERROR_APP_CMD_ERROR; | ||||
| 
 | ||||
|     uint32_t last_id = 0; | ||||
|     switch(event) { | ||||
|     case RpcAppEventAppExit: | ||||
|     case RpcAppEventLoadFile: | ||||
|     case RpcAppEventButtonPress: | ||||
|     case RpcAppEventButtonRelease: | ||||
|         last_id = rpc_app->last_id; | ||||
|         rpc_app->last_id = 0; | ||||
|         if(rpc_app->last_data) { | ||||
|             free(rpc_app->last_data); | ||||
|             rpc_app->last_data = NULL; | ||||
|         } | ||||
|         FURI_LOG_D(TAG, "AppConfirm: event %d last_id %d status %d", event, last_id, status); | ||||
|         rpc_send_and_release_empty(session, last_id, status); | ||||
|         break; | ||||
|     default: | ||||
|         furi_crash("RPC App state programming Error"); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void rpc_system_app_set_callback(RpcAppSystem* rpc_app, RpcAppSystemCallback callback, void* ctx) { | ||||
|     furi_assert(rpc_app); | ||||
| 
 | ||||
| @ -226,8 +265,6 @@ void* rpc_system_app_alloc(RpcSession* session) { | ||||
|     RpcAppSystem* rpc_app = malloc(sizeof(RpcAppSystem)); | ||||
|     rpc_app->session = session; | ||||
| 
 | ||||
|     rpc_app->timer = furi_timer_alloc(rpc_system_app_timer_callback, FuriTimerTypeOnce, rpc_app); | ||||
| 
 | ||||
|     // App exit message
 | ||||
|     rpc_app->state_msg = malloc(sizeof(PB_Main)); | ||||
|     rpc_app->state_msg->which_content = PB_Main_app_state_response_tag; | ||||
| @ -265,12 +302,16 @@ void rpc_system_app_free(void* context) { | ||||
|     RpcSession* session = rpc_app->session; | ||||
|     furi_assert(session); | ||||
| 
 | ||||
|     furi_timer_free(rpc_app->timer); | ||||
| 
 | ||||
|     if(rpc_app->app_callback) { | ||||
|         rpc_app->app_callback(RpcAppEventSessionClose, NULL, rpc_app->app_context); | ||||
|         rpc_app->app_callback(RpcAppEventSessionClose, rpc_app->app_context); | ||||
|     } | ||||
| 
 | ||||
|     while(rpc_app->app_callback) { | ||||
|         furi_delay_tick(1); | ||||
|     } | ||||
| 
 | ||||
|     if(rpc_app->last_data) free(rpc_app->last_data); | ||||
| 
 | ||||
|     free(rpc_app->state_msg); | ||||
|     free(rpc_app); | ||||
| } | ||||
|  | ||||
| @ -13,7 +13,7 @@ typedef enum { | ||||
|     RpcAppEventButtonRelease, | ||||
| } RpcAppSystemEvent; | ||||
| 
 | ||||
| typedef bool (*RpcAppSystemCallback)(RpcAppSystemEvent event, const char* arg, void* context); | ||||
| typedef void (*RpcAppSystemCallback)(RpcAppSystemEvent event, void* context); | ||||
| 
 | ||||
| typedef struct RpcAppSystem RpcAppSystem; | ||||
| 
 | ||||
| @ -23,6 +23,10 @@ void rpc_system_app_send_started(RpcAppSystem* rpc_app); | ||||
| 
 | ||||
| void rpc_system_app_send_exited(RpcAppSystem* rpc_app); | ||||
| 
 | ||||
| const char* rpc_system_app_get_data(RpcAppSystem* rpc_app); | ||||
| 
 | ||||
| void rpc_system_app_confirm(RpcAppSystem* rpc_app, RpcAppSystemEvent event, bool result); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -47,6 +47,9 @@ typedef enum { | ||||
|     SubGhzCustomEventSceneStay, | ||||
| 
 | ||||
|     SubGhzCustomEventSceneRpcLoad, | ||||
|     SubGhzCustomEventSceneRpcButtonPress, | ||||
|     SubGhzCustomEventSceneRpcButtonRelease, | ||||
|     SubGhzCustomEventSceneRpcSessionClose, | ||||
| 
 | ||||
|     SubGhzCustomEventViewReceiverOK, | ||||
|     SubGhzCustomEventViewReceiverConfig, | ||||
|  | ||||
| @ -22,20 +22,60 @@ bool subghz_scene_rpc_on_event(void* context, SceneManagerEvent event) { | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         consumed = true; | ||||
|         if(event.event == SubGhzCustomEventSceneExit) { | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             } | ||||
|             view_dispatcher_stop(subghz->view_dispatcher); | ||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventAppExit, true); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcSessionClose) { | ||||
|             rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||
|             subghz->rpc_ctx = NULL; | ||||
|             subghz_blink_stop(subghz); | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|             } | ||||
|             view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonPress) { | ||||
|             bool result = false; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||
|                 subghz_blink_start(subghz); | ||||
|                 result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||
|                 result = true; | ||||
|             } | ||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonPress, result); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcButtonRelease) { | ||||
|             bool result = false; | ||||
|             if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|                 subghz_blink_stop(subghz); | ||||
|                 subghz_tx_stop(subghz); | ||||
|                 subghz_sleep(subghz); | ||||
|                 result = true; | ||||
|             } | ||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventButtonRelease, result); | ||||
|         } else if(event.event == SubGhzCustomEventSceneRpcLoad) { | ||||
|             string_t file_name; | ||||
|             string_init(file_name); | ||||
|             path_extract_filename(subghz->file_path, file_name, true); | ||||
|             bool result = false; | ||||
|             const char* arg = rpc_system_app_get_data(subghz->rpc_ctx); | ||||
|             if(arg) { | ||||
|                 if(subghz_key_load(subghz, arg, false)) { | ||||
|                     string_set_str(subghz->file_path, arg); | ||||
|                     result = true; | ||||
|                     string_t file_name; | ||||
|                     string_init(file_name); | ||||
|                     path_extract_filename(subghz->file_path, file_name, true); | ||||
| 
 | ||||
|             snprintf( | ||||
|                 subghz->file_name_tmp, | ||||
|                 SUBGHZ_MAX_LEN_NAME, | ||||
|                 "loaded\n%s", | ||||
|                 string_get_cstr(file_name)); | ||||
|             popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); | ||||
|                     snprintf( | ||||
|                         subghz->file_name_tmp, | ||||
|                         SUBGHZ_MAX_LEN_NAME, | ||||
|                         "loaded\n%s", | ||||
|                         string_get_cstr(file_name)); | ||||
|                     popup_set_text(popup, subghz->file_name_tmp, 82, 32, AlignCenter, AlignTop); | ||||
| 
 | ||||
|             string_clear(file_name); | ||||
|                     string_clear(file_name); | ||||
|                 } | ||||
|             } | ||||
|             rpc_system_app_confirm(subghz->rpc_ctx, RpcAppEventLoadFile, result); | ||||
|         } | ||||
|     } | ||||
|     return consumed; | ||||
|  | ||||
| @ -35,57 +35,38 @@ void subghz_tick_event_callback(void* context) { | ||||
|     scene_manager_handle_tick_event(subghz->scene_manager); | ||||
| } | ||||
| 
 | ||||
| static bool subghz_rpc_command_callback(RpcAppSystemEvent event, const char* arg, void* context) { | ||||
| static void subghz_rpc_command_callback(RpcAppSystemEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
|     SubGhz* subghz = context; | ||||
| 
 | ||||
|     if(!subghz->rpc_ctx) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool result = false; | ||||
|     furi_assert(subghz->rpc_ctx); | ||||
| 
 | ||||
|     if(event == RpcAppEventSessionClose) { | ||||
|         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||
|         subghz->rpc_ctx = NULL; | ||||
|         notification_message(subghz->notifications, &sequence_blink_stop); | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|             subghz_tx_stop(subghz); | ||||
|             subghz_sleep(subghz); | ||||
|         } | ||||
|         result = true; | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneRpcSessionClose); | ||||
|     } else if(event == RpcAppEventAppExit) { | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneExit); | ||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|             subghz_tx_stop(subghz); | ||||
|             subghz_sleep(subghz); | ||||
|         } | ||||
|         result = true; | ||||
|     } else if(event == RpcAppEventLoadFile) { | ||||
|         if(arg) { | ||||
|             if(subghz_key_load(subghz, arg, false)) { | ||||
|                 string_set_str(subghz->file_path, arg); | ||||
|                 view_dispatcher_send_custom_event( | ||||
|                     subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); | ||||
|                 result = true; | ||||
|             } | ||||
|         } | ||||
|         view_dispatcher_send_custom_event(subghz->view_dispatcher, SubGhzCustomEventSceneRpcLoad); | ||||
|     } else if(event == RpcAppEventButtonPress) { | ||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateSleep) { | ||||
|             notification_message(subghz->notifications, &sequence_blink_start_magenta); | ||||
|             result = subghz_tx_start(subghz, subghz->txrx->fff_data); | ||||
|         } | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonPress); | ||||
|     } else if(event == RpcAppEventButtonRelease) { | ||||
|         if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { | ||||
|             notification_message(subghz->notifications, &sequence_blink_stop); | ||||
|             subghz_tx_stop(subghz); | ||||
|             subghz_sleep(subghz); | ||||
|             result = true; | ||||
|         } | ||||
|         view_dispatcher_send_custom_event( | ||||
|             subghz->view_dispatcher, SubGhzCustomEventSceneRpcButtonRelease); | ||||
|     } else { | ||||
|         rpc_system_app_confirm(subghz->rpc_ctx, event, false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     return result; | ||||
| void subghz_blink_start(SubGhz* instance) { | ||||
|     furi_assert(instance); | ||||
|     notification_message(instance->notifications, &sequence_blink_start_magenta); | ||||
| } | ||||
| 
 | ||||
| void subghz_blink_stop(SubGhz* instance) { | ||||
|     furi_assert(instance); | ||||
|     notification_message(instance->notifications, &sequence_blink_stop); | ||||
| } | ||||
| 
 | ||||
| SubGhz* subghz_alloc() { | ||||
| @ -237,7 +218,7 @@ void subghz_free(SubGhz* subghz) { | ||||
|     if(subghz->rpc_ctx) { | ||||
|         rpc_system_app_set_callback(subghz->rpc_ctx, NULL, NULL); | ||||
|         rpc_system_app_send_exited(subghz->rpc_ctx); | ||||
|         notification_message(subghz->notifications, &sequence_blink_stop); | ||||
|         subghz_blink_stop(subghz); | ||||
|         subghz->rpc_ctx = NULL; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -108,6 +108,10 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data); | ||||
| uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency); | ||||
| void subghz_rx_end(SubGhz* subghz); | ||||
| void subghz_sleep(SubGhz* subghz); | ||||
| 
 | ||||
| void subghz_blink_start(SubGhz* instance); | ||||
| void subghz_blink_stop(SubGhz* instance); | ||||
| 
 | ||||
| bool subghz_tx_start(SubGhz* subghz, FlipperFormat* flipper_format); | ||||
| void subghz_tx_stop(SubGhz* subghz); | ||||
| void subghz_dialog_message_show_only_rx(SubGhz* subghz); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく