flip_store_apps.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. #include <apps/flip_store_apps.h>
  2. FlipStoreAppInfo *flip_catalog = NULL;
  3. uint32_t app_selected_index = 0;
  4. bool flip_store_sent_request = false;
  5. bool flip_store_success = false;
  6. bool flip_store_saved_data = false;
  7. bool flip_store_saved_success = false;
  8. uint32_t flip_store_category_index = 0;
  9. // define the list of categories
  10. char *category_ids[] = {
  11. "64a69817effe1f448a4053b4", // "Bluetooth",
  12. "64971d11be1a76c06747de2f", // "Games",
  13. "64971d106617ba37a4bc79b9", // "GPIO",
  14. "64971d106617ba37a4bc79b6", // "Infrared",
  15. "64971d11be1a76c06747de29", // "iButton",
  16. "64971d116617ba37a4bc79bc", // "Media",
  17. "64971d10be1a76c06747de26", // "NFC",
  18. "64971d10577d519190ede5c2", // "RFID",
  19. "64971d0f6617ba37a4bc79b3", // "Sub-GHz",
  20. "64971d11577d519190ede5c5", // "Tools",
  21. "64971d11be1a76c06747de2c", // "USB",
  22. };
  23. char *categories[] = {
  24. "Bluetooth", // "64a69817effe1f448a4053b4"
  25. "Games", // "64971d11be1a76c06747de2f"
  26. "GPIO", // "64971d106617ba37a4bc79b9"
  27. "Infrared", // "64971d106617ba37a4bc79b6"
  28. "iButton", // "64971d11be1a76c06747de29"
  29. "Media", // "64971d116617ba37a4bc79bc"
  30. "NFC", // "64971d10be1a76c06747de26"
  31. "RFID", // "64971d10577d519190ede5c2"
  32. "Sub-GHz", // "64971d0f6617ba37a4bc79b3"
  33. "Tools", // "64971d11577d519190ede5c5"
  34. "USB", // "64971d11be1a76c06747de2c"
  35. };
  36. FlipStoreAppInfo *flip_catalog_alloc()
  37. {
  38. if (memmgr_get_free_heap() < MAX_APP_COUNT * sizeof(FlipStoreAppInfo))
  39. {
  40. FURI_LOG_E(TAG, "Not enough memory to allocate flip_catalog.");
  41. return NULL;
  42. }
  43. FlipStoreAppInfo *app_catalog = malloc(MAX_APP_COUNT * sizeof(FlipStoreAppInfo));
  44. if (!app_catalog)
  45. {
  46. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  47. return NULL;
  48. }
  49. // No need for a loop since all memory is allocated in one block
  50. return app_catalog;
  51. }
  52. void flip_catalog_free()
  53. {
  54. if (flip_catalog)
  55. {
  56. free(flip_catalog);
  57. flip_catalog = NULL;
  58. }
  59. }
  60. bool flip_store_process_app_list(FlipperHTTP *fhttp)
  61. {
  62. if (!fhttp)
  63. {
  64. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  65. return false;
  66. }
  67. // Initialize the flip_catalog
  68. flip_catalog = flip_catalog_alloc();
  69. if (!flip_catalog)
  70. {
  71. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  72. return false;
  73. }
  74. FuriString *feed_data = flipper_http_load_from_file(fhttp->file_path);
  75. if (feed_data == NULL)
  76. {
  77. FURI_LOG_E(TAG, "Failed to load received data from file.");
  78. return false;
  79. }
  80. FuriString *json_data_str = furi_string_alloc();
  81. if (!json_data_str)
  82. {
  83. FURI_LOG_E("Game", "Failed to allocate json_data string");
  84. return NULL;
  85. }
  86. furi_string_cat_str(json_data_str, "{\"json_data\":");
  87. if (memmgr_get_free_heap() < furi_string_size(feed_data) + furi_string_size(json_data_str) + 2)
  88. {
  89. FURI_LOG_E(TAG, "Not enough memory to allocate json_data_str.");
  90. furi_string_free(feed_data);
  91. furi_string_free(json_data_str);
  92. return false;
  93. }
  94. furi_string_cat(json_data_str, feed_data);
  95. furi_string_free(feed_data);
  96. furi_string_cat_str(json_data_str, "}");
  97. int app_count = 0;
  98. // parse the JSON data
  99. for (int i = 0; i < MAX_APP_COUNT; i++)
  100. {
  101. FuriString *json_data_array = get_json_array_value_furi("json_data", i, json_data_str);
  102. if (!json_data_array)
  103. {
  104. break;
  105. }
  106. FuriString *app_id = get_json_value_furi("alias", json_data_array);
  107. if (!app_id)
  108. {
  109. FURI_LOG_E(TAG, "Failed to get app_id.");
  110. furi_string_free(json_data_array);
  111. break;
  112. }
  113. snprintf(flip_catalog[i].app_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(app_id));
  114. furi_string_free(app_id);
  115. FuriString *current_version = get_json_value_furi("current_version", json_data_array);
  116. if (!current_version)
  117. {
  118. FURI_LOG_E(TAG, "Failed to get current_version.");
  119. furi_string_free(json_data_array);
  120. break;
  121. }
  122. FuriString *app_name = get_json_value_furi("name", current_version);
  123. if (!app_name)
  124. {
  125. FURI_LOG_E(TAG, "Failed to get app_name.");
  126. furi_string_free(json_data_array);
  127. furi_string_free(current_version);
  128. break;
  129. }
  130. snprintf(flip_catalog[i].app_name, MAX_APP_NAME_LENGTH, "%s", furi_string_get_cstr(app_name));
  131. furi_string_free(app_name);
  132. FuriString *app_description = get_json_value_furi("short_description", current_version);
  133. if (!app_description)
  134. {
  135. FURI_LOG_E(TAG, "Failed to get app_description.");
  136. furi_string_free(json_data_array);
  137. furi_string_free(current_version);
  138. break;
  139. }
  140. snprintf(flip_catalog[i].app_description, MAX_APP_DESCRIPTION_LENGTH, "%s", furi_string_get_cstr(app_description));
  141. furi_string_free(app_description);
  142. FuriString *app_version = get_json_value_furi("version", current_version);
  143. if (!app_version)
  144. {
  145. FURI_LOG_E(TAG, "Failed to get app_version.");
  146. furi_string_free(json_data_array);
  147. furi_string_free(current_version);
  148. break;
  149. }
  150. snprintf(flip_catalog[i].app_version, MAX_APP_VERSION_LENGTH, "%s", furi_string_get_cstr(app_version));
  151. furi_string_free(app_version);
  152. FuriString *_id = get_json_value_furi("_id", current_version);
  153. if (!_id)
  154. {
  155. FURI_LOG_E(TAG, "Failed to get _id.");
  156. furi_string_free(json_data_array);
  157. furi_string_free(current_version);
  158. break;
  159. }
  160. snprintf(flip_catalog[i].app_build_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(_id));
  161. furi_string_free(_id);
  162. app_count++;
  163. furi_string_free(json_data_array);
  164. furi_string_free(current_version);
  165. }
  166. furi_string_free(json_data_str);
  167. return app_count > 0;
  168. }
  169. bool flip_store_get_fap_file(FlipperHTTP *fhttp, char *build_id, uint8_t target, uint16_t api_major, uint16_t api_minor)
  170. {
  171. if (!fhttp)
  172. {
  173. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  174. return false;
  175. }
  176. if (!build_id)
  177. {
  178. FURI_LOG_E(TAG, "Build ID is NULL.");
  179. return false;
  180. }
  181. fhttp->state = IDLE;
  182. char url[128];
  183. fhttp->save_received_data = false;
  184. fhttp->is_bytes_request = true;
  185. snprintf(url, sizeof(url), "https://catalog.flipperzero.one/api/v0/application/version/%s/build/compatible?target=f%d&api=%d.%d", build_id, target, api_major, api_minor);
  186. return flipper_http_get_request_bytes(fhttp, url, "{\"Content-Type\": \"application/octet-stream\"}");
  187. }
  188. bool flip_store_install_app(FlipperHTTP *fhttp, char *category)
  189. {
  190. if (!fhttp)
  191. {
  192. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  193. return false;
  194. }
  195. // create /apps/FlipStore directory if it doesn't exist
  196. char directory_path[128];
  197. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps/%s", category);
  198. // Create the directory
  199. Storage *storage = furi_record_open(RECORD_STORAGE);
  200. storage_common_mkdir(storage, directory_path);
  201. furi_record_close(RECORD_STORAGE);
  202. snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps/%s/%s.fap", category, flip_catalog[app_selected_index].app_id);
  203. uint8_t target = furi_hal_version_get_hw_target();
  204. uint16_t api_major, api_minor;
  205. furi_hal_info_get_api_version(&api_major, &api_minor);
  206. if (fhttp->state != INACTIVE && flip_store_get_fap_file(fhttp, flip_catalog[app_selected_index].app_build_id, target, api_major, api_minor))
  207. {
  208. fhttp->state = RECEIVING;
  209. return true;
  210. }
  211. else
  212. {
  213. FURI_LOG_E(TAG, "Failed to send the request");
  214. flip_store_success = false;
  215. return false;
  216. }
  217. }