flip_store_apps.c 7.0 KB

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