|
|
@@ -1,96 +1,19 @@
|
|
|
-#include <furi.h>
|
|
|
#include <furi_hal.h>
|
|
|
#include <gui/elements.h>
|
|
|
#include <gui/gui.h>
|
|
|
-#include <gui/modules/text_box.h>
|
|
|
-#include <gui/modules/text_input.h>
|
|
|
-#include <gui/view_dispatcher_i.h>
|
|
|
-#include <gui/view_port_i.h>
|
|
|
-#include <gui/scene_manager.h>
|
|
|
-#include <toolbox/sha256.h>
|
|
|
-#include <notification/notification_messages.h>
|
|
|
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
|
|
|
-#include <lib/subghz/subghz_tx_rx_worker.h>
|
|
|
|
|
|
#include "esubghz_chat_icons.h"
|
|
|
|
|
|
-#include "crypto_wrapper.h"
|
|
|
-
|
|
|
-#define APPLICATION_NAME "ESubGhzChat"
|
|
|
-
|
|
|
-#define DEFAULT_FREQ 433920000
|
|
|
-
|
|
|
-#define RX_TX_BUFFER_SIZE 1024
|
|
|
-
|
|
|
-#define CHAT_BOX_STORE_SIZE 4096
|
|
|
-#define TEXT_INPUT_STORE_SIZE 256
|
|
|
+#include "esubghz_chat_i.h"
|
|
|
|
|
|
#define TICK_INTERVAL 50
|
|
|
#define MESSAGE_COMPLETION_TIMEOUT 500
|
|
|
#define TIMEOUT_BETWEEN_MESSAGES 500
|
|
|
-#define CHAT_LEAVE_DELAY 10
|
|
|
|
|
|
#define KBD_UNLOCK_CNT 3
|
|
|
#define KBD_UNLOCK_TIMEOUT 1000
|
|
|
|
|
|
-typedef struct {
|
|
|
- SceneManager *scene_manager;
|
|
|
- ViewDispatcher *view_dispatcher;
|
|
|
- NotificationApp *notification;
|
|
|
-
|
|
|
- // UI elements
|
|
|
- TextBox *chat_box;
|
|
|
- FuriString *chat_box_store;
|
|
|
- TextInput *text_input;
|
|
|
- char text_input_store[TEXT_INPUT_STORE_SIZE + 1];
|
|
|
-
|
|
|
- // for Sub-GHz
|
|
|
- uint32_t frequency;
|
|
|
- SubGhzTxRxWorker *subghz_worker;
|
|
|
- const SubGhzDevice *subghz_device;
|
|
|
-
|
|
|
- // message assembly before TX
|
|
|
- FuriString *name_prefix;
|
|
|
- FuriString *msg_input;
|
|
|
-
|
|
|
- // encryption
|
|
|
- bool encrypted;
|
|
|
- ESubGhzChatCryptoCtx *crypto_ctx;
|
|
|
-
|
|
|
- // RX and TX buffers
|
|
|
- uint8_t rx_buffer[RX_TX_BUFFER_SIZE];
|
|
|
- uint8_t tx_buffer[RX_TX_BUFFER_SIZE];
|
|
|
- char rx_str_buffer[RX_TX_BUFFER_SIZE + 1];
|
|
|
- volatile uint32_t last_time_rx_data;
|
|
|
-
|
|
|
- // for locking
|
|
|
- ViewPortDrawCallback orig_draw_cb;
|
|
|
- ViewPortInputCallback orig_input_cb;
|
|
|
- bool kbd_locked;
|
|
|
- uint32_t kbd_lock_msg_ticks;
|
|
|
- uint8_t kbd_lock_count;
|
|
|
- bool kbd_ok_input_ongoing;
|
|
|
-} ESubGhzChatState;
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- ESubGhzChatScene_FreqInput,
|
|
|
- ESubGhzChatScene_PassInput,
|
|
|
- ESubGhzChatScene_ChatInput,
|
|
|
- ESubGhzChatScene_ChatBox,
|
|
|
- ESubGhzChatScene_MAX
|
|
|
-} ESubGhzChatScene;
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- ESubGhzChatView_Input,
|
|
|
- ESubGhzChatView_ChatBox,
|
|
|
-} ESubGhzChatView;
|
|
|
-
|
|
|
-typedef enum {
|
|
|
- ESubGhzChatEvent_FreqEntered,
|
|
|
- ESubGhzChatEvent_PassEntered,
|
|
|
- ESubGhzChatEvent_MsgEntered
|
|
|
-} ESubGhzChatEvent;
|
|
|
-
|
|
|
/* Callback for RX events from the Sub-GHz worker. Records the current ticks as
|
|
|
* the time of the last reception. */
|
|
|
static void have_read_cb(void* context)
|
|
|
@@ -161,7 +84,7 @@ static void post_rx(ESubGhzChatState *state, size_t rx_size)
|
|
|
|
|
|
/* Reads the message from msg_input, encrypts it if necessary and then
|
|
|
* transmits it. */
|
|
|
-static void tx_msg_input(ESubGhzChatState *state)
|
|
|
+void tx_msg_input(ESubGhzChatState *state)
|
|
|
{
|
|
|
/* encrypt message if necessary */
|
|
|
size_t msg_len = strlen(furi_string_get_cstr(state->msg_input));
|
|
|
@@ -192,501 +115,6 @@ static void tx_msg_input(ESubGhzChatState *state)
|
|
|
tx_size);
|
|
|
}
|
|
|
|
|
|
-/* Sends FreqEntered event to scene manager and displays the frequency in the
|
|
|
- * text box. */
|
|
|
-static void freq_input_cb(void *context)
|
|
|
-{
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- furi_string_cat_printf(state->chat_box_store, "Frequency: %lu",
|
|
|
- state->frequency);
|
|
|
-
|
|
|
- scene_manager_handle_custom_event(state->scene_manager,
|
|
|
- ESubGhzChatEvent_FreqEntered);
|
|
|
-}
|
|
|
-
|
|
|
-/* Validates the entered frequency. */
|
|
|
-static bool freq_input_validator(const char *text, FuriString *error,
|
|
|
- void *context)
|
|
|
-{
|
|
|
- furi_assert(text);
|
|
|
- furi_assert(error);
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- int ret = sscanf(text, "%lu", &(state->frequency));
|
|
|
- if (ret != 1) {
|
|
|
- furi_string_printf(error, "Please enter\nfrequency\nin Hz!");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (!subghz_devices_is_frequency_valid(state->subghz_device,
|
|
|
- state->frequency)) {
|
|
|
- furi_string_printf(error, "Frequency\n%lu\n is invalid!",
|
|
|
- state->frequency);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef FW_ORIGIN_Official
|
|
|
- if (!furi_hal_region_is_frequency_allowed(state->frequency)) {
|
|
|
-#else /* FW_ORIGIN_Official */
|
|
|
- if (!furi_hal_subghz_is_tx_allowed(state->frequency)) {
|
|
|
-#endif /* FW_ORIGIN_Official */
|
|
|
- furi_string_printf(error, "TX forbidden\non frequency\n%lu!",
|
|
|
- state->frequency);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-/* Sends PassEntered event to scene manager and displays whether or not
|
|
|
- * encryption has been enabled in the text box. Also clears the text input
|
|
|
- * buffer to remove the password and starts the Sub-GHz worker. After starting
|
|
|
- * the worker a join message is transmitted. */
|
|
|
-static void pass_input_cb(void *context)
|
|
|
-{
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- furi_string_cat_printf(state->chat_box_store, "\nEncrypted: %s",
|
|
|
- (state->encrypted ? "yes" : "no"));
|
|
|
-
|
|
|
- /* clear the text input buffer to remove the password */
|
|
|
- crypto_explicit_bzero(state->text_input_store,
|
|
|
- sizeof(state->text_input_store));
|
|
|
-
|
|
|
- subghz_tx_rx_worker_start(state->subghz_worker, state->subghz_device,
|
|
|
- state->frequency);
|
|
|
-
|
|
|
- /* concatenate the name prefix and join message */
|
|
|
- furi_string_set(state->msg_input, state->name_prefix);
|
|
|
- furi_string_cat_str(state->msg_input, " joined chat.");
|
|
|
-
|
|
|
- /* encrypt and transmit message */
|
|
|
- tx_msg_input(state);
|
|
|
-
|
|
|
- /* clear message input buffer */
|
|
|
- furi_string_set_char(state->msg_input, 0, 0);
|
|
|
-
|
|
|
- scene_manager_handle_custom_event(state->scene_manager,
|
|
|
- ESubGhzChatEvent_PassEntered);
|
|
|
-}
|
|
|
-
|
|
|
-/* If a password was entered this derives a key from the password using a
|
|
|
- * single pass of SHA256 and initiates the AES-GCM context for encryption. If
|
|
|
- * the initiation fails, the password is rejected. */
|
|
|
-static bool pass_input_validator(const char *text, FuriString *error,
|
|
|
- void *context)
|
|
|
-{
|
|
|
- furi_assert(text);
|
|
|
- furi_assert(error);
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
-#ifdef FW_ORIGIN_Official
|
|
|
- if (strlen(text) == 0) {
|
|
|
- furi_string_printf(error, "Enter a\npassword!");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (strcmp(text, " ") == 0) {
|
|
|
-#else /* FW_ORIGIN_Official */
|
|
|
- if (strlen(text) == 0) {
|
|
|
-#endif /* FW_ORIGIN_Official */
|
|
|
- state->encrypted = false;
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- unsigned char key[KEY_BITS / 8];
|
|
|
-
|
|
|
- /* derive a key from the password */
|
|
|
- sha256((unsigned char *) text, strlen(text), key);
|
|
|
-
|
|
|
- /* initiate the crypto context */
|
|
|
- bool ret = crypto_ctx_set_key(state->crypto_ctx, key);
|
|
|
-
|
|
|
- /* cleanup */
|
|
|
- crypto_explicit_bzero(key, sizeof(key));
|
|
|
-
|
|
|
- if (!ret) {
|
|
|
- crypto_ctx_clear(state->crypto_ctx);
|
|
|
- furi_string_printf(error, "Failed to\nset key!");
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- state->encrypted = true;
|
|
|
-
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-/* If no message was entred this simply emits a MsgEntered event to the scene
|
|
|
- * manager to switch to the text box. If a message was entered it is appended
|
|
|
- * to the name string. The result is encrypted, if encryption is enabled, and
|
|
|
- * then copied into the TX buffer. The contents of the TX buffer are then
|
|
|
- * transmitted. The sent message is appended to the text box and a MsgEntered
|
|
|
- * event is sent to the scene manager to switch to the text box view. */
|
|
|
-static void chat_input_cb(void *context)
|
|
|
-{
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- /* no message, just switch to the text box view */
|
|
|
-#ifdef FW_ORIGIN_Official
|
|
|
- if (strcmp(state->text_input_store, " ") == 0) {
|
|
|
-#else /* FW_ORIGIN_Official */
|
|
|
- if (strlen(state->text_input_store) == 0) {
|
|
|
-#endif /* FW_ORIGIN_Official */
|
|
|
- scene_manager_handle_custom_event(state->scene_manager,
|
|
|
- ESubGhzChatEvent_MsgEntered);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* concatenate the name prefix and the actual message */
|
|
|
- furi_string_set(state->msg_input, state->name_prefix);
|
|
|
- furi_string_cat_str(state->msg_input, ": ");
|
|
|
- furi_string_cat_str(state->msg_input, state->text_input_store);
|
|
|
-
|
|
|
- /* append the message to the chat box */
|
|
|
- furi_string_cat_printf(state->chat_box_store, "\n%s",
|
|
|
- furi_string_get_cstr(state->msg_input));
|
|
|
-
|
|
|
- /* encrypt and transmit message */
|
|
|
- tx_msg_input(state);
|
|
|
-
|
|
|
- /* clear message input buffer */
|
|
|
- furi_string_set_char(state->msg_input, 0, 0);
|
|
|
-
|
|
|
- /* switch to text box view */
|
|
|
- scene_manager_handle_custom_event(state->scene_manager,
|
|
|
- ESubGhzChatEvent_MsgEntered);
|
|
|
-}
|
|
|
-
|
|
|
-/* Prepares the frequency input scene. */
|
|
|
-static void scene_on_enter_freq_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_freq_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- snprintf(state->text_input_store, TEXT_INPUT_STORE_SIZE, "%lu",
|
|
|
- (uint32_t) DEFAULT_FREQ);
|
|
|
- text_input_reset(state->text_input);
|
|
|
- text_input_set_result_callback(
|
|
|
- state->text_input,
|
|
|
- freq_input_cb,
|
|
|
- state,
|
|
|
- state->text_input_store,
|
|
|
- sizeof(state->text_input_store),
|
|
|
- true);
|
|
|
- text_input_set_validator(
|
|
|
- state->text_input,
|
|
|
- freq_input_validator,
|
|
|
- state);
|
|
|
- text_input_set_header_text(
|
|
|
- state->text_input,
|
|
|
- "Frequency");
|
|
|
-
|
|
|
- view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Handles scene manager events for the frequency input scene. */
|
|
|
-static bool scene_on_event_freq_input(void* context, SceneManagerEvent event)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_event_freq_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- bool consumed = false;
|
|
|
-
|
|
|
- switch(event.type) {
|
|
|
- case SceneManagerEventTypeCustom:
|
|
|
- switch(event.event) {
|
|
|
- /* switch to password input scene */
|
|
|
- case ESubGhzChatEvent_FreqEntered:
|
|
|
- scene_manager_next_scene(state->scene_manager,
|
|
|
- ESubGhzChatScene_PassInput);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case SceneManagerEventTypeBack:
|
|
|
- /* stop the application if the user presses back here */
|
|
|
- view_dispatcher_stop(state->view_dispatcher);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- consumed = false;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return consumed;
|
|
|
-}
|
|
|
-
|
|
|
-/* Cleans up the frequency input scene. */
|
|
|
-static void scene_on_exit_freq_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_freq_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- text_input_reset(state->text_input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Prepares the password input scene. */
|
|
|
-static void scene_on_enter_pass_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_pass_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- state->text_input_store[0] = 0;
|
|
|
- text_input_reset(state->text_input);
|
|
|
- text_input_set_result_callback(
|
|
|
- state->text_input,
|
|
|
- pass_input_cb,
|
|
|
- state,
|
|
|
- state->text_input_store,
|
|
|
- sizeof(state->text_input_store),
|
|
|
- true);
|
|
|
- text_input_set_validator(
|
|
|
- state->text_input,
|
|
|
- pass_input_validator,
|
|
|
- state);
|
|
|
- text_input_set_header_text(
|
|
|
- state->text_input,
|
|
|
-#ifdef FW_ORIGIN_Official
|
|
|
- "Password (space for no encr.)");
|
|
|
-#else /* FW_ORIGIN_Official */
|
|
|
- "Password (empty for no encr.)");
|
|
|
- text_input_set_minimum_length(state->text_input, 0);
|
|
|
-#endif /* FW_ORIGIN_Official */
|
|
|
-
|
|
|
- view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Handles scene manager events for the password input scene. */
|
|
|
-static bool scene_on_event_pass_input(void* context, SceneManagerEvent event)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_event_pass_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- bool consumed = false;
|
|
|
-
|
|
|
- switch(event.type) {
|
|
|
- case SceneManagerEventTypeCustom:
|
|
|
- switch(event.event) {
|
|
|
- /* switch to message input scene */
|
|
|
- case ESubGhzChatEvent_PassEntered:
|
|
|
- scene_manager_next_scene(state->scene_manager,
|
|
|
- ESubGhzChatScene_ChatInput);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case SceneManagerEventTypeBack:
|
|
|
- /* stop the application if the user presses back here */
|
|
|
- view_dispatcher_stop(state->view_dispatcher);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- consumed = false;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return consumed;
|
|
|
-}
|
|
|
-
|
|
|
-/* Cleans up the password input scene. */
|
|
|
-static void scene_on_exit_pass_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_pass_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- text_input_reset(state->text_input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Prepares the message input scene. */
|
|
|
-static void scene_on_enter_chat_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- state->text_input_store[0] = 0;
|
|
|
- text_input_reset(state->text_input);
|
|
|
- text_input_set_result_callback(
|
|
|
- state->text_input,
|
|
|
- chat_input_cb,
|
|
|
- state,
|
|
|
- state->text_input_store,
|
|
|
- sizeof(state->text_input_store),
|
|
|
- true);
|
|
|
- text_input_set_validator(
|
|
|
- state->text_input,
|
|
|
- NULL,
|
|
|
- NULL);
|
|
|
- text_input_set_header_text(
|
|
|
- state->text_input,
|
|
|
-#ifdef FW_ORIGIN_Official
|
|
|
- "Message (space for none)");
|
|
|
-#else /* FW_ORIGIN_Official */
|
|
|
- "Message");
|
|
|
- text_input_set_minimum_length(state->text_input, 0);
|
|
|
-#endif /* FW_ORIGIN_Official */
|
|
|
-
|
|
|
- view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Handles scene manager events for the message input scene. */
|
|
|
-static bool scene_on_event_chat_input(void* context, SceneManagerEvent event)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- bool consumed = false;
|
|
|
-
|
|
|
- switch(event.type) {
|
|
|
- case SceneManagerEventTypeCustom:
|
|
|
- switch(event.event) {
|
|
|
- /* switch to text box scene */
|
|
|
- case ESubGhzChatEvent_MsgEntered:
|
|
|
- scene_manager_next_scene(state->scene_manager,
|
|
|
- ESubGhzChatScene_ChatBox);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- break;
|
|
|
-
|
|
|
- case SceneManagerEventTypeBack:
|
|
|
- /* stop the application and send a leave message if the user
|
|
|
- * presses back here */
|
|
|
-
|
|
|
- /* concatenate the name prefix and leave message */
|
|
|
- furi_string_set(state->msg_input, state->name_prefix);
|
|
|
- furi_string_cat_str(state->msg_input, " left chat.");
|
|
|
-
|
|
|
- /* encrypt and transmit message */
|
|
|
- tx_msg_input(state);
|
|
|
-
|
|
|
- /* clear message input buffer */
|
|
|
- furi_string_set_char(state->msg_input, 0, 0);
|
|
|
-
|
|
|
- /* wait for leave message to be delivered */
|
|
|
- furi_delay_ms(CHAT_LEAVE_DELAY);
|
|
|
-
|
|
|
- view_dispatcher_stop(state->view_dispatcher);
|
|
|
- consumed = true;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- consumed = false;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- return consumed;
|
|
|
-}
|
|
|
-
|
|
|
-/* Cleans up the password input scene. */
|
|
|
-static void scene_on_exit_chat_input(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_input");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- text_input_reset(state->text_input);
|
|
|
-}
|
|
|
-
|
|
|
-/* Prepares the text box scene. */
|
|
|
-static void scene_on_enter_chat_box(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_box");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- text_box_reset(state->chat_box);
|
|
|
- text_box_set_text(state->chat_box,
|
|
|
- furi_string_get_cstr(state->chat_box_store));
|
|
|
- text_box_set_focus(state->chat_box, TextBoxFocusEnd);
|
|
|
-
|
|
|
- view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
|
|
|
-}
|
|
|
-
|
|
|
-/* Handles scene manager events for the text box scene. No events are handled
|
|
|
- * here. */
|
|
|
-static bool scene_on_event_chat_box(void* context, SceneManagerEvent event)
|
|
|
-{
|
|
|
- UNUSED(event);
|
|
|
-
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_box");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-/* Cleans up the text box scene. */
|
|
|
-static void scene_on_exit_chat_box(void* context)
|
|
|
-{
|
|
|
- FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_box");
|
|
|
-
|
|
|
- furi_assert(context);
|
|
|
- ESubGhzChatState* state = context;
|
|
|
-
|
|
|
- text_box_reset(state->chat_box);
|
|
|
-}
|
|
|
-
|
|
|
-/* Scene entry handlers. */
|
|
|
-static void (*const esubghz_chat_scene_on_enter_handlers[])(void*) = {
|
|
|
- scene_on_enter_freq_input,
|
|
|
- scene_on_enter_pass_input,
|
|
|
- scene_on_enter_chat_input,
|
|
|
- scene_on_enter_chat_box
|
|
|
-};
|
|
|
-
|
|
|
-/* Scene event handlers. */
|
|
|
-static bool (*const esubghz_chat_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
|
|
|
- scene_on_event_freq_input,
|
|
|
- scene_on_event_pass_input,
|
|
|
- scene_on_event_chat_input,
|
|
|
- scene_on_event_chat_box
|
|
|
-};
|
|
|
-
|
|
|
-/* Scene exit handlers. */
|
|
|
-static void (*const esubghz_chat_scene_on_exit_handlers[])(void*) = {
|
|
|
- scene_on_exit_freq_input,
|
|
|
- scene_on_exit_pass_input,
|
|
|
- scene_on_exit_chat_input,
|
|
|
- scene_on_exit_chat_box
|
|
|
-};
|
|
|
-
|
|
|
-/* Handlers for the scene manager. */
|
|
|
-static const SceneManagerHandlers esubghz_chat_scene_event_handlers = {
|
|
|
- .on_enter_handlers = esubghz_chat_scene_on_enter_handlers,
|
|
|
- .on_event_handlers = esubghz_chat_scene_on_event_handlers,
|
|
|
- .on_exit_handlers = esubghz_chat_scene_on_exit_handlers,
|
|
|
- .scene_num = ESubGhzChatScene_MAX};
|
|
|
-
|
|
|
/* Whether or not to display the locked message. */
|
|
|
static bool kbd_lock_msg_display(ESubGhzChatState *state)
|
|
|
{
|