flip_store_apps.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. #include <apps/flip_store_apps.h>
  2. FlipStoreAppInfo *flip_catalog = NULL;
  3. uint32_t app_selected_index = 0;
  4. uint32_t flip_store_category_index = 0;
  5. int catalog_iteration = 0;
  6. // define the list of categories
  7. char *category_ids[] = {
  8. "64a69817effe1f448a4053b4", // "Bluetooth",
  9. "64971d11be1a76c06747de2f", // "Games",
  10. "64971d106617ba37a4bc79b9", // "GPIO",
  11. "64971d106617ba37a4bc79b6", // "Infrared",
  12. "64971d11be1a76c06747de29", // "iButton",
  13. "64971d116617ba37a4bc79bc", // "Media",
  14. "64971d10be1a76c06747de26", // "NFC",
  15. "64971d10577d519190ede5c2", // "RFID",
  16. "64971d0f6617ba37a4bc79b3", // "Sub-GHz",
  17. "64971d11577d519190ede5c5", // "Tools",
  18. "64971d11be1a76c06747de2c", // "USB",
  19. };
  20. char *categories[] = {
  21. "Bluetooth", // "64a69817effe1f448a4053b4"
  22. "Games", // "64971d11be1a76c06747de2f"
  23. "GPIO", // "64971d106617ba37a4bc79b9"
  24. "Infrared", // "64971d106617ba37a4bc79b6"
  25. "iButton", // "64971d11be1a76c06747de29"
  26. "Media", // "64971d116617ba37a4bc79bc"
  27. "NFC", // "64971d10be1a76c06747de26"
  28. "RFID", // "64971d10577d519190ede5c2"
  29. "Sub-GHz", // "64971d0f6617ba37a4bc79b3"
  30. "Tools", // "64971d11577d519190ede5c5"
  31. "USB", // "64971d11be1a76c06747de2c"
  32. };
  33. FlipStoreAppInfo *flip_catalog_alloc()
  34. {
  35. if (memmgr_heap_get_max_free_block() < MAX_APP_COUNT * sizeof(FlipStoreAppInfo))
  36. {
  37. FURI_LOG_E(TAG, "Not enough memory to allocate flip_catalog.");
  38. return NULL;
  39. }
  40. FlipStoreAppInfo *app_catalog = malloc(MAX_APP_COUNT * sizeof(FlipStoreAppInfo));
  41. if (!app_catalog)
  42. {
  43. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  44. return NULL;
  45. }
  46. app_catalog->count = 0;
  47. app_catalog->iteration = catalog_iteration;
  48. return app_catalog;
  49. }
  50. void flip_catalog_free()
  51. {
  52. if (flip_catalog)
  53. {
  54. free(flip_catalog);
  55. flip_catalog = NULL;
  56. }
  57. }
  58. bool flip_store_process_app_list(FlipperHTTP *fhttp)
  59. {
  60. if (!fhttp)
  61. {
  62. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  63. return false;
  64. }
  65. // Initialize the flip_catalog
  66. flip_catalog = flip_catalog_alloc();
  67. if (!flip_catalog)
  68. {
  69. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  70. return false;
  71. }
  72. FuriString *feed_data = flipper_http_load_from_file(fhttp->file_path);
  73. if (feed_data == NULL)
  74. {
  75. FURI_LOG_E(TAG, "Failed to load received data from file.");
  76. return false;
  77. }
  78. FuriString *json_data_str = furi_string_alloc();
  79. if (!json_data_str)
  80. {
  81. FURI_LOG_E("Game", "Failed to allocate json_data string");
  82. return NULL;
  83. }
  84. furi_string_cat_str(json_data_str, "{\"json_data\":");
  85. if (memmgr_heap_get_max_free_block() < furi_string_size(feed_data) + furi_string_size(json_data_str) + 2)
  86. {
  87. FURI_LOG_E(TAG, "Not enough memory to allocate json_data_str.");
  88. furi_string_free(feed_data);
  89. furi_string_free(json_data_str);
  90. return false;
  91. }
  92. furi_string_cat(json_data_str, feed_data);
  93. furi_string_free(feed_data);
  94. furi_string_cat_str(json_data_str, "}");
  95. flip_catalog->count = 0;
  96. // parse the JSON data
  97. for (int i = 0; i < MAX_APP_COUNT; i++)
  98. {
  99. FuriString *json_data_array = get_json_array_value_furi("json_data", i, json_data_str);
  100. if (!json_data_array)
  101. {
  102. break;
  103. }
  104. FuriString *app_id = get_json_value_furi("alias", json_data_array);
  105. if (!app_id)
  106. {
  107. FURI_LOG_E(TAG, "Failed to get app_id.");
  108. furi_string_free(json_data_array);
  109. break;
  110. }
  111. snprintf(flip_catalog[i].app_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(app_id));
  112. furi_string_free(app_id);
  113. FuriString *current_version = get_json_value_furi("current_version", json_data_array);
  114. if (!current_version)
  115. {
  116. FURI_LOG_E(TAG, "Failed to get current_version.");
  117. furi_string_free(json_data_array);
  118. break;
  119. }
  120. FuriString *app_name = get_json_value_furi("name", current_version);
  121. if (!app_name)
  122. {
  123. FURI_LOG_E(TAG, "Failed to get app_name.");
  124. furi_string_free(json_data_array);
  125. furi_string_free(current_version);
  126. break;
  127. }
  128. snprintf(flip_catalog[i].app_name, MAX_APP_NAME_LENGTH, "%s", furi_string_get_cstr(app_name));
  129. furi_string_free(app_name);
  130. FuriString *app_description = get_json_value_furi("short_description", current_version);
  131. if (!app_description)
  132. {
  133. FURI_LOG_E(TAG, "Failed to get app_description.");
  134. furi_string_free(json_data_array);
  135. furi_string_free(current_version);
  136. break;
  137. }
  138. snprintf(flip_catalog[i].app_description, MAX_APP_DESCRIPTION_LENGTH, "%s", furi_string_get_cstr(app_description));
  139. furi_string_free(app_description);
  140. FuriString *app_version = get_json_value_furi("version", current_version);
  141. if (!app_version)
  142. {
  143. FURI_LOG_E(TAG, "Failed to get app_version.");
  144. furi_string_free(json_data_array);
  145. furi_string_free(current_version);
  146. break;
  147. }
  148. snprintf(flip_catalog[i].app_version, MAX_APP_VERSION_LENGTH, "%s", furi_string_get_cstr(app_version));
  149. furi_string_free(app_version);
  150. FuriString *_id = get_json_value_furi("_id", current_version);
  151. if (!_id)
  152. {
  153. FURI_LOG_E(TAG, "Failed to get _id.");
  154. furi_string_free(json_data_array);
  155. furi_string_free(current_version);
  156. break;
  157. }
  158. snprintf(flip_catalog[i].app_build_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(_id));
  159. furi_string_free(_id);
  160. flip_catalog->count++;
  161. furi_string_free(json_data_array);
  162. furi_string_free(current_version);
  163. }
  164. furi_string_free(json_data_str);
  165. return flip_catalog->count > 0;
  166. }
  167. static bool flip_store_get_fap_file(FlipperHTTP *fhttp, char *build_id, uint8_t target, uint16_t api_major, uint16_t api_minor)
  168. {
  169. if (!fhttp || !build_id)
  170. {
  171. FURI_LOG_E(TAG, "FlipperHTTP or build_id is NULL.");
  172. return false;
  173. }
  174. char url[256];
  175. fhttp->save_received_data = false;
  176. fhttp->is_bytes_request = true;
  177. 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);
  178. // return flipper_http_get_request_bytes(fhttp, url, "{\"Content-Type\": \"application/octet-stream\"}");
  179. return flipper_http_request(fhttp, BYTES, url, "{\"Content-Type\": \"application/octet-stream\"}", NULL);
  180. }
  181. bool flip_store_install_app(FlipperHTTP *fhttp, char *category)
  182. {
  183. if (!fhttp || !category)
  184. {
  185. FURI_LOG_E(TAG, "FlipperHTTP or category is NULL.");
  186. return false;
  187. }
  188. snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps/%s/%s.fap", category, flip_catalog[app_selected_index].app_id);
  189. uint8_t target = furi_hal_version_get_hw_target();
  190. uint16_t api_major, api_minor;
  191. furi_hal_info_get_api_version(&api_major, &api_minor);
  192. return flip_store_get_fap_file(fhttp, flip_catalog[app_selected_index].app_build_id, target, api_major, api_minor);
  193. }