flip_store_apps.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  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. FlipStoreAppInfo *app_catalog = malloc(MAX_APP_COUNT * sizeof(FlipStoreAppInfo));
  39. if (!app_catalog)
  40. {
  41. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  42. return NULL;
  43. }
  44. // No need for a loop since all memory is allocated in one block
  45. return app_catalog;
  46. }
  47. void flip_catalog_free()
  48. {
  49. if (flip_catalog)
  50. {
  51. free(flip_catalog);
  52. flip_catalog = NULL;
  53. }
  54. }
  55. bool flip_store_process_app_list(FlipperHTTP *fhttp)
  56. {
  57. if (!fhttp)
  58. {
  59. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  60. return false;
  61. }
  62. // Initialize the flip_catalog
  63. flip_catalog = flip_catalog_alloc();
  64. if (!flip_catalog)
  65. {
  66. FURI_LOG_E(TAG, "Failed to allocate memory for flip_catalog.");
  67. return false;
  68. }
  69. FuriString *feed_data = flipper_http_load_from_file(fhttp->file_path);
  70. if (feed_data == NULL)
  71. {
  72. FURI_LOG_E(TAG, "Failed to load received data from file.");
  73. return false;
  74. }
  75. FuriString *json_data_str = furi_string_alloc();
  76. if (!json_data_str)
  77. {
  78. FURI_LOG_E("Game", "Failed to allocate json_data string");
  79. return NULL;
  80. }
  81. furi_string_cat_str(json_data_str, "{\"json_data\":");
  82. furi_string_cat(json_data_str, feed_data);
  83. furi_string_free(feed_data);
  84. furi_string_cat_str(json_data_str, "}");
  85. int app_count = 0;
  86. // parse the JSON data
  87. for (int i = 0; i < MAX_APP_COUNT; i++)
  88. {
  89. FuriString *json_data_array = get_json_array_value_furi("json_data", i, json_data_str);
  90. if (!json_data_array)
  91. {
  92. FURI_LOG_I(TAG, "No more apps to process, i = %d", i);
  93. break;
  94. }
  95. FuriString *app_id = get_json_value_furi("alias", json_data_array); // app_id is stored in the "alias" field
  96. if (!app_id)
  97. {
  98. FURI_LOG_E(TAG, "Failed to get app_id.");
  99. break;
  100. }
  101. snprintf(flip_catalog[i].app_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(app_id));
  102. furi_string_free(app_id);
  103. FuriString *current_version = get_json_value_furi("current_version", json_data_array);
  104. if (!current_version)
  105. {
  106. FURI_LOG_E(TAG, "Failed to get current_version.");
  107. break;
  108. }
  109. FuriString *app_name = get_json_value_furi("name", current_version);
  110. if (!app_name)
  111. {
  112. FURI_LOG_E(TAG, "Failed to get app_name.");
  113. break;
  114. }
  115. snprintf(flip_catalog[i].app_name, MAX_APP_NAME_LENGTH, "%s", furi_string_get_cstr(app_name));
  116. furi_string_free(app_name);
  117. FuriString *app_description = get_json_value_furi("short_description", current_version);
  118. if (!app_description)
  119. {
  120. FURI_LOG_E(TAG, "Failed to get app_description.");
  121. break;
  122. }
  123. snprintf(flip_catalog[i].app_description, MAX_APP_DESCRIPTION_LENGTH, "%s", furi_string_get_cstr(app_description));
  124. furi_string_free(app_description);
  125. FuriString *app_version = get_json_value_furi("version", current_version);
  126. if (!app_version)
  127. {
  128. FURI_LOG_E(TAG, "Failed to get app_version.");
  129. break;
  130. }
  131. snprintf(flip_catalog[i].app_version, MAX_APP_VERSION_LENGTH, "%s", furi_string_get_cstr(app_version));
  132. furi_string_free(app_version);
  133. FuriString *_id = get_json_value_furi("_id", current_version);
  134. if (!_id)
  135. {
  136. FURI_LOG_E(TAG, "Failed to get _id.");
  137. break;
  138. }
  139. snprintf(flip_catalog[i].app_build_id, MAX_ID_LENGTH, "%s", furi_string_get_cstr(_id));
  140. furi_string_free(_id);
  141. app_count++;
  142. furi_string_free(json_data_array);
  143. furi_string_free(current_version);
  144. }
  145. furi_string_free(json_data_str);
  146. return app_count > 0;
  147. }
  148. bool flip_store_get_fap_file(FlipperHTTP *fhttp, char *build_id, uint8_t target, uint16_t api_major, uint16_t api_minor)
  149. {
  150. if (!fhttp)
  151. {
  152. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  153. return false;
  154. }
  155. if (!build_id)
  156. {
  157. FURI_LOG_E(TAG, "Build ID is NULL.");
  158. return false;
  159. }
  160. fhttp->state = IDLE;
  161. char url[128];
  162. fhttp->save_received_data = false;
  163. fhttp->is_bytes_request = true;
  164. 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);
  165. return flipper_http_get_request_bytes(fhttp, url, "{\"Content-Type\": \"application/octet-stream\"}");
  166. }
  167. bool flip_store_install_app(FlipperHTTP *fhttp, char *category)
  168. {
  169. if (!fhttp)
  170. {
  171. FURI_LOG_E(TAG, "FlipperHTTP is NULL.");
  172. return false;
  173. }
  174. // create /apps/FlipStore directory if it doesn't exist
  175. char directory_path[128];
  176. snprintf(directory_path, sizeof(directory_path), STORAGE_EXT_PATH_PREFIX "/apps/%s", category);
  177. // Create the directory
  178. Storage *storage = furi_record_open(RECORD_STORAGE);
  179. storage_common_mkdir(storage, directory_path);
  180. furi_record_close(RECORD_STORAGE);
  181. snprintf(fhttp->file_path, sizeof(fhttp->file_path), STORAGE_EXT_PATH_PREFIX "/apps/%s/%s.fap", category, flip_catalog[app_selected_index].app_id);
  182. uint8_t target = furi_hal_version_get_hw_target();
  183. uint16_t api_major, api_minor;
  184. furi_hal_info_get_api_version(&api_major, &api_minor);
  185. if (fhttp->state != INACTIVE && flip_store_get_fap_file(fhttp, flip_catalog[app_selected_index].app_build_id, target, api_major, api_minor))
  186. {
  187. fhttp->state = RECEIVING;
  188. return true;
  189. }
  190. else
  191. {
  192. FURI_LOG_E(TAG, "Failed to send the request");
  193. flip_store_success = false;
  194. return false;
  195. }
  196. }