barcode_app.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. #include "barcode_app.h"
  2. #include "barcode_app_icons.h"
  3. #include <notification/notification.h>
  4. #include <notification/notification_messages.h>
  5. #include <notification/notification_app.h>
  6. /**
  7. * Opens a file browser dialog and returns the filepath of the selected file
  8. *
  9. * @param folder the folder to view when the browser opens
  10. * @param file_path a string pointer for the file_path when a file is selected,
  11. * file_path will be the folder path is nothing is selected
  12. * @returns true if a file is selected
  13. */
  14. NotificationApp* notifications = 0;
  15. static bool select_file(const char* folder, FuriString* file_path) {
  16. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  17. DialogsFileBrowserOptions browser_options;
  18. dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10);
  19. browser_options.base_path = DEFAULT_USER_BARCODES;
  20. furi_string_set(file_path, folder);
  21. bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
  22. furi_record_close(RECORD_DIALOGS);
  23. return res;
  24. }
  25. /**
  26. * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data
  27. */
  28. ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) {
  29. //Open Storage
  30. Storage* storage = furi_record_open(RECORD_STORAGE);
  31. FlipperFormat* ff = flipper_format_file_alloc(storage);
  32. ErrorCode reason = OKCode;
  33. if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) {
  34. FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path));
  35. reason = FileOpening;
  36. } else {
  37. if(!flipper_format_read_string(ff, "Type", raw_type)) {
  38. FURI_LOG_E(TAG, "Could not read \"Type\" string");
  39. reason = InvalidFileData;
  40. }
  41. if(!flipper_format_read_string(ff, "Data", raw_data)) {
  42. FURI_LOG_E(TAG, "Could not read \"Data\" string");
  43. reason = InvalidFileData;
  44. }
  45. }
  46. //Close Storage
  47. flipper_format_free(ff);
  48. furi_record_close(RECORD_STORAGE);
  49. return reason;
  50. }
  51. /**
  52. * Gets the file name from a file path
  53. * @param file_path the file path
  54. * @param file_name the FuriString to store the file name
  55. * @param remove_extension true if the extension should be removed, otherwise false
  56. */
  57. bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) {
  58. if(file_path == NULL || file_name == NULL) {
  59. return false;
  60. }
  61. uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0);
  62. if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) {
  63. return false;
  64. }
  65. furi_string_set(file_name, file_path);
  66. furi_string_right(file_name, slash_index + 1);
  67. if(remove_extension) {
  68. uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0);
  69. if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) {
  70. furi_string_left(file_name, ext_index);
  71. }
  72. }
  73. return true;
  74. }
  75. /**
  76. * Creates the barcode folder
  77. */
  78. void init_folder() {
  79. Storage* storage = furi_record_open(RECORD_STORAGE);
  80. FURI_LOG_I(TAG, "Creating barcodes folder");
  81. if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) {
  82. FURI_LOG_I(TAG, "Barcodes folder successfully created!");
  83. } else {
  84. FURI_LOG_I(TAG, "Barcodes folder already exists.");
  85. }
  86. furi_record_close(RECORD_STORAGE);
  87. }
  88. void select_barcode_item(BarcodeApp* app) {
  89. FuriString* file_path = furi_string_alloc();
  90. FuriString* raw_type = furi_string_alloc();
  91. FuriString* raw_data = furi_string_alloc();
  92. //this determines if the data was read correctly or if the
  93. bool loaded_success = true;
  94. ErrorCode reason = OKCode;
  95. bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
  96. if(file_selected) {
  97. FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
  98. Barcode* barcode = app->barcode_view;
  99. reason = read_raw_data(file_path, raw_type, raw_data);
  100. if(reason != OKCode) {
  101. loaded_success = false;
  102. FURI_LOG_E(TAG, "Could not read data correctly");
  103. }
  104. //Free the data from the previous barcode
  105. barcode_free_model(barcode);
  106. with_view_model(
  107. barcode->view,
  108. BarcodeModel * model,
  109. {
  110. model->file_path = furi_string_alloc_set(file_path);
  111. model->data = malloc(sizeof(BarcodeData));
  112. model->data->valid = loaded_success;
  113. if(loaded_success) {
  114. model->data->raw_data = furi_string_alloc_set(raw_data);
  115. model->data->correct_data = furi_string_alloc();
  116. model->data->type_obj = get_type(raw_type);
  117. barcode_loader(model->data);
  118. } else {
  119. model->data->reason = reason;
  120. }
  121. },
  122. true);
  123. view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView);
  124. }
  125. furi_string_free(raw_type);
  126. furi_string_free(raw_data);
  127. furi_string_free(file_path);
  128. }
  129. void edit_barcode_item(BarcodeApp* app) {
  130. FuriString* file_path = furi_string_alloc();
  131. FuriString* file_name = furi_string_alloc();
  132. FuriString* raw_type = furi_string_alloc();
  133. FuriString* raw_data = furi_string_alloc();
  134. //this determines if the data was read correctly or if the
  135. ErrorCode reason = OKCode;
  136. bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
  137. if(file_selected) {
  138. FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
  139. CreateView* create_view_object = app->create_view;
  140. reason = read_raw_data(file_path, raw_type, raw_data);
  141. if(reason != OKCode) {
  142. FURI_LOG_E(TAG, "Could not read data correctly");
  143. with_view_model(
  144. app->message_view->view,
  145. MessageViewModel * model,
  146. { model->message = get_error_code_message(reason); },
  147. true);
  148. view_dispatcher_switch_to_view(
  149. create_view_object->barcode_app->view_dispatcher, MessageErrorView);
  150. } else {
  151. BarcodeTypeObj* type_obj = get_type(raw_type);
  152. if(type_obj->type == UNKNOWN) {
  153. type_obj = barcode_type_objs[0];
  154. }
  155. get_file_name_from_path(file_path, file_name, true);
  156. create_view_free_model(create_view_object);
  157. with_view_model(
  158. create_view_object->view,
  159. CreateViewModel * model,
  160. {
  161. model->selected_menu_item = 0;
  162. model->barcode_type = type_obj;
  163. model->file_path = furi_string_alloc_set(file_path);
  164. model->file_name = furi_string_alloc_set(file_name);
  165. model->barcode_data = furi_string_alloc_set(raw_data);
  166. model->mode = EditMode;
  167. },
  168. true);
  169. view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
  170. }
  171. }
  172. furi_string_free(raw_type);
  173. furi_string_free(raw_data);
  174. furi_string_free(file_name);
  175. furi_string_free(file_path);
  176. }
  177. void create_barcode_item(BarcodeApp* app) {
  178. CreateView* create_view_object = app->create_view;
  179. create_view_free_model(create_view_object);
  180. with_view_model(
  181. create_view_object->view,
  182. CreateViewModel * model,
  183. {
  184. model->selected_menu_item = 0;
  185. model->barcode_type = barcode_type_objs[0];
  186. model->file_path = furi_string_alloc();
  187. model->file_name = furi_string_alloc();
  188. model->barcode_data = furi_string_alloc();
  189. model->mode = NewMode;
  190. },
  191. true);
  192. view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
  193. }
  194. void submenu_callback(void* context, uint32_t index) {
  195. furi_assert(context);
  196. BarcodeApp* app = context;
  197. if(index == SelectBarcodeItem) {
  198. select_barcode_item(app);
  199. } else if(index == EditBarcodeItem) {
  200. edit_barcode_item(app);
  201. } else if(index == CreateBarcodeItem) {
  202. create_barcode_item(app);
  203. }
  204. }
  205. uint32_t create_view_callback(void* context) {
  206. UNUSED(context);
  207. return CreateBarcodeView;
  208. }
  209. uint32_t main_menu_callback(void* context) {
  210. UNUSED(context);
  211. return MainMenuView;
  212. }
  213. uint32_t exit_callback(void* context) {
  214. UNUSED(context);
  215. return VIEW_NONE;
  216. }
  217. void free_app(BarcodeApp* app) {
  218. FURI_LOG_I(TAG, "Freeing Data");
  219. init_folder();
  220. free_types();
  221. view_dispatcher_remove_view(app->view_dispatcher, TextInputView);
  222. text_input_free(app->text_input);
  223. view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView);
  224. message_view_free(app->message_view);
  225. view_dispatcher_remove_view(app->view_dispatcher, MainMenuView);
  226. submenu_free(app->main_menu);
  227. view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView);
  228. create_view_free(app->create_view);
  229. view_dispatcher_remove_view(app->view_dispatcher, BarcodeView);
  230. barcode_free(app->barcode_view);
  231. //free the dispatcher
  232. view_dispatcher_free(app->view_dispatcher);
  233. furi_message_queue_free(app->event_queue);
  234. furi_record_close(RECORD_GUI);
  235. app->gui = NULL;
  236. free(app);
  237. }
  238. void set_backlight_brightness(float brightness) {
  239. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  240. notifications->settings.display_brightness = brightness;
  241. notification_message(notifications, &sequence_display_backlight_on);
  242. }
  243. int32_t barcode_main(void* p) {
  244. UNUSED(p);
  245. BarcodeApp* app = malloc(sizeof(BarcodeApp));
  246. init_types();
  247. app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  248. // Register view port in GUI
  249. app->gui = furi_record_open(RECORD_GUI);
  250. app->view_dispatcher = view_dispatcher_alloc();
  251. view_dispatcher_enable_queue(app->view_dispatcher);
  252. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  253. app->main_menu = submenu_alloc();
  254. submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app);
  255. view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback);
  256. view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu));
  257. submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app);
  258. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  259. // Save original brightness
  260. float originalBrightness = notifications->settings.display_brightness;
  261. // force backlight and increase brightness
  262. notification_message_block(notifications, &sequence_display_backlight_enforce_on);
  263. set_backlight_brightness(10); // set to highest
  264. /*****************************
  265. * Creating Text Input View
  266. ******************************/
  267. app->text_input = text_input_alloc();
  268. view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback);
  269. view_dispatcher_add_view(
  270. app->view_dispatcher, TextInputView, text_input_get_view(app->text_input));
  271. /*****************************
  272. * Creating Message View
  273. ******************************/
  274. app->message_view = message_view_allocate(app);
  275. view_dispatcher_add_view(
  276. app->view_dispatcher, MessageErrorView, message_get_view(app->message_view));
  277. /*****************************
  278. * Creating Create View
  279. ******************************/
  280. app->create_view = create_view_allocate(app);
  281. submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app);
  282. view_set_previous_callback(create_get_view(app->create_view), main_menu_callback);
  283. view_dispatcher_add_view(
  284. app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view));
  285. /*****************************
  286. * Creating Barcode View
  287. ******************************/
  288. app->barcode_view = barcode_view_allocate(app);
  289. view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback);
  290. view_dispatcher_add_view(
  291. app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view));
  292. //switch view to submenu and run dispatcher
  293. view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView);
  294. view_dispatcher_run(app->view_dispatcher);
  295. free_app(app);
  296. notification_message_block(notifications, &sequence_display_backlight_enforce_auto);
  297. set_backlight_brightness(originalBrightness);
  298. return 0;
  299. }