flip_store_callback.h 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. #ifndef FLIP_STORE_CALLBACK_H
  2. #define FLIP_STORE_CALLBACK_H
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include "jsmn.h"
  7. // Define maximum limits
  8. #define MAX_APP_NAME_LENGTH 50
  9. #define MAX_APP_COUNT 200
  10. typedef struct
  11. {
  12. char app_names[MAX_APP_COUNT][MAX_APP_NAME_LENGTH];
  13. char app_ids[MAX_APP_COUNT][MAX_APP_NAME_LENGTH];
  14. } FlipStoreAppCatalog;
  15. static FlipStoreAppCatalog flip_catalog;
  16. uint32_t app_selected_index = 0;
  17. bool flip_store_sent_request = false;
  18. bool flip_store_success = false;
  19. bool flip_store_saved_data = false;
  20. bool flip_store_saved_success = false;
  21. // Helper function to compare JSON keys
  22. int jsoneq(const char *json, jsmntok_t *tok, const char *s)
  23. {
  24. if (tok->type == JSMN_STRING &&
  25. (int)strlen(s) == tok->end - tok->start &&
  26. strncmp(json + tok->start, s, tok->end - tok->start) == 0)
  27. {
  28. return 0;
  29. }
  30. return -1;
  31. }
  32. // Function to clean app name string
  33. void clean_app_name(char *name)
  34. {
  35. // Remove leading and trailing whitespace (if needed)
  36. char *end;
  37. while (isspace((unsigned char)*name))
  38. name++; // Trim leading
  39. end = name + strlen(name) - 1;
  40. while (end > name && isspace((unsigned char)*end))
  41. end--; // Trim trailing
  42. *(end + 1) = '\0'; // Null terminate
  43. }
  44. // Function to skip tokens correctly
  45. int skip_tokens(jsmntok_t *tokens, int index, int total_tokens)
  46. {
  47. int skip = 1; // Start with 1 to skip the current token
  48. int child_count = tokens[index].size;
  49. for (int i = 0; i < child_count; i++)
  50. {
  51. if ((index + skip) >= total_tokens)
  52. break;
  53. skip += skip_tokens(tokens, index + skip, total_tokens);
  54. }
  55. return skip;
  56. }
  57. bool flip_store_process_app_list(char *json_data)
  58. {
  59. if (json_data == NULL)
  60. {
  61. FURI_LOG_E(TAG, "JSON data is NULL.");
  62. return false;
  63. }
  64. jsmn_parser parser;
  65. jsmn_init(&parser);
  66. // Initial token allocation
  67. int token_count = 128;
  68. jsmntok_t *tokens = (jsmntok_t *)malloc(sizeof(jsmntok_t) * token_count);
  69. if (tokens == NULL)
  70. {
  71. FURI_LOG_E(TAG, "Failed to allocate memory for JSON tokens.");
  72. return false;
  73. }
  74. int ret = jsmn_parse(&parser, json_data, strlen(json_data), tokens, token_count);
  75. // Reallocate tokens if needed
  76. while (ret == JSMN_ERROR_NOMEM)
  77. {
  78. token_count *= 2;
  79. jsmntok_t *new_tokens = (jsmntok_t *)realloc(tokens, sizeof(jsmntok_t) * token_count);
  80. if (new_tokens == NULL)
  81. {
  82. FURI_LOG_E(TAG, "Failed to reallocate memory for JSON tokens.");
  83. free(tokens);
  84. return false;
  85. }
  86. tokens = new_tokens;
  87. ret = jsmn_parse(&parser, json_data, strlen(json_data), tokens, token_count);
  88. }
  89. if (ret < 0)
  90. {
  91. FURI_LOG_E(TAG, "Failed to parse JSON: %d", ret);
  92. free(tokens);
  93. return false;
  94. }
  95. if (ret < 1 || tokens[0].type != JSMN_OBJECT)
  96. {
  97. FURI_LOG_E(TAG, "Root element is not an object.");
  98. free(tokens);
  99. return false;
  100. }
  101. int app_count = 0;
  102. int i = 1;
  103. while (i < ret)
  104. {
  105. if (jsoneq(json_data, &tokens[i], "apps") == 0)
  106. {
  107. jsmntok_t *apps_array = &tokens[i + 1];
  108. if (apps_array->type != JSMN_ARRAY)
  109. {
  110. FURI_LOG_E(TAG, "\"apps\" is not an array.");
  111. free(tokens);
  112. return false;
  113. }
  114. int current = i + 2;
  115. for (int j = 0; j < apps_array->size; j++)
  116. {
  117. if (current >= ret)
  118. {
  119. FURI_LOG_E(TAG, "Token index out of bounds while accessing apps.");
  120. break;
  121. }
  122. jsmntok_t *app_token = &tokens[current];
  123. if (app_token->type != JSMN_OBJECT)
  124. {
  125. FURI_LOG_E(TAG, "App entry is not an object.");
  126. current++;
  127. continue;
  128. }
  129. int app_size = app_token->size;
  130. int app_token_index = current + 1;
  131. char name_value[MAX_APP_NAME_LENGTH] = {0};
  132. char id_value[MAX_APP_NAME_LENGTH] = {0};
  133. for (int k = 0; k < app_size; k++)
  134. {
  135. if (app_token_index + 1 >= ret)
  136. {
  137. FURI_LOG_E(TAG, "Token index out of bounds while accessing app properties.");
  138. break;
  139. }
  140. jsmntok_t *key_token = &tokens[app_token_index];
  141. jsmntok_t *val_token = &tokens[app_token_index + 1];
  142. int key_length = key_token->end - key_token->start;
  143. if (key_length >= MAX_APP_NAME_LENGTH)
  144. key_length = MAX_APP_NAME_LENGTH - 1;
  145. char key_string[MAX_APP_NAME_LENGTH];
  146. strncpy(key_string, json_data + key_token->start, key_length);
  147. key_string[key_length] = '\0';
  148. if (jsoneq(json_data, key_token, "name") == 0)
  149. {
  150. int val_length = val_token->end - val_token->start;
  151. if (val_length >= MAX_APP_NAME_LENGTH)
  152. val_length = MAX_APP_NAME_LENGTH - 1;
  153. strncpy(name_value, json_data + val_token->start, val_length);
  154. name_value[val_length] = '\0';
  155. }
  156. else if (jsoneq(json_data, key_token, "id") == 0)
  157. {
  158. int val_length = val_token->end - val_token->start;
  159. if (val_length >= MAX_APP_NAME_LENGTH)
  160. val_length = MAX_APP_NAME_LENGTH - 1;
  161. strncpy(id_value, json_data + val_token->start, val_length);
  162. id_value[val_length] = '\0';
  163. }
  164. app_token_index += 2;
  165. }
  166. if (app_count >= MAX_APP_COUNT)
  167. {
  168. FURI_LOG_E(TAG, "Reached maximum app count limit.");
  169. break;
  170. }
  171. strncpy(flip_catalog.app_names[app_count], name_value, MAX_APP_NAME_LENGTH - 1);
  172. flip_catalog.app_names[app_count][MAX_APP_NAME_LENGTH - 1] = '\0';
  173. strncpy(flip_catalog.app_ids[app_count], id_value, MAX_APP_NAME_LENGTH - 1);
  174. flip_catalog.app_ids[app_count][MAX_APP_NAME_LENGTH - 1] = '\0';
  175. app_count++;
  176. int tokens_to_skip = 1 + 2 * app_size;
  177. current += tokens_to_skip;
  178. }
  179. break;
  180. }
  181. else
  182. {
  183. i += 2;
  184. }
  185. }
  186. free(tokens);
  187. return true;
  188. }
  189. bool flip_store_get_fap_file(char *app_id)
  190. {
  191. char payload[256];
  192. snprintf(payload, sizeof(payload), "{\"app_id\":\"%s\"}", app_id);
  193. return flipper_http_post_request_bytes("https://www.flipsocial.net/api/app/compile/", "{\"Content-Type\":\"application/json\"}", payload);
  194. }
  195. void flip_store_request_error(Canvas *canvas)
  196. {
  197. if (fhttp.received_data == NULL)
  198. {
  199. if (fhttp.last_response != NULL)
  200. {
  201. if (strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") != NULL)
  202. {
  203. canvas_clear(canvas);
  204. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  205. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  206. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  207. }
  208. else if (strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL)
  209. {
  210. canvas_clear(canvas);
  211. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  212. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  213. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  214. }
  215. else
  216. {
  217. FURI_LOG_E(TAG, "Received an error: %s", fhttp.last_response);
  218. canvas_draw_str(canvas, 0, 42, "Unusual error...");
  219. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  220. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  221. }
  222. }
  223. else
  224. {
  225. canvas_clear(canvas);
  226. canvas_draw_str(canvas, 0, 10, "[ERROR] Unknown error.");
  227. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  228. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  229. }
  230. }
  231. else
  232. {
  233. canvas_clear(canvas);
  234. canvas_draw_str(canvas, 0, 10, "Failed to receive data.");
  235. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  236. }
  237. }
  238. // function to handle the entire installation process "asynchronously"
  239. bool flip_store_install_app(Canvas *canvas)
  240. {
  241. // create /apps/FlipStore directory if it doesn't exist
  242. char directory_path[256];
  243. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps/FlipStore");
  244. // Create the directory
  245. Storage *storage = furi_record_open(RECORD_STORAGE);
  246. storage_common_mkdir(storage, directory_path);
  247. char *app_name = flip_catalog.app_names[app_selected_index];
  248. char installaing_text[128];
  249. snprintf(installaing_text, sizeof(installaing_text), "Installing %s", app_name);
  250. char bin_path[256];
  251. snprintf(bin_path, sizeof(bin_path), STORAGE_EXT_PATH_PREFIX "/apps/FlipStore/%s.fap", flip_catalog.app_ids[app_selected_index]);
  252. strncpy(fhttp.file_path, bin_path, sizeof(fhttp.file_path) - 1);
  253. canvas_draw_str(canvas, 0, 10, installaing_text);
  254. canvas_draw_str(canvas, 0, 20, "Sending reqeuest..");
  255. if (fhttp.state != INACTIVE && flip_store_get_fap_file(flip_catalog.app_ids[app_selected_index]))
  256. {
  257. canvas_draw_str(canvas, 0, 30, "Request sent.");
  258. fhttp.state = RECEIVING;
  259. // furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  260. canvas_draw_str(canvas, 0, 40, "Receiving...");
  261. }
  262. else
  263. {
  264. FURI_LOG_E(TAG, "Failed to send the request");
  265. flip_store_success = false;
  266. return false;
  267. }
  268. while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
  269. {
  270. // Wait for the feed to be received
  271. // furi_delay_ms(100);
  272. }
  273. // furi_timer_stop(fhttp.get_timeout_timer);
  274. if (fhttp.state == ISSUE || fhttp.received_data == NULL)
  275. {
  276. flip_store_request_error(canvas);
  277. flip_store_success = false;
  278. return false;
  279. }
  280. flip_store_success = true;
  281. return true;
  282. }
  283. // Callback for drawing the main screen
  284. static void flip_store_view_draw_callback_main(Canvas *canvas, void *model)
  285. {
  286. UNUSED(model);
  287. canvas_set_font(canvas, FontSecondary);
  288. if (fhttp.state == INACTIVE)
  289. {
  290. canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected.");
  291. canvas_draw_str(canvas, 0, 17, "Please connect to the board.");
  292. canvas_draw_str(canvas, 0, 32, "If you board is connected,");
  293. canvas_draw_str(canvas, 0, 42, "make sure you have flashed");
  294. canvas_draw_str(canvas, 0, 52, "your Dev Board with the");
  295. canvas_draw_str(canvas, 0, 62, "FlipperHTTP firmware.");
  296. return;
  297. }
  298. if (!flip_store_sent_request)
  299. {
  300. flip_store_sent_request = true;
  301. if (!flip_store_install_app(canvas))
  302. {
  303. canvas_clear(canvas);
  304. canvas_draw_str(canvas, 0, 10, "Failed to install app.");
  305. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  306. }
  307. else
  308. {
  309. canvas_clear(canvas);
  310. canvas_draw_str(canvas, 0, 10, "App installed successfully.");
  311. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  312. }
  313. }
  314. else
  315. {
  316. if (flip_store_success)
  317. {
  318. canvas_clear(canvas);
  319. canvas_draw_str(canvas, 0, 10, "App installed successfully.");
  320. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  321. }
  322. else
  323. {
  324. canvas_clear(canvas);
  325. canvas_draw_str(canvas, 0, 10, "Failed to install app.");
  326. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  327. }
  328. }
  329. }
  330. static void flip_store_view_draw_callback_app_list(Canvas *canvas, void *model)
  331. {
  332. UNUSED(model);
  333. canvas_clear(canvas);
  334. canvas_set_font(canvas, FontPrimary);
  335. canvas_draw_str(canvas, 0, 10, flip_catalog.app_names[app_selected_index]);
  336. // canvas_draw_icon(canvas, 0, 53, &I_ButtonLeft_4x7); (future implementation)
  337. // canvas_draw_str_aligned(canvas, 7, 54, AlignLeft, AlignTop, "Delete"); (future implementation)
  338. canvas_draw_icon(canvas, 0, 53, &I_ButtonBACK_10x8);
  339. canvas_draw_str_aligned(canvas, 12, 54, AlignLeft, AlignTop, "Back");
  340. canvas_draw_icon(canvas, 90, 53, &I_ButtonRight_4x7);
  341. canvas_draw_str_aligned(canvas, 97, 54, AlignLeft, AlignTop, "Install");
  342. }
  343. static bool flip_store_input_callback(InputEvent *event, void *context)
  344. {
  345. FlipStoreApp *app = (FlipStoreApp *)context;
  346. if (!app)
  347. {
  348. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  349. return false;
  350. }
  351. if (event->type == InputTypeShort)
  352. {
  353. // Future implementation
  354. // if (event->key == InputKeyLeft)
  355. //{
  356. // Left button clicked, delete the app with DialogEx confirmation
  357. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppDelete);
  358. // return true;
  359. //}
  360. if (event->key == InputKeyRight)
  361. {
  362. // Right button clicked, download the app
  363. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewMain);
  364. return true;
  365. }
  366. }
  367. else if (event->type == InputTypePress)
  368. {
  369. if (event->key == InputKeyBack)
  370. {
  371. // Back button clicked, switch to the previous view.
  372. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
  373. return true;
  374. }
  375. }
  376. return false;
  377. }
  378. static void flip_store_text_updated_ssid(void *context)
  379. {
  380. FlipStoreApp *app = (FlipStoreApp *)context;
  381. if (!app)
  382. {
  383. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  384. return;
  385. }
  386. // store the entered text
  387. strncpy(app->uart_text_input_buffer_ssid, app->uart_text_input_temp_buffer_ssid, app->uart_text_input_buffer_size_ssid);
  388. // Ensure null-termination
  389. app->uart_text_input_buffer_ssid[app->uart_text_input_buffer_size_ssid - 1] = '\0';
  390. // update the variable item text
  391. if (app->variable_item_ssid)
  392. {
  393. variable_item_set_current_value_text(app->variable_item_ssid, app->uart_text_input_buffer_ssid);
  394. }
  395. // save the settings
  396. save_settings(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_pass);
  397. // if SSID and PASS are not empty, connect to the WiFi
  398. if (strlen(app->uart_text_input_buffer_ssid) > 0 && strlen(app->uart_text_input_buffer_pass) > 0)
  399. {
  400. // save wifi settings
  401. if (!flipper_http_save_wifi(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_pass))
  402. {
  403. FURI_LOG_E(TAG, "Failed to save WiFi settings");
  404. }
  405. }
  406. // switch to the settings view
  407. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSettings);
  408. }
  409. static void flip_store_text_updated_pass(void *context)
  410. {
  411. FlipStoreApp *app = (FlipStoreApp *)context;
  412. if (!app)
  413. {
  414. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  415. return;
  416. }
  417. // store the entered text
  418. strncpy(app->uart_text_input_buffer_pass, app->uart_text_input_temp_buffer_pass, app->uart_text_input_buffer_size_pass);
  419. // Ensure null-termination
  420. app->uart_text_input_buffer_pass[app->uart_text_input_buffer_size_pass - 1] = '\0';
  421. // update the variable item text
  422. if (app->variable_item_pass)
  423. {
  424. variable_item_set_current_value_text(app->variable_item_pass, app->uart_text_input_buffer_pass);
  425. }
  426. // save the settings
  427. save_settings(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_pass);
  428. // if SSID and PASS are not empty, connect to the WiFi
  429. if (strlen(app->uart_text_input_buffer_ssid) > 0 && strlen(app->uart_text_input_buffer_pass) > 0)
  430. {
  431. // save wifi settings
  432. if (!flipper_http_save_wifi(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_pass))
  433. {
  434. FURI_LOG_E(TAG, "Failed to save WiFi settings");
  435. }
  436. }
  437. // switch to the settings view
  438. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSettings);
  439. }
  440. static uint32_t callback_to_submenu(void *context)
  441. {
  442. if (!context)
  443. {
  444. FURI_LOG_E(TAG, "Context is NULL");
  445. return VIEW_NONE;
  446. }
  447. UNUSED(context);
  448. return FlipStoreViewSubmenu;
  449. }
  450. static uint32_t callback_to_app_list(void *context)
  451. {
  452. if (!context)
  453. {
  454. FURI_LOG_E(TAG, "Context is NULL");
  455. return VIEW_NONE;
  456. }
  457. UNUSED(context);
  458. flip_store_sent_request = false;
  459. flip_store_success = false;
  460. flip_store_saved_data = false;
  461. flip_store_saved_success = false;
  462. return FlipStoreViewAppList;
  463. }
  464. static void settings_item_selected(void *context, uint32_t index)
  465. {
  466. FlipStoreApp *app = (FlipStoreApp *)context;
  467. if (!app)
  468. {
  469. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  470. return;
  471. }
  472. switch (index)
  473. {
  474. case 0: // Input SSID
  475. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewTextInputSSID);
  476. break;
  477. case 1: // Input Password
  478. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewTextInputPass);
  479. break;
  480. default:
  481. FURI_LOG_E(TAG, "Unknown configuration item index");
  482. break;
  483. }
  484. }
  485. void dialog_callback(DialogExResult result, void *context)
  486. {
  487. furi_assert(context);
  488. FlipStoreApp *app = (FlipStoreApp *)context;
  489. if (result == DialogExResultLeft) // No
  490. {
  491. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
  492. }
  493. else if (result == DialogExResultRight)
  494. {
  495. // delete the app then return to the app list
  496. // pop up a message
  497. popup_set_header(app->popup, "Success", 0, 0, AlignLeft, AlignTop);
  498. popup_set_text(app->popup, "App deleted successfully.", 0, 60, AlignLeft, AlignTop);
  499. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
  500. furi_delay_ms(2000); // delay for 2 seconds
  501. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
  502. }
  503. }
  504. void popup_callback(void *context)
  505. {
  506. FlipStoreApp *app = (FlipStoreApp *)context;
  507. if (!app)
  508. {
  509. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  510. return;
  511. }
  512. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSubmenu);
  513. }
  514. /**
  515. * @brief Navigation callback for exiting the application
  516. * @param context The context - unused
  517. * @return next view id (VIEW_NONE to exit the app)
  518. */
  519. static uint32_t callback_exit_app(void *context)
  520. {
  521. // Exit the application
  522. if (!context)
  523. {
  524. FURI_LOG_E(TAG, "Context is NULL");
  525. return VIEW_NONE;
  526. }
  527. UNUSED(context);
  528. return VIEW_NONE; // Return VIEW_NONE to exit the app
  529. }
  530. static void callback_submenu_choices(void *context, uint32_t index)
  531. {
  532. FlipStoreApp *app = (FlipStoreApp *)context;
  533. if (!app)
  534. {
  535. FURI_LOG_E(TAG, "FlipStoreApp is NULL");
  536. return;
  537. }
  538. switch (index)
  539. {
  540. case FlipStoreSubmenuIndexMain:
  541. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewMain);
  542. break;
  543. case FlipStoreSubmenuIndexAbout:
  544. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAbout);
  545. break;
  546. case FlipStoreSubmenuIndexSettings:
  547. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewSettings);
  548. break;
  549. case FlipStoreSubmenuIndexDownloadApp:
  550. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
  551. break;
  552. // Ideally users should be sent to a draw callback view to show to request process (like in FlipSocial and WebCrawler)
  553. case FlipStoreSubmenuIndexAppList:
  554. // async call to the app list with timer
  555. if (fhttp.state != INACTIVE && flipper_http_get_request_with_headers("https://www.flipsocial.net/api/flipper/apps/", "{\"Content-Type\":\"application/json\"}"))
  556. {
  557. furi_timer_start(fhttp.get_timeout_timer, TIMEOUT_DURATION_TICKS);
  558. fhttp.state = RECEIVING;
  559. }
  560. else
  561. {
  562. FURI_LOG_E(TAG, "Failed to send the request");
  563. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
  564. return;
  565. }
  566. while (fhttp.state == RECEIVING && furi_timer_is_running(fhttp.get_timeout_timer) > 0)
  567. {
  568. // Wait for the feed to be received
  569. furi_delay_ms(100);
  570. }
  571. furi_timer_stop(fhttp.get_timeout_timer);
  572. if (fhttp.state == ISSUE || fhttp.received_data == NULL)
  573. {
  574. if (fhttp.received_data == NULL)
  575. {
  576. FURI_LOG_E(TAG, "Failed to receive data");
  577. if (fhttp.last_response != NULL)
  578. {
  579. if (strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") != NULL)
  580. {
  581. popup_set_text(app->popup, "[ERROR] WiFi Disconnected.\n\n\nUpdate your WiFi settings.\nPress BACK to return.", 0, 10, AlignLeft, AlignTop);
  582. }
  583. else if (strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL)
  584. {
  585. popup_set_text(app->popup, "[ERROR] WiFi Disconnected.\n\n\nUpdate your WiFi settings.\nPress BACK to return.", 0, 10, AlignLeft, AlignTop);
  586. }
  587. else
  588. {
  589. popup_set_text(app->popup, fhttp.last_response, 0, 10, AlignLeft, AlignTop);
  590. }
  591. }
  592. else
  593. {
  594. popup_set_text(app->popup, "[ERROR] Unknown Error.\n\n\nUpdate your WiFi settings.\nPress BACK to return.", 0, 10, AlignLeft, AlignTop);
  595. }
  596. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
  597. return;
  598. }
  599. else
  600. {
  601. FURI_LOG_E(TAG, "Failed to receive data");
  602. popup_set_text(app->popup, "Failed to received data.", 0, 10, AlignLeft, AlignTop);
  603. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
  604. return;
  605. }
  606. }
  607. else
  608. {
  609. // process the app list
  610. if (flip_store_process_app_list(fhttp.received_data))
  611. {
  612. submenu_reset(app->submenu_app_list); // clear the submenu
  613. // add each app name to submenu
  614. for (int i = 0; i < MAX_APP_COUNT; i++)
  615. {
  616. if (strlen(flip_catalog.app_names[i]) > 0)
  617. {
  618. submenu_add_item(app->submenu_app_list, flip_catalog.app_names[i], FlipStoreSubmenuIndexStartAppList + i, callback_submenu_choices, app);
  619. }
  620. }
  621. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppList);
  622. }
  623. else
  624. {
  625. FURI_LOG_E(TAG, "Failed to process the app list");
  626. popup_set_text(app->popup, "Failed to process the app list", 0, 10, AlignLeft, AlignTop);
  627. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewPopup);
  628. return;
  629. }
  630. }
  631. break;
  632. default:
  633. // Check if the index is within the app list range
  634. if (index >= FlipStoreSubmenuIndexStartAppList && index < FlipStoreSubmenuIndexStartAppList + MAX_APP_COUNT)
  635. {
  636. // Get the app index
  637. uint32_t app_index = index - FlipStoreSubmenuIndexStartAppList;
  638. // Check if the app index is valid
  639. if ((int)app_index >= 0 && app_index < MAX_APP_COUNT)
  640. {
  641. // Get the app name
  642. char *app_name = flip_catalog.app_names[app_index];
  643. // Check if the app name is valid
  644. if (app_name != NULL && strlen(app_name) > 0)
  645. {
  646. app_selected_index = app_index;
  647. view_dispatcher_switch_to_view(app->view_dispatcher, FlipStoreViewAppInfo);
  648. }
  649. else
  650. {
  651. FURI_LOG_E(TAG, "Invalid app name");
  652. }
  653. }
  654. else
  655. {
  656. FURI_LOG_E(TAG, "Invalid app index");
  657. }
  658. }
  659. else
  660. {
  661. FURI_LOG_E(TAG, "Unknown submenu index");
  662. }
  663. break;
  664. }
  665. }
  666. #endif // FLIP_STORE_CALLBACK_H