#include #include #include #include #include #include #define APPLICATION_NAME "ESubGhzChat" #define CHAT_BOX_STORE_SIZE 4096 #define TEXT_INPUT_STORE_SIZE 512 typedef struct { SceneManager *scene_manager; ViewDispatcher *view_dispatcher; TextBox *chat_box; FuriString *chat_box_store; TextInput *text_input; char text_input_store[TEXT_INPUT_STORE_SIZE + 1]; } 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; static void freq_input_cb(void *context) { furi_assert(context); ESubGhzChatState* state = context; scene_manager_handle_custom_event(state->scene_manager, ESubGhzChatEvent_FreqEntered); } static bool freq_input_validator(const char *text, FuriString *error, void *context) { UNUSED(text); UNUSED(error); UNUSED(context); // TODO return true; } static void pass_input_cb(void *context) { furi_assert(context); ESubGhzChatState* state = context; scene_manager_handle_custom_event(state->scene_manager, ESubGhzChatEvent_PassEntered); } static bool pass_input_validator(const char *text, FuriString *error, void *context) { UNUSED(text); UNUSED(error); UNUSED(context); // TODO return true; } static void chat_input_cb(void *context) { furi_assert(context); ESubGhzChatState* state = context; scene_manager_handle_custom_event(state->scene_manager, ESubGhzChatEvent_MsgEntered); } 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; state->text_input_store[0] = 0; 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); } 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) { case ESubGhzChatEvent_FreqEntered: scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_PassInput); consumed = true; break; } break; case SceneManagerEventTypeBack: view_dispatcher_stop(state->view_dispatcher); consumed = true; break; default: consumed = false; break; } return consumed; } 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); } 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, "Password (empty for no encr.)"); text_input_set_minimum_length(state->text_input, 0); view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input); } 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) { case ESubGhzChatEvent_PassEntered: scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatInput); consumed = true; break; } break; case SceneManagerEventTypeBack: view_dispatcher_stop(state->view_dispatcher); consumed = true; break; default: consumed = false; break; } return consumed; } 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); } 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, "Message"); text_input_set_minimum_length(state->text_input, 0); view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input); } 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) { case ESubGhzChatEvent_MsgEntered: scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_ChatBox); consumed = true; break; } break; case SceneManagerEventTypeBack: view_dispatcher_stop(state->view_dispatcher); consumed = true; break; default: consumed = false; break; } return consumed; } 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); } 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); } 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); // TODO return false; } 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); } 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 }; 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 }; 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 }; 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}; static bool esubghz_chat_custom_event_callback(void* context, uint32_t event) { FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback"); furi_assert(context); ESubGhzChatState* state = context; return scene_manager_handle_custom_event(state->scene_manager, event); } static bool esubghz_chat_navigation_event_callback(void* context) { FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback"); furi_assert(context); ESubGhzChatState* state = context; return scene_manager_handle_back_event(state->scene_manager); } static bool chat_box_alloc(ESubGhzChatState *state) { furi_assert(state); state->chat_box = text_box_alloc(); if (state->chat_box == NULL) { return false; } state->chat_box_store = furi_string_alloc(); if (state->chat_box_store == NULL) { text_box_free(state->chat_box); return false; } furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE); furi_string_set_char(state->chat_box_store, 0, 0); text_box_set_text(state->chat_box, furi_string_get_cstr(state->chat_box_store)); text_box_set_focus(state->chat_box, TextBoxFocusEnd); return true; } static void chat_box_free(ESubGhzChatState *state) { furi_assert(state); text_box_free(state->chat_box); furi_string_free(state->chat_box_store); } int32_t esubghz_chat(void) { int32_t err = -1; FURI_LOG_I(APPLICATION_NAME, "Starting..."); ESubGhzChatState *state = malloc(sizeof(ESubGhzChatState)); if (state == NULL) { goto err_alloc; } state->scene_manager = scene_manager_alloc( &esubghz_chat_scene_event_handlers, state); if (state->scene_manager == NULL) { goto err_alloc_sm; } state->view_dispatcher = view_dispatcher_alloc(); if (state->view_dispatcher == NULL) { goto err_alloc_vd; } state->text_input = text_input_alloc(); if (state->text_input == NULL) { goto err_alloc_ti; } if (!chat_box_alloc(state)) { goto err_alloc_cb; } view_dispatcher_enable_queue(state->view_dispatcher); view_dispatcher_set_event_callback_context(state->view_dispatcher, state); view_dispatcher_set_custom_event_callback( state->view_dispatcher, esubghz_chat_custom_event_callback); view_dispatcher_set_navigation_event_callback( state->view_dispatcher, esubghz_chat_navigation_event_callback); view_dispatcher_add_view(state->view_dispatcher, ESubGhzChatView_Input, text_input_get_view(state->text_input)); view_dispatcher_add_view(state->view_dispatcher, ESubGhzChatView_ChatBox, text_box_get_view(state->chat_box)); /* no error handling here, don't know how */ Gui *gui = furi_record_open(RECORD_GUI); view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen); scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput); view_dispatcher_run(state->view_dispatcher); err = 0; furi_record_close(RECORD_GUI); view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input); view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox); chat_box_free(state); err_alloc_cb: text_input_free(state->text_input); err_alloc_ti: view_dispatcher_free(state->view_dispatcher); err_alloc_vd: scene_manager_free(state->scene_manager); err_alloc_sm: free(state); err_alloc: if (err != 0) { FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!"); } else { FURI_LOG_I(APPLICATION_NAME, "Clean exit."); } return err; }