callback.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. #include <callback/callback.h>
  2. #include <callback/loader.h>
  3. #include <callback/free.h>
  4. #include <callback/alloc.h>
  5. uint32_t callback_exit_app(void *context)
  6. {
  7. UNUSED(context);
  8. return VIEW_NONE;
  9. }
  10. uint32_t callback_submenu_ap(void *context)
  11. {
  12. FlipWiFiApp *app = (FlipWiFiApp *)context;
  13. furi_check(app);
  14. back_from_ap = true;
  15. return FlipWiFiViewSubmenu;
  16. }
  17. uint32_t callback_to_submenu_main(void *context)
  18. {
  19. UNUSED(context);
  20. ssid_index = 0;
  21. return FlipWiFiViewSubmenuMain;
  22. }
  23. uint32_t callback_to_submenu_scan(void *context)
  24. {
  25. UNUSED(context);
  26. ssid_index = 0;
  27. return FlipWiFiViewSubmenu;
  28. }
  29. uint32_t callback_to_submenu_saved(void *context)
  30. {
  31. UNUSED(context);
  32. ssid_index = 0;
  33. return FlipWiFiViewSubmenu;
  34. }
  35. void callback_custom_command_updated(void *context)
  36. {
  37. FlipWiFiApp *app = (FlipWiFiApp *)context;
  38. furi_check(app);
  39. if (!app->fhttp)
  40. {
  41. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  42. return;
  43. }
  44. if (!app->uart_text_input_temp_buffer)
  45. {
  46. FURI_LOG_E(TAG, "Text input buffer is NULL");
  47. return;
  48. }
  49. if (!app->uart_text_input_temp_buffer[0])
  50. {
  51. FURI_LOG_E(TAG, "Text input buffer is empty");
  52. return;
  53. }
  54. // Send the custom command
  55. flipper_http_send_data(app->fhttp, app->uart_text_input_temp_buffer);
  56. uint32_t timeout = 50; // 5 seconds / 100ms iterations
  57. while ((app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0) && timeout > 0)
  58. {
  59. furi_delay_ms(100);
  60. timeout--;
  61. }
  62. // Switch to the view
  63. char response[100];
  64. snprintf(response, sizeof(response), "%s", app->fhttp->last_response);
  65. easy_flipper_dialog("", response);
  66. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  67. }
  68. void callback_ap_ssid_updated(void *context)
  69. {
  70. FlipWiFiApp *app = (FlipWiFiApp *)context;
  71. furi_check(app);
  72. if (!app->uart_text_input_temp_buffer)
  73. {
  74. FURI_LOG_E(TAG, "Text input buffer is NULL");
  75. return;
  76. }
  77. if (!app->uart_text_input_temp_buffer[0])
  78. {
  79. FURI_LOG_E(TAG, "Text input buffer is empty");
  80. return;
  81. }
  82. save_char("ap_ssid", app->uart_text_input_temp_buffer);
  83. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  84. }
  85. void callback_redraw_submenu_saved(void *context)
  86. {
  87. // re draw the saved submenu
  88. FlipWiFiApp *app = (FlipWiFiApp *)context;
  89. furi_check(app);
  90. if (!app->submenu_wifi)
  91. {
  92. FURI_LOG_E(TAG, "Submenu is NULL");
  93. return;
  94. }
  95. if (!wifi_playlist)
  96. {
  97. FURI_LOG_E(TAG, "WiFi Playlist is NULL");
  98. return;
  99. }
  100. submenu_reset(app->submenu_wifi);
  101. submenu_set_header(app->submenu_wifi, "Saved APs");
  102. submenu_add_item(app->submenu_wifi, "[Add Network]", FlipWiFiSubmenuIndexWiFiSavedAddSSID, callback_submenu_choices, app);
  103. for (size_t i = 0; i < wifi_playlist->count; i++)
  104. {
  105. submenu_add_item(app->submenu_wifi, wifi_playlist->ssids[i], FlipWiFiSubmenuIndexWiFiSavedStart + i, callback_submenu_choices, app);
  106. }
  107. }
  108. // Callback for drawing the main screen
  109. void callback_view_draw_callback_scan(Canvas *canvas, void *model)
  110. {
  111. UNUSED(model);
  112. canvas_clear(canvas);
  113. canvas_set_font(canvas, FontPrimary);
  114. canvas_draw_str(canvas, 0, 10, ssid_list[ssid_index]);
  115. canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
  116. canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Back");
  117. canvas_draw_icon(canvas, 96, 53, &I_ButtonRight_4x7);
  118. canvas_draw_str_aligned(canvas, 103, 54, AlignLeft, AlignTop, "Add");
  119. }
  120. void callback_view_draw_callback_saved(Canvas *canvas, void *model)
  121. {
  122. UNUSED(model);
  123. canvas_clear(canvas);
  124. canvas_set_font(canvas, FontPrimary);
  125. canvas_draw_str(canvas, 0, 10, current_ssid);
  126. canvas_set_font(canvas, FontSecondary);
  127. char password[72];
  128. snprintf(password, sizeof(password), "Pass: %s", current_password);
  129. canvas_draw_str(canvas, 0, 20, password);
  130. canvas_draw_icon(canvas, 0, 54, &I_ButtonLeft_4x7);
  131. canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete");
  132. canvas_draw_icon(canvas, 37, 53, &I_ButtonBACK_10x8);
  133. canvas_draw_str_aligned(canvas, 49, 54, AlignLeft, AlignTop, "Back");
  134. canvas_draw_icon(canvas, 73, 54, &I_ButtonOK_7x7);
  135. canvas_draw_str_aligned(canvas, 81, 54, AlignLeft, AlignTop, "Set");
  136. canvas_draw_icon(canvas, 100, 54, &I_ButtonRight_4x7);
  137. canvas_draw_str_aligned(canvas, 107, 54, AlignLeft, AlignTop, "Edit");
  138. }
  139. // Input callback for the view (async input handling)
  140. bool callback_view_input_callback_scan(InputEvent *event, void *context)
  141. {
  142. FlipWiFiApp *app = (FlipWiFiApp *)context;
  143. if (event->type == InputTypePress && event->key == InputKeyRight)
  144. {
  145. // switch to text input to set password
  146. free_text_inputs(app);
  147. if (!alloc_text_inputs(app, FlipWiFiViewTextInputScan))
  148. {
  149. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved Add Password");
  150. return false;
  151. }
  152. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  153. return true;
  154. }
  155. return false;
  156. }
  157. // Input callback for the view (async input handling)
  158. bool callback_view_input_callback_saved(InputEvent *event, void *context)
  159. {
  160. FlipWiFiApp *app = (FlipWiFiApp *)context;
  161. furi_check(app);
  162. if (event->type == InputTypePress && event->key == InputKeyRight)
  163. {
  164. // set text input buffer as the selected password
  165. strncpy(app->uart_text_input_temp_buffer, wifi_playlist->passwords[ssid_index], app->uart_text_input_buffer_size);
  166. // switch to text input to set password
  167. free_text_inputs(app);
  168. if (!alloc_text_inputs(app, FlipWiFiViewTextInputSaved))
  169. {
  170. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved");
  171. return false;
  172. }
  173. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  174. return true;
  175. }
  176. else if (event->type == InputTypePress && event->key == InputKeyOk)
  177. {
  178. // save the settings
  179. save_settings(wifi_playlist->ssids[ssid_index], wifi_playlist->passwords[ssid_index]);
  180. // initialize uart
  181. FlipperHTTP *fhttp = flipper_http_alloc();
  182. if (!fhttp)
  183. {
  184. easy_flipper_dialog("[ERROR]", "Failed to initialize flipper http");
  185. return false;
  186. }
  187. // clear response
  188. if (fhttp->last_response)
  189. snprintf(fhttp->last_response, RX_BUF_SIZE, "%s", "");
  190. if (!flipper_http_save_wifi(fhttp, wifi_playlist->ssids[ssid_index], wifi_playlist->passwords[ssid_index]))
  191. {
  192. easy_flipper_dialog("[ERROR]", "Failed to save WiFi settings");
  193. return false;
  194. }
  195. while (!fhttp->last_response || strlen(fhttp->last_response) == 0)
  196. {
  197. furi_delay_ms(100);
  198. }
  199. // check success (if [SUCCESS] is in the response)
  200. if (strstr(fhttp->last_response, "[SUCCESS]") == NULL)
  201. {
  202. char response[256];
  203. snprintf(response, sizeof(response), "Failed to save WiFi settings:\n%s", fhttp->last_response);
  204. easy_flipper_dialog("[ERROR]", response);
  205. flipper_http_free(fhttp);
  206. return false;
  207. }
  208. flipper_http_free(fhttp);
  209. easy_flipper_dialog("[SUCCESS]", "All FlipperHTTP apps will now\nuse the selected network.");
  210. return true;
  211. }
  212. else if (event->type == InputTypePress && event->key == InputKeyLeft)
  213. {
  214. // shift the remaining ssids and passwords
  215. for (uint32_t i = ssid_index; i < wifi_playlist->count - 1; i++)
  216. {
  217. // Use strncpy to prevent buffer overflows and ensure null termination
  218. strncpy(wifi_playlist->ssids[i], wifi_playlist->ssids[i + 1], MAX_SSID_LENGTH - 1);
  219. wifi_playlist->ssids[i][MAX_SSID_LENGTH - 1] = '\0'; // Ensure null-termination
  220. strncpy(wifi_playlist->passwords[i], wifi_playlist->passwords[i + 1], MAX_SSID_LENGTH - 1);
  221. wifi_playlist->passwords[i][MAX_SSID_LENGTH - 1] = '\0'; // Ensure null-termination
  222. // Shift ssid_list
  223. ssid_list[i] = ssid_list[i + 1];
  224. }
  225. wifi_playlist->count--;
  226. // delete the last ssid and password
  227. wifi_playlist->ssids[wifi_playlist->count][0] = '\0';
  228. wifi_playlist->passwords[wifi_playlist->count][0] = '\0';
  229. // save the playlist to storage
  230. save_playlist(wifi_playlist);
  231. // re draw the saved submenu
  232. callback_redraw_submenu_saved(app);
  233. // switch back to the saved view
  234. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  235. return true;
  236. }
  237. return false;
  238. }
  239. static bool callback_set_html()
  240. {
  241. DialogsApp *dialogs = furi_record_open(RECORD_DIALOGS);
  242. DialogsFileBrowserOptions browser_options;
  243. dialog_file_browser_set_basic_options(&browser_options, ".js", NULL);
  244. browser_options.extension = "html";
  245. browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
  246. browser_options.skip_assets = true;
  247. browser_options.hide_dot_files = true;
  248. browser_options.icon = NULL;
  249. browser_options.hide_ext = false;
  250. FuriString *marauder_html_path = furi_string_alloc_set_str("/ext/apps_data/marauder/html");
  251. if (!marauder_html_path)
  252. {
  253. furi_record_close(RECORD_DIALOGS);
  254. return false;
  255. }
  256. if (dialog_file_browser_show(dialogs, marauder_html_path, marauder_html_path, &browser_options))
  257. {
  258. // Store the selected script file path
  259. const char *file_path = furi_string_get_cstr(marauder_html_path);
  260. save_char("ap_html_path", file_path);
  261. }
  262. furi_string_free(marauder_html_path);
  263. furi_record_close(RECORD_DIALOGS);
  264. return true;
  265. }
  266. static bool callback_run_ap_mode(void *context)
  267. {
  268. FlipWiFiApp *app = (FlipWiFiApp *)context;
  269. furi_check(app);
  270. if (!app->fhttp)
  271. {
  272. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  273. return false;
  274. }
  275. char ssid[64];
  276. if (!load_char("ap_ssid", ssid, sizeof(ssid)))
  277. {
  278. FURI_LOG_E(TAG, "Failed to load AP SSID");
  279. return false;
  280. }
  281. // clear response
  282. if (app->fhttp->last_response)
  283. snprintf(app->fhttp->last_response, RX_BUF_SIZE, "%s", "");
  284. char stat_command[128];
  285. snprintf(stat_command, sizeof(stat_command), "[WIFI/AP]{\"ssid\":\"%s\"}", ssid);
  286. if (!flipper_http_send_data(app->fhttp, stat_command))
  287. {
  288. FURI_LOG_E(TAG, "Failed to start AP mode");
  289. return false;
  290. }
  291. Loading *loading = loading_alloc();
  292. int32_t loading_view_id = 87654321; // Random ID
  293. view_dispatcher_add_view(app->view_dispatcher, loading_view_id, loading_get_view(loading));
  294. view_dispatcher_switch_to_view(app->view_dispatcher, loading_view_id);
  295. while (app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0)
  296. {
  297. furi_delay_ms(100);
  298. }
  299. // check success (if [AP/CONNECTED] is in the response)
  300. if (strstr(app->fhttp->last_response, "[AP/CONNECTED]") != NULL)
  301. {
  302. // send the HTML file
  303. char html_path[128];
  304. if (!load_char("ap_html_path", html_path, sizeof(html_path)))
  305. {
  306. FURI_LOG_E(TAG, "Failed to load HTML path");
  307. return false;
  308. }
  309. flipper_http_send_data(app->fhttp, "[WIFI/AP/UPDATE]");
  310. furi_delay_ms(1000);
  311. FuriString *html_content = flipper_http_load_from_file(html_path);
  312. if (html_content == NULL || furi_string_size(html_content) == 0)
  313. {
  314. FURI_LOG_E(TAG, "Failed to load HTML file");
  315. if (html_content)
  316. furi_string_free(html_content);
  317. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  318. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  319. loading_free(loading);
  320. loading = NULL;
  321. return false;
  322. }
  323. furi_string_cat_str(html_content, "\n");
  324. const char *send_buffer = furi_string_get_cstr(html_content);
  325. const size_t send_buffer_size = furi_string_size(html_content);
  326. app->fhttp->state = SENDING;
  327. size_t offset = 0;
  328. while (offset < send_buffer_size)
  329. {
  330. size_t chunk_size = send_buffer_size - offset > 512 ? 512 : send_buffer_size - offset;
  331. furi_hal_serial_tx(app->fhttp->serial_handle, (const uint8_t *)(send_buffer + offset), chunk_size);
  332. offset += chunk_size;
  333. furi_delay_ms(50); // cant go faster than this, no matter the chunk size
  334. }
  335. // send the [WIFI/AP/UPDATE/END] command
  336. flipper_http_send_data(app->fhttp, "[WIFI/AP/UPDATE/END]");
  337. app->fhttp->state = IDLE;
  338. furi_string_free(html_content);
  339. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  340. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  341. loading_free(loading);
  342. loading = NULL;
  343. return true;
  344. }
  345. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  346. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  347. loading_free(loading);
  348. loading = NULL;
  349. return false;
  350. }
  351. void callback_timer_callback(void *context)
  352. {
  353. furi_check(context, "callback_timer_callback: Context is NULL");
  354. FlipWiFiApp *app = (FlipWiFiApp *)context;
  355. view_dispatcher_send_custom_event(app->view_dispatcher, FlipWiFiCustomEventAP);
  356. }
  357. static size_t last_response_len = 0;
  358. static void update_text_box(FlipWiFiApp *app)
  359. {
  360. furi_check(app, "FlipWiFiApp is NULL");
  361. furi_check(app->textbox, "Text_box is NULL");
  362. if (app->fhttp)
  363. {
  364. if (!app->fhttp->last_response_str || furi_string_size(app->fhttp->last_response_str) == 0)
  365. {
  366. text_box_reset(app->textbox);
  367. text_box_set_focus(app->textbox, TextBoxFocusEnd);
  368. text_box_set_font(app->textbox, TextBoxFontText);
  369. text_box_set_text(app->textbox, "AP Connected... please wait");
  370. }
  371. else if (furi_string_size(app->fhttp->last_response_str) != last_response_len)
  372. {
  373. text_box_reset(app->textbox);
  374. text_box_set_focus(app->textbox, TextBoxFocusEnd);
  375. text_box_set_font(app->textbox, TextBoxFontText);
  376. last_response_len = furi_string_size(app->fhttp->last_response_str);
  377. text_box_set_text(app->textbox, furi_string_get_cstr(app->fhttp->last_response_str));
  378. }
  379. }
  380. }
  381. static void callback_loader_process_callback(void *context)
  382. {
  383. FlipWiFiApp *app = (FlipWiFiApp *)context;
  384. furi_check(app, "FlipWiFiApp is NULL");
  385. update_text_box(app);
  386. }
  387. static bool callback_custom_event_callback(void *context, uint32_t index)
  388. {
  389. furi_check(context, "callback_custom_event_callback: Context is NULL");
  390. switch (index)
  391. {
  392. case FlipWiFiCustomEventAP:
  393. callback_loader_process_callback(context);
  394. return true;
  395. default:
  396. FURI_LOG_E(TAG, "callback_custom_event_callback. Unknown index: %ld", index);
  397. return false;
  398. }
  399. }
  400. // scan for wifi ad parse the results
  401. static bool callback_scan(FlipperHTTP *fhttp)
  402. {
  403. if (!fhttp)
  404. {
  405. FURI_LOG_E(TAG, "FlipperHTTP is NULL");
  406. return false;
  407. }
  408. // storage setup
  409. Storage *storage = furi_record_open(RECORD_STORAGE);
  410. snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/data/scan.txt");
  411. storage_simply_remove_recursive(storage, fhttp->file_path); // ensure the file is empty
  412. // ensure flip_wifi directory is there
  413. char directory_path[128];
  414. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi");
  415. storage_common_mkdir(storage, directory_path);
  416. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps_data/flip_wifi/data");
  417. storage_common_mkdir(storage, directory_path);
  418. furi_record_close(RECORD_STORAGE);
  419. fhttp->just_started = true;
  420. fhttp->save_received_data = true;
  421. FURI_LOG_I(TAG, "callback_scan: Sending scan command");
  422. return flipper_http_send_command(fhttp, HTTP_CMD_SCAN);
  423. }
  424. static bool callback_handle_scan(FlipperHTTP *fhttp, void *context)
  425. {
  426. FURI_LOG_I(TAG, "callback_handle_scan1");
  427. FlipWiFiApp *app = (FlipWiFiApp *)context;
  428. furi_check(app);
  429. FURI_LOG_I(TAG, "callback_handle_scan2");
  430. if (!fhttp || !context)
  431. {
  432. FURI_LOG_E(TAG, "FlipperHTTP or context is NULL");
  433. return false;
  434. }
  435. // load the received data from the saved file
  436. FuriString *scan_data = flipper_http_load_from_file(fhttp->file_path);
  437. if (scan_data == NULL)
  438. {
  439. FURI_LOG_E(TAG, "Failed to load received data from file.");
  440. return false;
  441. }
  442. uint8_t ssid_count = 0;
  443. for (uint8_t i = 0; i < MAX_SCAN_NETWORKS; i++)
  444. {
  445. char *ssid_item = get_json_array_value("networks", i, furi_string_get_cstr(scan_data));
  446. if (ssid_item == NULL)
  447. {
  448. // end of the list
  449. break;
  450. }
  451. ssid_list[i] = malloc(MAX_SSID_LENGTH);
  452. if (ssid_list[i] == NULL)
  453. {
  454. FURI_LOG_E(TAG, "Failed to allocate memory for SSID");
  455. furi_string_free(scan_data);
  456. return false;
  457. }
  458. snprintf(ssid_list[i], MAX_SSID_LENGTH, "%s", ssid_item);
  459. free(ssid_item);
  460. ssid_count++;
  461. }
  462. // Add each SSID as a submenu item
  463. submenu_reset(app->submenu_wifi);
  464. submenu_set_header(app->submenu_wifi, "WiFi Nearby");
  465. for (uint8_t i = 0; i < ssid_count; i++)
  466. {
  467. char *ssid_item = ssid_list[i];
  468. if (ssid_item == NULL)
  469. {
  470. // end of the list
  471. break;
  472. }
  473. char ssid[64];
  474. snprintf(ssid, sizeof(ssid), "%s", ssid_item);
  475. submenu_add_item(app->submenu_wifi, ssid, FlipWiFiSubmenuIndexWiFiScanStart + i, callback_submenu_choices, app);
  476. }
  477. furi_string_free(scan_data);
  478. FURI_LOG_I(TAG, "Scan completed. Found %d networks.", ssid_count);
  479. return true;
  480. }
  481. void callback_submenu_choices(void *context, uint32_t index)
  482. {
  483. FlipWiFiApp *app = (FlipWiFiApp *)context;
  484. furi_check(app);
  485. switch (index)
  486. {
  487. case FlipWiFiSubmenuIndexWiFiScan:
  488. free_all(app);
  489. if (!alloc_submenus(app, FlipWiFiViewSubmenuScan))
  490. {
  491. easy_flipper_dialog("[ERROR]", "Failed to allocate submenus for WiFi Scan");
  492. return;
  493. }
  494. app->fhttp = flipper_http_alloc();
  495. if (!app->fhttp)
  496. {
  497. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  498. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  499. return;
  500. }
  501. if (callback_scan(app->fhttp))
  502. {
  503. furi_delay_ms(100); // wait for the command to be sent
  504. // wait for the scan to complete
  505. Loading *loading = loading_alloc();
  506. int32_t loading_view_id = 87654321; // Random ID
  507. view_dispatcher_add_view(app->view_dispatcher, loading_view_id, loading_get_view(loading));
  508. view_dispatcher_switch_to_view(app->view_dispatcher, loading_view_id);
  509. while (app->fhttp->state != IDLE)
  510. {
  511. furi_delay_ms(100);
  512. }
  513. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  514. if (!callback_handle_scan(app->fhttp, app))
  515. {
  516. FURI_LOG_E(TAG, "Failed to handle scan");
  517. easy_flipper_dialog("[ERROR]", "Failed to handle scan");
  518. return;
  519. }
  520. view_dispatcher_remove_view(app->view_dispatcher, loading_view_id);
  521. loading_free(loading);
  522. loading = NULL;
  523. flipper_http_free(app->fhttp);
  524. }
  525. else
  526. {
  527. flipper_http_free(app->fhttp);
  528. easy_flipper_dialog("[ERROR]", "Failed to scan for WiFi networks");
  529. return;
  530. }
  531. break;
  532. case FlipWiFiSubmenuIndexWiFiSaved:
  533. free_all(app);
  534. if (!alloc_submenus(app, FlipWiFiViewSubmenuSaved))
  535. {
  536. FURI_LOG_E(TAG, "Failed to allocate submenus for WiFi Saved");
  537. return;
  538. }
  539. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  540. break;
  541. case FlipWiFiSubmenuIndexAbout:
  542. free_all(app);
  543. if (!alloc_widgets(app, FlipWiFiViewAbout))
  544. {
  545. FURI_LOG_E(TAG, "Failed to allocate widget for About");
  546. return;
  547. }
  548. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewAbout);
  549. break;
  550. case FlipWiFiSubmenuIndexWiFiSavedAddSSID:
  551. free_text_inputs(app);
  552. if (!alloc_text_inputs(app, FlipWiFiViewTextInputSavedAddSSID))
  553. {
  554. FURI_LOG_E(TAG, "Failed to allocate text input for WiFi Saved Add Password");
  555. return;
  556. }
  557. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  558. break;
  559. case FlipWiFiSubmenuIndexWiFiAP:
  560. free_all(app);
  561. if (!alloc_submenus(app, FlipWiFiViewSubmenuAP))
  562. {
  563. FURI_LOG_E(TAG, "Failed to allocate submenus for APs");
  564. return;
  565. }
  566. app->fhttp = flipper_http_alloc();
  567. if (!app->fhttp)
  568. {
  569. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  570. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  571. return;
  572. }
  573. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  574. break;
  575. case FlipWiFiSubmenuIndexWiFiAPStart:
  576. // start AP
  577. view_dispatcher_set_custom_event_callback(app->view_dispatcher, callback_custom_event_callback);
  578. // send to AP View to see the responses
  579. if (!callback_run_ap_mode(app))
  580. {
  581. easy_flipper_dialog("[ERROR]", "Failed to start AP mode");
  582. return;
  583. }
  584. free_text_box(app);
  585. if (!alloc_text_box(app))
  586. {
  587. FURI_LOG_E(TAG, "Failed to allocate text box");
  588. return;
  589. }
  590. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewWiFiAP);
  591. break;
  592. case FlipWiFiSubmenuIndexWiFiAPSetSSID:
  593. // set SSID
  594. free_text_inputs(app);
  595. if (!alloc_text_inputs(app, FlipWiFiSubmenuIndexWiFiAPSetSSID))
  596. {
  597. FURI_LOG_E(TAG, "Failed to allocate text input for Fast Command");
  598. return;
  599. }
  600. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  601. break;
  602. case FlipWiFiSubmenuIndexWiFiAPSetHTML:
  603. // set HTML
  604. callback_set_html();
  605. break;
  606. case FlipWiFiSubmenuIndexCommands:
  607. free_all(app);
  608. if (!alloc_submenus(app, FlipWiFiViewSubmenuCommands))
  609. {
  610. FURI_LOG_E(TAG, "Failed to allocate submenus for Commands");
  611. return;
  612. }
  613. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  614. break;
  615. case FlipWiFiSubmenuIndexFastCommandStart ... FlipWiFiSubmenuIndexFastCommandStart + 4:
  616. // initialize uart
  617. if (!app->fhttp)
  618. {
  619. app->fhttp = flipper_http_alloc();
  620. if (!app->fhttp)
  621. {
  622. FURI_LOG_E(TAG, "Failed to allocate FlipperHTTP");
  623. easy_flipper_dialog("Error", "Failed to start UART.\nUART is likely busy or device\nis not connected.");
  624. return;
  625. }
  626. }
  627. // Handle fast commands
  628. switch (index)
  629. {
  630. case FlipWiFiSubmenuIndexFastCommandStart + 0:
  631. // CUSTOM - send to text input and return
  632. free_text_inputs(app);
  633. if (!alloc_text_inputs(app, FlipWiFiSubmenuIndexFastCommandStart))
  634. {
  635. FURI_LOG_E(TAG, "Failed to allocate text input for Fast Command");
  636. return;
  637. }
  638. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  639. return;
  640. case FlipWiFiSubmenuIndexFastCommandStart + 1:
  641. // PING
  642. flipper_http_send_command(app->fhttp, HTTP_CMD_PING);
  643. break;
  644. case FlipWiFiSubmenuIndexFastCommandStart + 2:
  645. // LIST
  646. flipper_http_send_command(app->fhttp, HTTP_CMD_LIST_COMMANDS);
  647. break;
  648. case FlipWiFiSubmenuIndexFastCommandStart + 3:
  649. // IP/ADDRESS
  650. flipper_http_send_command(app->fhttp, HTTP_CMD_IP_ADDRESS);
  651. break;
  652. case FlipWiFiSubmenuIndexFastCommandStart + 4:
  653. // WIFI/IP
  654. flipper_http_send_command(app->fhttp, HTTP_CMD_IP_WIFI);
  655. break;
  656. default:
  657. break;
  658. }
  659. while (app->fhttp->last_response == NULL || strlen(app->fhttp->last_response) == 0)
  660. {
  661. // Wait for the response
  662. furi_delay_ms(100);
  663. }
  664. if (app->fhttp->last_response != NULL)
  665. {
  666. char response[100];
  667. snprintf(response, sizeof(response), "%s", app->fhttp->last_response);
  668. easy_flipper_dialog("", response);
  669. }
  670. flipper_http_free(app->fhttp);
  671. break;
  672. case 100 ... 199:
  673. ssid_index = index - FlipWiFiSubmenuIndexWiFiScanStart;
  674. free_views(app);
  675. if (!alloc_views(app, FlipWiFiViewWiFiScan))
  676. {
  677. FURI_LOG_E(TAG, "Failed to allocate views for WiFi Scan");
  678. return;
  679. }
  680. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewGeneric);
  681. break;
  682. case 200 ... 299:
  683. ssid_index = index - FlipWiFiSubmenuIndexWiFiSavedStart;
  684. free_views(app);
  685. snprintf(current_ssid, sizeof(current_ssid), "%s", wifi_playlist->ssids[ssid_index]);
  686. snprintf(current_password, sizeof(current_password), "%s", wifi_playlist->passwords[ssid_index]);
  687. if (!alloc_views(app, FlipWiFiViewWiFiSaved))
  688. {
  689. FURI_LOG_E(TAG, "Failed to allocate views for WiFi Saved");
  690. return;
  691. }
  692. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewGeneric);
  693. break;
  694. default:
  695. break;
  696. }
  697. }
  698. void callback_text_updated_password_scan(void *context)
  699. {
  700. FlipWiFiApp *app = (FlipWiFiApp *)context;
  701. furi_check(app);
  702. // Store the entered text with buffer size limit
  703. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size - 1);
  704. // Ensure null-termination
  705. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  706. if (!alloc_playlist(app))
  707. {
  708. FURI_LOG_E(TAG, "Failed to allocate playlist");
  709. return;
  710. }
  711. // Ensure ssid_index is valid
  712. if (ssid_index >= MAX_SCAN_NETWORKS)
  713. {
  714. FURI_LOG_E(TAG, "Invalid ssid_index: %ld", ssid_index);
  715. return;
  716. }
  717. // Check if there's space in the playlist
  718. if (wifi_playlist->count >= MAX_SAVED_NETWORKS)
  719. {
  720. FURI_LOG_E(TAG, "Playlist is full. Cannot add more entries.");
  721. return;
  722. }
  723. // Add the SSID and password to the playlist
  724. snprintf(wifi_playlist->ssids[wifi_playlist->count], MAX_SSID_LENGTH, "%s", ssid_list[ssid_index]);
  725. snprintf(wifi_playlist->passwords[wifi_playlist->count], MAX_SSID_LENGTH, "%s", app->uart_text_input_buffer);
  726. wifi_playlist->count++;
  727. // Save the updated playlist to storage
  728. save_playlist(wifi_playlist);
  729. // Redraw the submenu to reflect changes
  730. callback_redraw_submenu_saved(app);
  731. // Switch back to the scan view
  732. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  733. }
  734. void callback_text_updated_password_saved(void *context)
  735. {
  736. FlipWiFiApp *app = (FlipWiFiApp *)context;
  737. furi_check(app);
  738. // store the entered text
  739. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  740. // Ensure null-termination
  741. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  742. // update the password_saved in the playlist
  743. snprintf(wifi_playlist->passwords[ssid_index], MAX_SSID_LENGTH, app->uart_text_input_buffer);
  744. // save the playlist to storage
  745. save_playlist(wifi_playlist);
  746. // switch to back to the saved view
  747. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  748. }
  749. void callback_text_updated_add_ssid(void *context)
  750. {
  751. FlipWiFiApp *app = (FlipWiFiApp *)context;
  752. furi_check(app);
  753. // check if empty
  754. if (strlen(app->uart_text_input_temp_buffer) == 0)
  755. {
  756. easy_flipper_dialog("[ERROR]", "SSID cannot be empty");
  757. return;
  758. }
  759. // store the entered text
  760. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  761. // Ensure null-termination
  762. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  763. save_char("wifi-ssid", app->uart_text_input_buffer);
  764. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
  765. uart_text_input_reset(app->uart_text_input);
  766. uart_text_input_set_header_text(app->uart_text_input, "Enter Password");
  767. app->uart_text_input_buffer_size = MAX_SSID_LENGTH;
  768. free(app->uart_text_input_buffer);
  769. free(app->uart_text_input_temp_buffer);
  770. easy_flipper_set_buffer(&app->uart_text_input_buffer, app->uart_text_input_buffer_size);
  771. easy_flipper_set_buffer(&app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  772. uart_text_input_set_result_callback(app->uart_text_input, callback_text_updated_add_password, app, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size, false);
  773. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewTextInput);
  774. }
  775. void callback_text_updated_add_password(void *context)
  776. {
  777. FlipWiFiApp *app = (FlipWiFiApp *)context;
  778. furi_check(app);
  779. // check if empty
  780. if (strlen(app->uart_text_input_temp_buffer) == 0)
  781. {
  782. easy_flipper_dialog("[ERROR]", "Password cannot be empty");
  783. return;
  784. }
  785. // store the entered text
  786. strncpy(app->uart_text_input_buffer, app->uart_text_input_temp_buffer, app->uart_text_input_buffer_size);
  787. // Ensure null-termination
  788. app->uart_text_input_buffer[app->uart_text_input_buffer_size - 1] = '\0';
  789. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenuMain);
  790. save_char("wifi-password", app->uart_text_input_buffer);
  791. char wifi_ssid[64];
  792. if (!load_char("wifi-ssid", wifi_ssid, sizeof(wifi_ssid)))
  793. {
  794. FURI_LOG_E(TAG, "Failed to load wifi ssid");
  795. return;
  796. }
  797. // add the SSID and password_scan to the playlist
  798. snprintf(wifi_playlist->ssids[wifi_playlist->count], MAX_SSID_LENGTH, wifi_ssid);
  799. snprintf(wifi_playlist->passwords[wifi_playlist->count], MAX_SSID_LENGTH, app->uart_text_input_buffer);
  800. wifi_playlist->count++;
  801. // save the playlist to storage
  802. save_playlist(wifi_playlist);
  803. callback_redraw_submenu_saved(app);
  804. // switch to back to the saved view
  805. view_dispatcher_switch_to_view(app->view_dispatcher, FlipWiFiViewSubmenu);
  806. }