esubghz_chat.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. #include <furi.h>
  2. #include <gui/gui.h>
  3. #include <gui/modules/text_box.h>
  4. #include <gui/modules/text_input.h>
  5. #include <gui/view_dispatcher.h>
  6. #include <gui/scene_manager.h>
  7. #define APPLICATION_NAME "ESubGhzChat"
  8. #define CHAT_BOX_STORE_SIZE 4096
  9. #define TEXT_INPUT_STORE_SIZE 512
  10. typedef struct {
  11. SceneManager *scene_manager;
  12. ViewDispatcher *view_dispatcher;
  13. TextBox *chat_box;
  14. FuriString *chat_box_store;
  15. TextInput *text_input;
  16. char text_input_store[TEXT_INPUT_STORE_SIZE + 1];
  17. } ESubGhzChatState;
  18. typedef enum {
  19. ESubGhzChatScene_FreqInput,
  20. ESubGhzChatScene_PassInput,
  21. ESubGhzChatScene_ChatInput,
  22. ESubGhzChatScene_ChatBox,
  23. ESubGhzChatScene_MAX
  24. } ESubGhzChatScene;
  25. typedef enum {
  26. ESubGhzChatView_Input,
  27. ESubGhzChatView_ChatBox,
  28. } ESubGhzChatView;
  29. typedef enum {
  30. ESubGhzChatEvent_FreqEntered,
  31. ESubGhzChatEvent_PassEntered,
  32. ESubGhzChatEvent_MsgEntered
  33. } ESubGhzChatEvent;
  34. static void freq_input_cb(void *context)
  35. {
  36. furi_assert(context);
  37. ESubGhzChatState* state = context;
  38. scene_manager_handle_custom_event(state->scene_manager,
  39. ESubGhzChatEvent_FreqEntered);
  40. }
  41. static bool freq_input_validator(const char *text, FuriString *error,
  42. void *context)
  43. {
  44. UNUSED(text);
  45. UNUSED(error);
  46. UNUSED(context);
  47. // TODO
  48. return true;
  49. }
  50. static void pass_input_cb(void *context)
  51. {
  52. furi_assert(context);
  53. ESubGhzChatState* state = context;
  54. scene_manager_handle_custom_event(state->scene_manager,
  55. ESubGhzChatEvent_PassEntered);
  56. }
  57. static bool pass_input_validator(const char *text, FuriString *error,
  58. void *context)
  59. {
  60. UNUSED(text);
  61. UNUSED(error);
  62. UNUSED(context);
  63. // TODO
  64. return true;
  65. }
  66. static void chat_input_cb(void *context)
  67. {
  68. furi_assert(context);
  69. ESubGhzChatState* state = context;
  70. scene_manager_handle_custom_event(state->scene_manager,
  71. ESubGhzChatEvent_MsgEntered);
  72. }
  73. static void scene_on_enter_freq_input(void* context)
  74. {
  75. FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_freq_input");
  76. furi_assert(context);
  77. ESubGhzChatState* state = context;
  78. state->text_input_store[0] = 0;
  79. text_input_reset(state->text_input);
  80. text_input_set_result_callback(
  81. state->text_input,
  82. freq_input_cb,
  83. state,
  84. state->text_input_store,
  85. sizeof(state->text_input_store),
  86. true);
  87. text_input_set_validator(
  88. state->text_input,
  89. freq_input_validator,
  90. state);
  91. text_input_set_header_text(
  92. state->text_input,
  93. "Frequency");
  94. view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
  95. }
  96. static bool scene_on_event_freq_input(void* context, SceneManagerEvent event)
  97. {
  98. FURI_LOG_T(APPLICATION_NAME, "scene_on_event_freq_input");
  99. furi_assert(context);
  100. ESubGhzChatState* state = context;
  101. bool consumed = false;
  102. switch(event.type) {
  103. case SceneManagerEventTypeCustom:
  104. switch(event.event) {
  105. case ESubGhzChatEvent_FreqEntered:
  106. scene_manager_next_scene(state->scene_manager,
  107. ESubGhzChatScene_PassInput);
  108. consumed = true;
  109. break;
  110. }
  111. break;
  112. case SceneManagerEventTypeBack:
  113. view_dispatcher_stop(state->view_dispatcher);
  114. consumed = true;
  115. break;
  116. default:
  117. consumed = false;
  118. break;
  119. }
  120. return consumed;
  121. }
  122. static void scene_on_exit_freq_input(void* context)
  123. {
  124. FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_freq_input");
  125. furi_assert(context);
  126. ESubGhzChatState* state = context;
  127. text_input_reset(state->text_input);
  128. }
  129. static void scene_on_enter_pass_input(void* context)
  130. {
  131. FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_pass_input");
  132. furi_assert(context);
  133. ESubGhzChatState* state = context;
  134. state->text_input_store[0] = 0;
  135. text_input_reset(state->text_input);
  136. text_input_set_result_callback(
  137. state->text_input,
  138. pass_input_cb,
  139. state,
  140. state->text_input_store,
  141. sizeof(state->text_input_store),
  142. true);
  143. text_input_set_validator(
  144. state->text_input,
  145. pass_input_validator,
  146. state);
  147. text_input_set_header_text(
  148. state->text_input,
  149. "Password (empty for no encr.)");
  150. text_input_set_minimum_length(state->text_input, 0);
  151. view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
  152. }
  153. static bool scene_on_event_pass_input(void* context, SceneManagerEvent event)
  154. {
  155. FURI_LOG_T(APPLICATION_NAME, "scene_on_event_pass_input");
  156. furi_assert(context);
  157. ESubGhzChatState* state = context;
  158. bool consumed = false;
  159. switch(event.type) {
  160. case SceneManagerEventTypeCustom:
  161. switch(event.event) {
  162. case ESubGhzChatEvent_PassEntered:
  163. scene_manager_next_scene(state->scene_manager,
  164. ESubGhzChatScene_ChatInput);
  165. consumed = true;
  166. break;
  167. }
  168. break;
  169. case SceneManagerEventTypeBack:
  170. view_dispatcher_stop(state->view_dispatcher);
  171. consumed = true;
  172. break;
  173. default:
  174. consumed = false;
  175. break;
  176. }
  177. return consumed;
  178. }
  179. static void scene_on_exit_pass_input(void* context)
  180. {
  181. FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_pass_input");
  182. furi_assert(context);
  183. ESubGhzChatState* state = context;
  184. text_input_reset(state->text_input);
  185. }
  186. static void scene_on_enter_chat_input(void* context)
  187. {
  188. FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_input");
  189. furi_assert(context);
  190. ESubGhzChatState* state = context;
  191. state->text_input_store[0] = 0;
  192. text_input_reset(state->text_input);
  193. text_input_set_result_callback(
  194. state->text_input,
  195. chat_input_cb,
  196. state,
  197. state->text_input_store,
  198. sizeof(state->text_input_store),
  199. true);
  200. text_input_set_validator(
  201. state->text_input,
  202. NULL,
  203. NULL);
  204. text_input_set_header_text(
  205. state->text_input,
  206. "Message");
  207. text_input_set_minimum_length(state->text_input, 0);
  208. view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_Input);
  209. }
  210. static bool scene_on_event_chat_input(void* context, SceneManagerEvent event)
  211. {
  212. FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_input");
  213. furi_assert(context);
  214. ESubGhzChatState* state = context;
  215. bool consumed = false;
  216. switch(event.type) {
  217. case SceneManagerEventTypeCustom:
  218. switch(event.event) {
  219. case ESubGhzChatEvent_MsgEntered:
  220. scene_manager_next_scene(state->scene_manager,
  221. ESubGhzChatScene_ChatBox);
  222. consumed = true;
  223. break;
  224. }
  225. break;
  226. case SceneManagerEventTypeBack:
  227. view_dispatcher_stop(state->view_dispatcher);
  228. consumed = true;
  229. break;
  230. default:
  231. consumed = false;
  232. break;
  233. }
  234. return consumed;
  235. }
  236. static void scene_on_exit_chat_input(void* context)
  237. {
  238. FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_input");
  239. furi_assert(context);
  240. ESubGhzChatState* state = context;
  241. text_input_reset(state->text_input);
  242. }
  243. static void scene_on_enter_chat_box(void* context)
  244. {
  245. FURI_LOG_T(APPLICATION_NAME, "scene_on_enter_chat_box");
  246. furi_assert(context);
  247. ESubGhzChatState* state = context;
  248. text_box_reset(state->chat_box);
  249. text_box_set_text(state->chat_box,
  250. furi_string_get_cstr(state->chat_box_store));
  251. text_box_set_focus(state->chat_box, TextBoxFocusEnd);
  252. view_dispatcher_switch_to_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
  253. }
  254. static bool scene_on_event_chat_box(void* context, SceneManagerEvent event)
  255. {
  256. UNUSED(event);
  257. FURI_LOG_T(APPLICATION_NAME, "scene_on_event_chat_box");
  258. furi_assert(context);
  259. // TODO
  260. return false;
  261. }
  262. static void scene_on_exit_chat_box(void* context)
  263. {
  264. FURI_LOG_T(APPLICATION_NAME, "scene_on_exit_chat_box");
  265. furi_assert(context);
  266. ESubGhzChatState* state = context;
  267. text_box_reset(state->chat_box);
  268. }
  269. static void (*const esubghz_chat_scene_on_enter_handlers[])(void*) = {
  270. scene_on_enter_freq_input,
  271. scene_on_enter_pass_input,
  272. scene_on_enter_chat_input,
  273. scene_on_enter_chat_box
  274. };
  275. static bool (*const esubghz_chat_scene_on_event_handlers[])(void*, SceneManagerEvent) = {
  276. scene_on_event_freq_input,
  277. scene_on_event_pass_input,
  278. scene_on_event_chat_input,
  279. scene_on_event_chat_box
  280. };
  281. static void (*const esubghz_chat_scene_on_exit_handlers[])(void*) = {
  282. scene_on_exit_freq_input,
  283. scene_on_exit_pass_input,
  284. scene_on_exit_chat_input,
  285. scene_on_exit_chat_box
  286. };
  287. static const SceneManagerHandlers esubghz_chat_scene_event_handlers = {
  288. .on_enter_handlers = esubghz_chat_scene_on_enter_handlers,
  289. .on_event_handlers = esubghz_chat_scene_on_event_handlers,
  290. .on_exit_handlers = esubghz_chat_scene_on_exit_handlers,
  291. .scene_num = ESubGhzChatScene_MAX};
  292. static bool esubghz_chat_custom_event_callback(void* context, uint32_t event)
  293. {
  294. FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_custom_event_callback");
  295. furi_assert(context);
  296. ESubGhzChatState* state = context;
  297. return scene_manager_handle_custom_event(state->scene_manager, event);
  298. }
  299. static bool esubghz_chat_navigation_event_callback(void* context)
  300. {
  301. FURI_LOG_T(APPLICATION_NAME, "esubghz_chat_navigation_event_callback");
  302. furi_assert(context);
  303. ESubGhzChatState* state = context;
  304. return scene_manager_handle_back_event(state->scene_manager);
  305. }
  306. static bool chat_box_alloc(ESubGhzChatState *state)
  307. {
  308. furi_assert(state);
  309. state->chat_box = text_box_alloc();
  310. if (state->chat_box == NULL) {
  311. return false;
  312. }
  313. state->chat_box_store = furi_string_alloc();
  314. if (state->chat_box_store == NULL) {
  315. text_box_free(state->chat_box);
  316. return false;
  317. }
  318. furi_string_reserve(state->chat_box_store, CHAT_BOX_STORE_SIZE);
  319. furi_string_set_char(state->chat_box_store, 0, 0);
  320. text_box_set_text(state->chat_box,
  321. furi_string_get_cstr(state->chat_box_store));
  322. text_box_set_focus(state->chat_box, TextBoxFocusEnd);
  323. return true;
  324. }
  325. static void chat_box_free(ESubGhzChatState *state)
  326. {
  327. furi_assert(state);
  328. text_box_free(state->chat_box);
  329. furi_string_free(state->chat_box_store);
  330. }
  331. int32_t esubghz_chat(void)
  332. {
  333. int32_t err = -1;
  334. FURI_LOG_I(APPLICATION_NAME, "Starting...");
  335. ESubGhzChatState *state = malloc(sizeof(ESubGhzChatState));
  336. if (state == NULL) {
  337. goto err_alloc;
  338. }
  339. state->scene_manager = scene_manager_alloc(
  340. &esubghz_chat_scene_event_handlers, state);
  341. if (state->scene_manager == NULL) {
  342. goto err_alloc_sm;
  343. }
  344. state->view_dispatcher = view_dispatcher_alloc();
  345. if (state->view_dispatcher == NULL) {
  346. goto err_alloc_vd;
  347. }
  348. state->text_input = text_input_alloc();
  349. if (state->text_input == NULL) {
  350. goto err_alloc_ti;
  351. }
  352. if (!chat_box_alloc(state)) {
  353. goto err_alloc_cb;
  354. }
  355. view_dispatcher_enable_queue(state->view_dispatcher);
  356. view_dispatcher_set_event_callback_context(state->view_dispatcher, state);
  357. view_dispatcher_set_custom_event_callback(
  358. state->view_dispatcher,
  359. esubghz_chat_custom_event_callback);
  360. view_dispatcher_set_navigation_event_callback(
  361. state->view_dispatcher,
  362. esubghz_chat_navigation_event_callback);
  363. view_dispatcher_add_view(state->view_dispatcher, ESubGhzChatView_Input,
  364. text_input_get_view(state->text_input));
  365. view_dispatcher_add_view(state->view_dispatcher, ESubGhzChatView_ChatBox,
  366. text_box_get_view(state->chat_box));
  367. /* no error handling here, don't know how */
  368. Gui *gui = furi_record_open(RECORD_GUI);
  369. view_dispatcher_attach_to_gui(state->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
  370. scene_manager_next_scene(state->scene_manager, ESubGhzChatScene_FreqInput);
  371. view_dispatcher_run(state->view_dispatcher);
  372. err = 0;
  373. furi_record_close(RECORD_GUI);
  374. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_Input);
  375. view_dispatcher_remove_view(state->view_dispatcher, ESubGhzChatView_ChatBox);
  376. chat_box_free(state);
  377. err_alloc_cb:
  378. text_input_free(state->text_input);
  379. err_alloc_ti:
  380. view_dispatcher_free(state->view_dispatcher);
  381. err_alloc_vd:
  382. scene_manager_free(state->scene_manager);
  383. err_alloc_sm:
  384. free(state);
  385. err_alloc:
  386. if (err != 0) {
  387. FURI_LOG_E(APPLICATION_NAME, "Failed to launch (alloc error)!");
  388. } else {
  389. FURI_LOG_I(APPLICATION_NAME, "Clean exit.");
  390. }
  391. return err;
  392. }