flip_trader_callback.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689
  1. #include <callback/flip_trader_callback.h>
  2. // Below added by Derek Jamison
  3. // FURI_LOG_DEV will log only during app development. Be sure that Settings/System/Log Device is "LPUART"; so we dont use serial port.
  4. #ifdef DEVELOPMENT
  5. #define FURI_LOG_DEV(tag, format, ...) \
  6. furi_log_print_format(FuriLogLevelInfo, tag, format, ##__VA_ARGS__)
  7. #define DEV_CRASH() furi_crash()
  8. #else
  9. #define FURI_LOG_DEV(tag, format, ...)
  10. #define DEV_CRASH()
  11. #endif
  12. // hold the price of the asset
  13. char asset_price[64];
  14. bool sent_get_request = false;
  15. bool get_request_success = false;
  16. bool request_processed = false;
  17. static void flip_trader_request_error_draw(Canvas* canvas) {
  18. if(canvas == NULL) {
  19. FURI_LOG_E(TAG, "flip_trader_request_error_draw - canvas is NULL");
  20. DEV_CRASH();
  21. return;
  22. }
  23. if(fhttp.last_response != NULL) {
  24. if(strstr(fhttp.last_response, "[ERROR] Not connected to Wifi. Failed to reconnect.") !=
  25. NULL) {
  26. canvas_clear(canvas);
  27. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  28. canvas_draw_str(canvas, 0, 22, "Failed to reconnect.");
  29. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  30. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  31. } else if(strstr(fhttp.last_response, "[ERROR] Failed to connect to Wifi.") != NULL) {
  32. canvas_clear(canvas);
  33. canvas_draw_str(canvas, 0, 10, "[ERROR] Not connected to Wifi.");
  34. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  35. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  36. } else if(strstr(fhttp.last_response, "[PONG]") != NULL) {
  37. canvas_clear(canvas);
  38. canvas_draw_str(canvas, 0, 10, "[STATUS]Connecting to AP...");
  39. } else {
  40. canvas_clear(canvas);
  41. FURI_LOG_E(TAG, "Received an error: %s", fhttp.last_response);
  42. canvas_draw_str(canvas, 0, 10, "[ERROR] Unusual error...");
  43. canvas_draw_str(canvas, 0, 60, "Press BACK and retry.");
  44. }
  45. } else {
  46. canvas_clear(canvas);
  47. canvas_draw_str(canvas, 0, 10, "Failed to receive data.");
  48. canvas_draw_str(canvas, 0, 50, "Update your WiFi settings.");
  49. canvas_draw_str(canvas, 0, 60, "Press BACK to return.");
  50. }
  51. }
  52. static bool send_price_request() {
  53. if(fhttp.state == INACTIVE) {
  54. return false;
  55. }
  56. if(!sent_get_request && fhttp.state == IDLE) {
  57. sent_get_request = true;
  58. char url[128];
  59. char* headers = jsmn("Content-Type", "application/json");
  60. snprintf(
  61. fhttp.file_path,
  62. sizeof(fhttp.file_path),
  63. STORAGE_EXT_PATH_PREFIX "/apps_data/flip_trader/price.txt");
  64. fhttp.save_received_data = true;
  65. snprintf(
  66. url,
  67. 128,
  68. "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=%s&apikey=2X90WLEFMP43OJKE",
  69. asset_names[asset_index]);
  70. get_request_success = flipper_http_get_request_with_headers(url, headers);
  71. free(headers);
  72. if(!get_request_success) {
  73. FURI_LOG_E(TAG, "Failed to send GET request");
  74. fhttp.state = ISSUE;
  75. return false;
  76. }
  77. fhttp.state = RECEIVING;
  78. }
  79. return true;
  80. }
  81. static char* process_asset_price() {
  82. if(!request_processed) {
  83. // load the received data from the saved file
  84. FuriString* price_data = flipper_http_load_from_file(fhttp.file_path);
  85. if(price_data == NULL) {
  86. FURI_LOG_E(TAG, "Failed to load received data from file.");
  87. fhttp.state = ISSUE;
  88. return NULL;
  89. }
  90. char* data_cstr = (char*)furi_string_get_cstr(price_data);
  91. if(data_cstr == NULL) {
  92. FURI_LOG_E(TAG, "Failed to get C-string from FuriString.");
  93. furi_string_free(price_data);
  94. fhttp.state = ISSUE;
  95. return NULL;
  96. }
  97. request_processed = true;
  98. char* global_quote = get_json_value("Global Quote", data_cstr, MAX_TOKENS);
  99. if(global_quote == NULL) {
  100. FURI_LOG_E(TAG, "Failed to get Global Quote");
  101. fhttp.state = ISSUE;
  102. furi_string_free(price_data);
  103. free(global_quote);
  104. free(data_cstr);
  105. return NULL;
  106. }
  107. char* price = get_json_value("05. price", global_quote, MAX_TOKENS);
  108. if(price == NULL) {
  109. FURI_LOG_E(TAG, "Failed to get price");
  110. fhttp.state = ISSUE;
  111. furi_string_free(price_data);
  112. free(global_quote);
  113. free(price);
  114. free(data_cstr);
  115. return NULL;
  116. }
  117. // store the price "Asset: $price"
  118. snprintf(asset_price, 64, "%s: $%s", asset_names[asset_index], price);
  119. fhttp.state = IDLE;
  120. furi_string_free(price_data);
  121. free(global_quote);
  122. free(price);
  123. free(data_cstr);
  124. }
  125. return asset_price;
  126. }
  127. static void flip_trader_asset_switch_to_view(FlipTraderApp* app) {
  128. flip_trader_generic_switch_to_view(
  129. app,
  130. "Fetching..",
  131. send_price_request,
  132. process_asset_price,
  133. 1,
  134. callback_to_assets_submenu,
  135. FlipTraderViewLoader);
  136. }
  137. void callback_submenu_choices(void* context, uint32_t index) {
  138. FlipTraderApp* app = (FlipTraderApp*)context;
  139. if(!app) {
  140. FURI_LOG_E(TAG, "FlipTraderApp is NULL");
  141. return;
  142. }
  143. switch(index) {
  144. // view the assets submenu
  145. case FlipTradeSubmenuIndexAssets:
  146. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewAssetsSubmenu);
  147. break;
  148. // view the about screen
  149. case FlipTraderSubmenuIndexAbout:
  150. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewAbout);
  151. break;
  152. // view the wifi settings screen
  153. case FlipTraderSubmenuIndexSettings:
  154. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewWiFiSettings);
  155. break;
  156. default:
  157. // handle FlipTraderSubmenuIndexAssetStartIndex + index
  158. if(index >= FlipTraderSubmenuIndexAssetStartIndex) {
  159. asset_index = index - FlipTraderSubmenuIndexAssetStartIndex;
  160. flip_trader_asset_switch_to_view(app);
  161. } else {
  162. FURI_LOG_E(TAG, "Unknown submenu index");
  163. }
  164. break;
  165. }
  166. }
  167. void text_updated_ssid(void* context) {
  168. FlipTraderApp* app = (FlipTraderApp*)context;
  169. if(!app) {
  170. FURI_LOG_E(TAG, "FlipTraderApp is NULL");
  171. return;
  172. }
  173. // store the entered text
  174. strncpy(
  175. app->uart_text_input_buffer_ssid,
  176. app->uart_text_input_temp_buffer_ssid,
  177. app->uart_text_input_buffer_size_ssid);
  178. // Ensure null-termination
  179. app->uart_text_input_buffer_ssid[app->uart_text_input_buffer_size_ssid - 1] = '\0';
  180. // update the variable item text
  181. if(app->variable_item_ssid) {
  182. variable_item_set_current_value_text(
  183. app->variable_item_ssid, app->uart_text_input_buffer_ssid);
  184. }
  185. // save settings
  186. save_settings(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_password);
  187. // save wifi settings to devboard
  188. if(strlen(app->uart_text_input_buffer_ssid) > 0 &&
  189. strlen(app->uart_text_input_buffer_password) > 0) {
  190. if(!flipper_http_save_wifi(
  191. app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_password)) {
  192. FURI_LOG_E(TAG, "Failed to save wifi settings");
  193. }
  194. }
  195. // switch to the settings view
  196. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewWiFiSettings);
  197. }
  198. void text_updated_password(void* context) {
  199. FlipTraderApp* app = (FlipTraderApp*)context;
  200. if(!app) {
  201. FURI_LOG_E(TAG, "FlipTraderApp is NULL");
  202. return;
  203. }
  204. // store the entered text
  205. strncpy(
  206. app->uart_text_input_buffer_password,
  207. app->uart_text_input_temp_buffer_password,
  208. app->uart_text_input_buffer_size_password);
  209. // Ensure null-termination
  210. app->uart_text_input_buffer_password[app->uart_text_input_buffer_size_password - 1] = '\0';
  211. // update the variable item text
  212. if(app->variable_item_password) {
  213. variable_item_set_current_value_text(
  214. app->variable_item_password, app->uart_text_input_buffer_password);
  215. }
  216. // save settings
  217. save_settings(app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_password);
  218. // save wifi settings to devboard
  219. if(strlen(app->uart_text_input_buffer_ssid) > 0 &&
  220. strlen(app->uart_text_input_buffer_password) > 0) {
  221. if(!flipper_http_save_wifi(
  222. app->uart_text_input_buffer_ssid, app->uart_text_input_buffer_password)) {
  223. FURI_LOG_E(TAG, "Failed to save wifi settings");
  224. }
  225. }
  226. // switch to the settings view
  227. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewWiFiSettings);
  228. }
  229. uint32_t callback_to_submenu(void* context) {
  230. if(!context) {
  231. FURI_LOG_E(TAG, "Context is NULL");
  232. return VIEW_NONE;
  233. }
  234. UNUSED(context);
  235. sent_get_request = false;
  236. get_request_success = false;
  237. request_processed = false;
  238. asset_index = 0;
  239. return FlipTraderViewMainSubmenu;
  240. }
  241. uint32_t callback_to_wifi_settings(void* context) {
  242. if(!context) {
  243. FURI_LOG_E(TAG, "Context is NULL");
  244. return VIEW_NONE;
  245. }
  246. UNUSED(context);
  247. return FlipTraderViewWiFiSettings;
  248. }
  249. uint32_t callback_to_assets_submenu(void* context) {
  250. if(!context) {
  251. FURI_LOG_E(TAG, "Context is NULL");
  252. return VIEW_NONE;
  253. }
  254. UNUSED(context);
  255. sent_get_request = false;
  256. get_request_success = false;
  257. request_processed = false;
  258. asset_index = 0;
  259. return FlipTraderViewAssetsSubmenu;
  260. }
  261. void settings_item_selected(void* context, uint32_t index) {
  262. FlipTraderApp* app = (FlipTraderApp*)context;
  263. if(!app) {
  264. FURI_LOG_E(TAG, "FlipTraderApp is NULL");
  265. return;
  266. }
  267. switch(index) {
  268. case 0: // Input SSID
  269. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewTextInputSSID);
  270. break;
  271. case 1: // Input Password
  272. view_dispatcher_switch_to_view(app->view_dispatcher, FlipTraderViewTextInputPassword);
  273. break;
  274. default:
  275. FURI_LOG_E(TAG, "Unknown configuration item index");
  276. break;
  277. }
  278. }
  279. static void flip_trader_widget_set_text(char* message, Widget** widget) {
  280. if(widget == NULL) {
  281. FURI_LOG_E(TAG, "flip_trader_set_widget_text - widget is NULL");
  282. DEV_CRASH();
  283. return;
  284. }
  285. if(message == NULL) {
  286. FURI_LOG_E(TAG, "flip_trader_set_widget_text - message is NULL");
  287. DEV_CRASH();
  288. return;
  289. }
  290. widget_reset(*widget);
  291. uint32_t message_length = strlen(message); // Length of the message
  292. uint32_t i = 0; // Index tracker
  293. uint32_t formatted_index = 0; // Tracker for where we are in the formatted message
  294. char* formatted_message; // Buffer to hold the final formatted message
  295. if(!easy_flipper_set_buffer(&formatted_message, message_length * 2 + 1)) {
  296. return;
  297. }
  298. while(i < message_length) {
  299. // TODO: Use canvas_glyph_width to calculate the maximum characters for the line
  300. uint32_t max_line_length = 29; // Maximum characters per line
  301. uint32_t remaining_length = message_length - i; // Remaining characters
  302. uint32_t line_length = (remaining_length < max_line_length) ? remaining_length :
  303. max_line_length;
  304. // Temporary buffer to hold the current line
  305. char line[30];
  306. strncpy(line, message + i, line_length);
  307. line[line_length] = '\0';
  308. // Check if the line ends in the middle of a word and adjust accordingly
  309. if(line_length == 29 && message[i + line_length] != '\0' &&
  310. message[i + line_length] != ' ') {
  311. // Find the last space within the 30-character segment
  312. char* last_space = strrchr(line, ' ');
  313. if(last_space != NULL) {
  314. // Adjust the line length to avoid cutting the word
  315. line_length = last_space - line;
  316. line[line_length] = '\0'; // Null-terminate at the space
  317. }
  318. }
  319. // Manually copy the fixed line into the formatted_message buffer
  320. for(uint32_t j = 0; j < line_length; j++) {
  321. formatted_message[formatted_index++] = line[j];
  322. }
  323. // Add a newline character for line spacing
  324. formatted_message[formatted_index++] = '\n';
  325. // Move i forward to the start of the next word
  326. i += line_length;
  327. // Skip spaces at the beginning of the next line
  328. while(message[i] == ' ') {
  329. i++;
  330. }
  331. }
  332. // Add the formatted message to the widget
  333. widget_add_text_scroll_element(*widget, 0, 0, 128, 64, formatted_message);
  334. }
  335. void flip_trader_loader_draw_callback(Canvas* canvas, void* model) {
  336. if(!canvas || !model) {
  337. FURI_LOG_E(TAG, "flip_trader_loader_draw_callback - canvas or model is NULL");
  338. return;
  339. }
  340. SerialState http_state = fhttp.state;
  341. AssetLoaderModel* asset_loader_model = (AssetLoaderModel*)model;
  342. AssetState asset_state = asset_loader_model->asset_state;
  343. char* title = asset_loader_model->title;
  344. canvas_set_font(canvas, FontSecondary);
  345. if(http_state == INACTIVE) {
  346. canvas_draw_str(canvas, 0, 7, "Wifi Dev Board disconnected.");
  347. canvas_draw_str(canvas, 0, 17, "Please connect to the board.");
  348. canvas_draw_str(canvas, 0, 32, "If your board is connected,");
  349. canvas_draw_str(canvas, 0, 42, "make sure you have flashed");
  350. canvas_draw_str(canvas, 0, 52, "your WiFi Devboard with the");
  351. canvas_draw_str(canvas, 0, 62, "latest FlipperHTTP flash.");
  352. return;
  353. }
  354. if(asset_state == AssetStateError || asset_state == AssetStateParseError) {
  355. flip_trader_request_error_draw(canvas);
  356. return;
  357. }
  358. canvas_draw_str(canvas, 0, 7, title);
  359. canvas_draw_str(canvas, 0, 17, "Loading...");
  360. if(asset_state == AssetStateInitial) {
  361. return;
  362. }
  363. if(http_state == SENDING) {
  364. canvas_draw_str(canvas, 0, 27, "Sending...");
  365. return;
  366. }
  367. if(http_state == RECEIVING || asset_state == AssetStateRequested) {
  368. canvas_draw_str(canvas, 0, 27, "Receiving...");
  369. return;
  370. }
  371. if(http_state == IDLE && asset_state == AssetStateReceived) {
  372. canvas_draw_str(canvas, 0, 27, "Processing...");
  373. return;
  374. }
  375. if(http_state == IDLE && asset_state == AssetStateParsed) {
  376. canvas_draw_str(canvas, 0, 27, "Processed...");
  377. return;
  378. }
  379. }
  380. static void flip_trader_loader_process_callback(void* context) {
  381. if(context == NULL) {
  382. FURI_LOG_E(TAG, "flip_trader_loader_process_callback - context is NULL");
  383. DEV_CRASH();
  384. return;
  385. }
  386. FlipTraderApp* app = (FlipTraderApp*)context;
  387. View* view = app->view_loader;
  388. AssetState current_asset_state;
  389. with_view_model(
  390. view, AssetLoaderModel * model, { current_asset_state = model->asset_state; }, false);
  391. if(current_asset_state == AssetStateInitial) {
  392. with_view_model(
  393. view,
  394. AssetLoaderModel * model,
  395. {
  396. model->asset_state = AssetStateRequested;
  397. AssetLoaderFetch fetch = model->fetcher;
  398. if(fetch == NULL) {
  399. FURI_LOG_E(TAG, "Model doesn't have Fetch function assigned.");
  400. model->asset_state = AssetStateError;
  401. return;
  402. }
  403. // Clear any previous responses
  404. strncpy(fhttp.last_response, "", 1);
  405. bool request_status = fetch(model);
  406. if(!request_status) {
  407. model->asset_state = AssetStateError;
  408. }
  409. },
  410. true);
  411. } else if(current_asset_state == AssetStateRequested || current_asset_state == AssetStateError) {
  412. if(fhttp.state == IDLE && fhttp.last_response != NULL) {
  413. if(strstr(fhttp.last_response, "[PONG]") != NULL) {
  414. FURI_LOG_DEV(TAG, "PONG received.");
  415. } else if(strncmp(fhttp.last_response, "[SUCCESS]", 9) == 0) {
  416. FURI_LOG_DEV(
  417. TAG,
  418. "SUCCESS received. %s",
  419. fhttp.last_response ? fhttp.last_response : "NULL");
  420. } else if(strncmp(fhttp.last_response, "[ERROR]", 9) == 0) {
  421. FURI_LOG_DEV(
  422. TAG, "ERROR received. %s", fhttp.last_response ? fhttp.last_response : "NULL");
  423. } else if(strlen(fhttp.last_response) == 0) {
  424. // Still waiting on response
  425. } else {
  426. with_view_model(
  427. view,
  428. AssetLoaderModel * model,
  429. { model->asset_state = AssetStateReceived; },
  430. true);
  431. }
  432. } else if(fhttp.state == SENDING || fhttp.state == RECEIVING) {
  433. // continue waiting
  434. } else if(fhttp.state == INACTIVE) {
  435. // inactive. try again
  436. } else if(fhttp.state == ISSUE) {
  437. with_view_model(
  438. view, AssetLoaderModel * model, { model->asset_state = AssetStateError; }, true);
  439. } else {
  440. FURI_LOG_DEV(
  441. TAG,
  442. "Unexpected state: %d lastresp: %s",
  443. fhttp.state,
  444. fhttp.last_response ? fhttp.last_response : "NULL");
  445. DEV_CRASH();
  446. }
  447. } else if(current_asset_state == AssetStateReceived) {
  448. with_view_model(
  449. view,
  450. AssetLoaderModel * model,
  451. {
  452. char* asset_text;
  453. if(model->parser == NULL) {
  454. asset_text = NULL;
  455. FURI_LOG_DEV(TAG, "Parser is NULL");
  456. DEV_CRASH();
  457. } else {
  458. asset_text = model->parser(model);
  459. }
  460. FURI_LOG_DEV(
  461. TAG,
  462. "Parsed asset: %s\r\ntext: %s",
  463. fhttp.last_response ? fhttp.last_response : "NULL",
  464. asset_text ? asset_text : "NULL");
  465. model->asset_text = asset_text;
  466. if(asset_text == NULL) {
  467. model->asset_state = AssetStateParseError;
  468. } else {
  469. model->asset_state = AssetStateParsed;
  470. }
  471. },
  472. true);
  473. } else if(current_asset_state == AssetStateParsed) {
  474. with_view_model(
  475. view,
  476. AssetLoaderModel * model,
  477. {
  478. if(++model->request_index < model->request_count) {
  479. model->asset_state = AssetStateInitial;
  480. } else {
  481. flip_trader_widget_set_text(
  482. model->asset_text != NULL ? model->asset_text : "Empty result",
  483. &app_instance->widget_result);
  484. if(model->asset_text != NULL) {
  485. free(model->asset_text);
  486. model->asset_text = NULL;
  487. }
  488. view_set_previous_callback(
  489. widget_get_view(app_instance->widget_result), model->back_callback);
  490. view_dispatcher_switch_to_view(
  491. app_instance->view_dispatcher, FlipTraderViewWidgetResult);
  492. }
  493. },
  494. true);
  495. }
  496. }
  497. static void flip_trader_loader_timer_callback(void* context) {
  498. if(context == NULL) {
  499. FURI_LOG_E(TAG, "flip_trader_loader_timer_callback - context is NULL");
  500. DEV_CRASH();
  501. return;
  502. }
  503. FlipTraderApp* app = (FlipTraderApp*)context;
  504. view_dispatcher_send_custom_event(app->view_dispatcher, FlipTraderCustomEventProcess);
  505. }
  506. static void flip_trader_loader_on_enter(void* context) {
  507. if(context == NULL) {
  508. FURI_LOG_E(TAG, "flip_trader_loader_on_enter - context is NULL");
  509. DEV_CRASH();
  510. return;
  511. }
  512. FlipTraderApp* app = (FlipTraderApp*)context;
  513. View* view = app->view_loader;
  514. with_view_model(
  515. view,
  516. AssetLoaderModel * model,
  517. {
  518. view_set_previous_callback(view, model->back_callback);
  519. if(model->timer == NULL) {
  520. model->timer = furi_timer_alloc(
  521. flip_trader_loader_timer_callback, FuriTimerTypePeriodic, app);
  522. }
  523. furi_timer_start(model->timer, 250);
  524. },
  525. true);
  526. }
  527. static void flip_trader_loader_on_exit(void* context) {
  528. if(context == NULL) {
  529. FURI_LOG_E(TAG, "flip_trader_loader_on_exit - context is NULL");
  530. DEV_CRASH();
  531. return;
  532. }
  533. FlipTraderApp* app = (FlipTraderApp*)context;
  534. View* view = app->view_loader;
  535. with_view_model(
  536. view,
  537. AssetLoaderModel * model,
  538. {
  539. if(model->timer) {
  540. furi_timer_stop(model->timer);
  541. }
  542. },
  543. false);
  544. }
  545. void flip_trader_loader_init(View* view) {
  546. if(view == NULL) {
  547. FURI_LOG_E(TAG, "flip_trader_loader_init - view is NULL");
  548. DEV_CRASH();
  549. return;
  550. }
  551. view_allocate_model(view, ViewModelTypeLocking, sizeof(AssetLoaderModel));
  552. view_set_enter_callback(view, flip_trader_loader_on_enter);
  553. view_set_exit_callback(view, flip_trader_loader_on_exit);
  554. }
  555. void flip_trader_loader_free_model(View* view) {
  556. if(view == NULL) {
  557. FURI_LOG_E(TAG, "flip_trader_loader_free_model - view is NULL");
  558. DEV_CRASH();
  559. return;
  560. }
  561. with_view_model(
  562. view,
  563. AssetLoaderModel * model,
  564. {
  565. if(model->timer) {
  566. furi_timer_free(model->timer);
  567. model->timer = NULL;
  568. }
  569. if(model->parser_context) {
  570. free(model->parser_context);
  571. model->parser_context = NULL;
  572. }
  573. },
  574. false);
  575. }
  576. bool flip_trader_custom_event_callback(void* context, uint32_t index) {
  577. if(context == NULL) {
  578. FURI_LOG_E(TAG, "flip_trader_custom_event_callback - context is NULL");
  579. DEV_CRASH();
  580. return false;
  581. }
  582. switch(index) {
  583. case FlipTraderCustomEventProcess:
  584. flip_trader_loader_process_callback(context);
  585. return true;
  586. default:
  587. FURI_LOG_DEV(TAG, "flip_trader_custom_event_callback. Unknown index: %ld", index);
  588. return false;
  589. }
  590. }
  591. void flip_trader_generic_switch_to_view(
  592. FlipTraderApp* app,
  593. char* title,
  594. AssetLoaderFetch fetcher,
  595. AssetLoaderParser parser,
  596. size_t request_count,
  597. ViewNavigationCallback back,
  598. uint32_t view_id) {
  599. if(app == NULL) {
  600. FURI_LOG_E(TAG, "flip_trader_generic_switch_to_view - app is NULL");
  601. DEV_CRASH();
  602. return;
  603. }
  604. View* view = app->view_loader;
  605. if(view == NULL) {
  606. FURI_LOG_E(TAG, "flip_trader_generic_switch_to_view - view is NULL");
  607. DEV_CRASH();
  608. return;
  609. }
  610. with_view_model(
  611. view,
  612. AssetLoaderModel * model,
  613. {
  614. model->title = title;
  615. model->fetcher = fetcher;
  616. model->parser = parser;
  617. model->request_index = 0;
  618. model->request_count = request_count;
  619. model->back_callback = back;
  620. model->asset_state = AssetStateInitial;
  621. model->asset_text = NULL;
  622. },
  623. true);
  624. view_dispatcher_switch_to_view(app->view_dispatcher, view_id);
  625. }