MX пре 2 година
родитељ
комит
70dad3217a
7 измењених фајлова са 166 додато и 18 уклоњено
  1. 3 2
      README.md
  2. 21 0
      bgloader_api.h
  3. 132 16
      esubghz_chat.c
  4. 3 0
      esubghz_chat_i.h
  5. 2 0
      scenes/esubghz_chat_chat_box.c
  6. 3 0
      scenes/esubghz_chat_freq_input.c
  7. 2 0
      scenes/esubghz_chat_key_display.c

+ 3 - 2
README.md

@@ -4,8 +4,9 @@ This is a plugin for the Flipper Zero that reimplements the Sub-GHz chat
 feature that is available on the CLI. In addition it allows for basic
 feature that is available on the CLI. In addition it allows for basic
 encryption of messages.
 encryption of messages.
 
 
-The plugin has been tested on the official firmware (version 0.87.0) and on
-Unleashed (version unlshd-060).
+The plugin has been tested on the official firmware (version 0.89.0) and on
+Unleashed (version unlshd-062). Currently it does not work properly on
+unlshd-061.
 
 
 ## Warning
 ## Warning
 
 

+ 21 - 0
bgloader_api.h

@@ -0,0 +1,21 @@
+#include <furi.h>
+#include <flipper_application/flipper_application.h>
+
+#define APP_BASE_ARGS "run_in_background"
+
+typedef enum {
+	BGLoaderMessageType_AppReattached,
+	BGLoaderMessageType_LoaderBackground,
+	BGLoaderMessageType_LoaderExit,
+} BGLoaderMessageType;
+
+typedef struct {
+	BGLoaderMessageType type;
+} BGLoaderMessage;
+
+typedef struct {
+	FlipperApplication *fap;
+	FuriThread *thread;
+	FuriMessageQueue *to_app;
+	FuriMessageQueue *to_loader;
+} BGLoaderApp;

+ 132 - 16
esubghz_chat.c

@@ -5,6 +5,8 @@
 #include "helpers/radio_device_loader.h"
 #include "helpers/radio_device_loader.h"
 #include "esubghz_chat_i.h"
 #include "esubghz_chat_i.h"
 
 
+#include "bgloader_api.h"
+
 #define CHAT_LEAVE_DELAY 10
 #define CHAT_LEAVE_DELAY 10
 #define TICK_INTERVAL 50
 #define TICK_INTERVAL 50
 #define MESSAGE_COMPLETION_TIMEOUT 500
 #define MESSAGE_COMPLETION_TIMEOUT 500
@@ -252,21 +254,8 @@ static bool esubghz_chat_navigation_event_callback(void* context)
 	return scene_manager_handle_back_event(state->scene_manager);
 	return scene_manager_handle_back_event(state->scene_manager);
 }
 }
 
 
-/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
- * the locked message if necessary. Retrieves a received message from the
- * Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
-static void esubghz_chat_tick_event_callback(void* context)
+static void esubghz_chat_check_messages(ESubGhzChatState *state)
 {
 {
-	FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");
-
-	furi_assert(context);
-	ESubGhzChatState* state = context;
-
-	/* reset locked message if necessary */
-	if (kbd_lock_msg_reset_timeout(state)) {
-		kbd_lock_msg_reset(state, true);
-	}
-
 	/* if the maximum message size was reached or the
 	/* if the maximum message size was reached or the
 	 * MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
 	 * MESSAGE_COMPLETION_TIMEOUT has expired, retrieve a message and call
 	 * post_rx() */
 	 * post_rx() */
@@ -284,6 +273,24 @@ static void esubghz_chat_tick_event_callback(void* context)
 				state->rx_buffer, RX_TX_BUFFER_SIZE);
 				state->rx_buffer, RX_TX_BUFFER_SIZE);
 		post_rx(state, rx_size);
 		post_rx(state, rx_size);
 	}
 	}
+}
+
+/* Tick event callback for view dispatcher. Called every TICK_INTERVAL. Resets
+ * the locked message if necessary. Retrieves a received message from the
+ * Sub-GHz worker and calls post_rx(). Then calls the scene manager. */
+static void esubghz_chat_tick_event_callback(void* context)
+{
+	FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_tick_event_callback");
+
+	furi_assert(context);
+	ESubGhzChatState* state = context;
+
+	/* reset locked message if necessary */
+	if (kbd_lock_msg_reset_timeout(state)) {
+		kbd_lock_msg_reset(state, true);
+	}
+
+	esubghz_chat_check_messages(state);
 
 
 	/* call scene manager */
 	/* call scene manager */
 	scene_manager_handle_tick_event(state->scene_manager);
 	scene_manager_handle_tick_event(state->scene_manager);
@@ -355,6 +362,18 @@ static void esubghz_hooked_input_callback(InputEvent* event, void* context)
 		return;
 		return;
 	}
 	}
 
 
+	/* handle long press of back key to exit for real */
+	if (event->key == InputKeyBack) {
+		if (state->view_dispatcher->current_view ==
+				text_input_get_view(state->text_input)) {
+			if (event->type == InputTypeLong) {
+				state->exit_for_real = true;
+				view_dispatcher_stop(state->view_dispatcher);
+				return;
+			}
+		}
+	}
+
 	if (event->key == InputKeyOk) {
 	if (event->key == InputKeyOk) {
 		/* if we are in the chat view and no input is ongoing, allow
 		/* if we are in the chat view and no input is ongoing, allow
 		 * locking */
 		 * locking */
@@ -434,6 +453,95 @@ static void esubghz_hooked_input_callback(InputEvent* event, void* context)
 	state->orig_input_cb(event, state->view_dispatcher);
 	state->orig_input_cb(event, state->view_dispatcher);
 }
 }
 
 
+static const char *esubghz_get_bgloader_app_path(const char *args)
+{
+	size_t base_args_len = strlen(APP_BASE_ARGS);
+
+	return (args + base_args_len + 1);
+}
+
+static bool esubghz_run_with_bgloader(const char *args)
+{
+	size_t base_args_len = strlen(APP_BASE_ARGS);
+
+	if (args == NULL) {
+		return false;
+	}
+
+	if (strncmp(args, APP_BASE_ARGS, base_args_len) != 0) {
+		return false;
+	}
+
+	if (strlen(args) < base_args_len + 2) {
+		return false;
+	}
+
+	if (args[base_args_len] != ':') {
+		return false;
+	}
+
+	const char *app_path = esubghz_get_bgloader_app_path(args);
+	return furi_record_exists(app_path);
+}
+
+static void esubghz_attach_to_gui(ESubGhzChatState *state)
+{
+	Gui *gui = furi_record_open(RECORD_GUI);
+	view_dispatcher_attach_to_gui(state->view_dispatcher, gui,
+			ViewDispatcherTypeFullscreen);
+}
+
+static void esubghz_detach_from_gui(ESubGhzChatState *state)
+{
+	gui_remove_view_port(state->view_dispatcher->gui,
+			state->view_dispatcher->view_port);
+	state->view_dispatcher->gui = NULL;
+	furi_record_close(RECORD_GUI);
+}
+
+static void esubghz_bgloader_loop(ESubGhzChatState *state, const char
+		*bg_app_path)
+{
+	while (true) {
+		view_dispatcher_run(state->view_dispatcher);
+		
+		if (state->exit_for_real) {
+			/* exit for real */
+			break;
+		}
+		
+		BGLoaderApp *bg_app = furi_record_open(bg_app_path);
+		
+		/* signal loader that we're ready to go to background */
+		BGLoaderMessage msg;
+		msg.type = BGLoaderMessageType_LoaderBackground;
+		furi_check(furi_message_queue_put(bg_app->to_loader, &msg,
+					FuriWaitForever) == FuriStatusOk);
+		
+		esubghz_detach_from_gui(state);
+
+		while (true) {
+			/* wait for loader to wake us up again */
+			if (furi_message_queue_get(bg_app->to_app, &msg,
+						TICK_INTERVAL) != FuriStatusOk)
+			{
+				/* check for messages on timeout */
+				esubghz_chat_check_messages(state);
+				continue;
+			}
+			if (msg.type == BGLoaderMessageType_AppReattached) {
+				break;
+			} else {
+				furi_check(0);
+			}
+		}
+		
+		furi_record_close(bg_app_path);
+		
+		esubghz_attach_to_gui(state);
+        }
+}
+
 static bool helper_strings_alloc(ESubGhzChatState *state)
 static bool helper_strings_alloc(ESubGhzChatState *state)
 {
 {
 	furi_assert(state);
 	furi_assert(state);
@@ -492,7 +600,7 @@ static void chat_box_free(ESubGhzChatState *state)
 	furi_string_free(state->chat_box_store);
 	furi_string_free(state->chat_box_store);
 }
 }
 
 
-int32_t esubghz_chat(void)
+int32_t esubghz_chat(const char *args)
 {
 {
 	/* init the crypto system */
 	/* init the crypto system */
 	crypto_init();
 	crypto_init();
@@ -577,6 +685,9 @@ int32_t esubghz_chat(void)
 	/* set the default frequency */
 	/* set the default frequency */
 	state->frequency = DEFAULT_FREQ;
 	state->frequency = DEFAULT_FREQ;
 
 
+	/* in the first few views there is no background support */
+	state->exit_for_real = true;
+
 	/* set the have_read callback of the Sub-GHz worker */
 	/* set the have_read callback of the Sub-GHz worker */
 	subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker,
 	subghz_tx_rx_worker_set_callback_have_read(state->subghz_worker,
 			have_read_cb, state);
 			have_read_cb, state);
@@ -649,7 +760,12 @@ int32_t esubghz_chat(void)
 
 
 	/* run the view dispatcher, this call only returns when we close the
 	/* run the view dispatcher, this call only returns when we close the
 	 * application */
 	 * application */
-	view_dispatcher_run(state->view_dispatcher);
+	if (!esubghz_run_with_bgloader(args)) {
+		view_dispatcher_run(state->view_dispatcher);
+	} else {
+		const char *bg_app_path = esubghz_get_bgloader_app_path(args);
+		esubghz_bgloader_loop(state, bg_app_path);
+	}
 
 
 	/* if it is running, stop the Sub-GHz worker */
 	/* if it is running, stop the Sub-GHz worker */
 	if (subghz_tx_rx_worker_is_running(state->subghz_worker)) {
 	if (subghz_tx_rx_worker_is_running(state->subghz_worker)) {

+ 3 - 0
esubghz_chat_i.h

@@ -88,6 +88,9 @@ typedef struct {
 	bool kbd_ok_input_ongoing;
 	bool kbd_ok_input_ongoing;
 	bool kbd_left_input_ongoing;
 	bool kbd_left_input_ongoing;
 	bool kbd_right_input_ongoing;
 	bool kbd_right_input_ongoing;
+
+	// for background support
+	bool exit_for_real;
 } ESubGhzChatState;
 } ESubGhzChatState;
 
 
 typedef enum {
 typedef enum {

+ 2 - 0
scenes/esubghz_chat_chat_box.c

@@ -33,6 +33,8 @@ bool scene_on_event_chat_box(void* context, SceneManagerEvent event)
 		case ESubGhzChatEvent_GotoMsgInput:
 		case ESubGhzChatEvent_GotoMsgInput:
 			if (!scene_manager_previous_scene(
 			if (!scene_manager_previous_scene(
 						state->scene_manager)) {
 						state->scene_manager)) {
+				/* error condition, exit for real */
+				state->exit_for_real = true;
 				view_dispatcher_stop(state->view_dispatcher);
 				view_dispatcher_stop(state->view_dispatcher);
 			}
 			}
 			consumed = true;
 			consumed = true;

+ 3 - 0
scenes/esubghz_chat_freq_input.c

@@ -8,6 +8,9 @@ static void freq_input_cb(void *context)
 
 
 	enter_chat(state);
 	enter_chat(state);
 
 
+	/* starting from here running in background is supported */
+	state->exit_for_real = false;
+
 	view_dispatcher_send_custom_event(state->view_dispatcher,
 	view_dispatcher_send_custom_event(state->view_dispatcher,
 			ESubGhzChatEvent_FreqEntered);
 			ESubGhzChatEvent_FreqEntered);
 }
 }

+ 2 - 0
scenes/esubghz_chat_key_display.c

@@ -93,6 +93,8 @@ bool scene_on_event_key_display(void* context, SceneManagerEvent event)
 		case ESubGhzChatEvent_KeyDisplayBack:
 		case ESubGhzChatEvent_KeyDisplayBack:
 			if (!scene_manager_previous_scene(
 			if (!scene_manager_previous_scene(
 						state->scene_manager)) {
 						state->scene_manager)) {
+				/* error condition, exit for real */
+				state->exit_for_real = true;
 				view_dispatcher_stop(state->view_dispatcher);
 				view_dispatcher_stop(state->view_dispatcher);
 			}
 			}
 			consumed = true;
 			consumed = true;