barcode_app.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. #include "barcode_app.h"
  2. #include "barcode_app_icons.h"
  3. #include <assets_icons.h>
  4. #include <notification/notification.h>
  5. #include <notification/notification_messages.h>
  6. #include <notification/notification_app.h>
  7. /**
  8. * Opens a file browser dialog and returns the filepath of the selected file
  9. *
  10. * @param folder the folder to view when the browser opens
  11. * @param file_path a string pointer for the file_path when a file is selected,
  12. * file_path will be the folder path is nothing is selected
  13. * @returns true if a file is selected
  14. */
  15. NotificationApp* notifications = 0;
  16. static bool select_file(const char* folder, FuriString* file_path) {
  17. DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS);
  18. DialogsFileBrowserOptions browser_options;
  19. dialog_file_browser_set_basic_options(&browser_options, "", &I_barcode_10);
  20. browser_options.base_path = DEFAULT_USER_BARCODES;
  21. furi_string_set(file_path, folder);
  22. bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options);
  23. furi_record_close(RECORD_DIALOGS);
  24. return res;
  25. }
  26. /**
  27. * Reads the data from a file and stores them in the FuriStrings raw_type and raw_data
  28. */
  29. ErrorCode read_raw_data(FuriString* file_path, FuriString* raw_type, FuriString* raw_data) {
  30. //Open Storage
  31. Storage* storage = furi_record_open(RECORD_STORAGE);
  32. FlipperFormat* ff = flipper_format_file_alloc(storage);
  33. ErrorCode reason = OKCode;
  34. if(!flipper_format_file_open_existing(ff, furi_string_get_cstr(file_path))) {
  35. FURI_LOG_E(TAG, "Could not open file %s", furi_string_get_cstr(file_path));
  36. reason = FileOpening;
  37. } else {
  38. if(!flipper_format_read_string(ff, "Type", raw_type)) {
  39. FURI_LOG_E(TAG, "Could not read \"Type\" string");
  40. reason = InvalidFileData;
  41. }
  42. if(!flipper_format_read_string(ff, "Data", raw_data)) {
  43. FURI_LOG_E(TAG, "Could not read \"Data\" string");
  44. reason = InvalidFileData;
  45. }
  46. }
  47. //Close Storage
  48. flipper_format_free(ff);
  49. furi_record_close(RECORD_STORAGE);
  50. return reason;
  51. }
  52. /**
  53. * Gets the file name from a file path
  54. * @param file_path the file path
  55. * @param file_name the FuriString to store the file name
  56. * @param remove_extension true if the extension should be removed, otherwise false
  57. */
  58. bool get_file_name_from_path(FuriString* file_path, FuriString* file_name, bool remove_extension) {
  59. if(file_path == NULL || file_name == NULL) {
  60. return false;
  61. }
  62. uint32_t slash_index = furi_string_search_rchar(file_path, '/', 0);
  63. if(slash_index == FURI_STRING_FAILURE || slash_index >= (furi_string_size(file_path) - 1)) {
  64. return false;
  65. }
  66. furi_string_set(file_name, file_path);
  67. furi_string_right(file_name, slash_index + 1);
  68. if(remove_extension) {
  69. uint32_t ext_index = furi_string_search_rchar(file_name, '.', 0);
  70. if(ext_index != FURI_STRING_FAILURE && ext_index < (furi_string_size(file_path))) {
  71. furi_string_left(file_name, ext_index);
  72. }
  73. }
  74. return true;
  75. }
  76. /**
  77. * Creates the barcode folder
  78. */
  79. void init_folder() {
  80. Storage* storage = furi_record_open(RECORD_STORAGE);
  81. FURI_LOG_I(TAG, "Creating barcodes folder");
  82. if(storage_simply_mkdir(storage, DEFAULT_USER_BARCODES)) {
  83. FURI_LOG_I(TAG, "Barcodes folder successfully created!");
  84. } else {
  85. FURI_LOG_I(TAG, "Barcodes folder already exists.");
  86. }
  87. furi_record_close(RECORD_STORAGE);
  88. }
  89. void select_barcode_item(BarcodeApp* app) {
  90. FuriString* file_path = furi_string_alloc();
  91. FuriString* raw_type = furi_string_alloc();
  92. FuriString* raw_data = furi_string_alloc();
  93. //this determines if the data was read correctly or if the
  94. bool loaded_success = true;
  95. ErrorCode reason = OKCode;
  96. bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
  97. if(file_selected) {
  98. FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
  99. Barcode* barcode = app->barcode_view;
  100. reason = read_raw_data(file_path, raw_type, raw_data);
  101. if(reason != OKCode) {
  102. loaded_success = false;
  103. FURI_LOG_E(TAG, "Could not read data correctly");
  104. }
  105. //Free the data from the previous barcode
  106. barcode_free_model(barcode);
  107. with_view_model(
  108. barcode->view,
  109. BarcodeModel * model,
  110. {
  111. model->file_path = furi_string_alloc_set(file_path);
  112. model->data = malloc(sizeof(BarcodeData));
  113. model->data->valid = loaded_success;
  114. if(loaded_success) {
  115. model->data->raw_data = furi_string_alloc_set(raw_data);
  116. model->data->correct_data = furi_string_alloc();
  117. model->data->type_obj = get_type(raw_type);
  118. barcode_loader(model->data);
  119. } else {
  120. model->data->reason = reason;
  121. }
  122. },
  123. true);
  124. view_dispatcher_switch_to_view(app->view_dispatcher, BarcodeView);
  125. }
  126. furi_string_free(raw_type);
  127. furi_string_free(raw_data);
  128. furi_string_free(file_path);
  129. }
  130. void edit_barcode_item(BarcodeApp* app) {
  131. FuriString* file_path = furi_string_alloc();
  132. FuriString* file_name = furi_string_alloc();
  133. FuriString* raw_type = furi_string_alloc();
  134. FuriString* raw_data = furi_string_alloc();
  135. //this determines if the data was read correctly or if the
  136. ErrorCode reason = OKCode;
  137. bool file_selected = select_file(DEFAULT_USER_BARCODES, file_path);
  138. if(file_selected) {
  139. FURI_LOG_I(TAG, "The file selected is %s", furi_string_get_cstr(file_path));
  140. CreateView* create_view_object = app->create_view;
  141. reason = read_raw_data(file_path, raw_type, raw_data);
  142. if(reason != OKCode) {
  143. FURI_LOG_E(TAG, "Could not read data correctly");
  144. with_view_model(
  145. app->message_view->view,
  146. MessageViewModel * model,
  147. { model->message = get_error_code_message(reason); },
  148. true);
  149. view_dispatcher_switch_to_view(
  150. create_view_object->barcode_app->view_dispatcher, MessageErrorView);
  151. } else {
  152. BarcodeTypeObj* type_obj = get_type(raw_type);
  153. if(type_obj->type == UNKNOWN) {
  154. type_obj = barcode_type_objs[0];
  155. }
  156. get_file_name_from_path(file_path, file_name, true);
  157. create_view_free_model(create_view_object);
  158. with_view_model(
  159. create_view_object->view,
  160. CreateViewModel * model,
  161. {
  162. model->selected_menu_item = 0;
  163. model->barcode_type = type_obj;
  164. model->file_path = furi_string_alloc_set(file_path);
  165. model->file_name = furi_string_alloc_set(file_name);
  166. model->barcode_data = furi_string_alloc_set(raw_data);
  167. model->mode = EditMode;
  168. },
  169. true);
  170. view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
  171. }
  172. }
  173. furi_string_free(raw_type);
  174. furi_string_free(raw_data);
  175. furi_string_free(file_name);
  176. furi_string_free(file_path);
  177. }
  178. void create_barcode_item(BarcodeApp* app) {
  179. CreateView* create_view_object = app->create_view;
  180. create_view_free_model(create_view_object);
  181. with_view_model(
  182. create_view_object->view,
  183. CreateViewModel * model,
  184. {
  185. model->selected_menu_item = 0;
  186. model->barcode_type = barcode_type_objs[0];
  187. model->file_path = furi_string_alloc();
  188. model->file_name = furi_string_alloc();
  189. model->barcode_data = furi_string_alloc();
  190. model->mode = NewMode;
  191. },
  192. true);
  193. view_dispatcher_switch_to_view(app->view_dispatcher, CreateBarcodeView);
  194. }
  195. void submenu_callback(void* context, uint32_t index) {
  196. furi_assert(context);
  197. BarcodeApp* app = context;
  198. if(index == SelectBarcodeItem) {
  199. select_barcode_item(app);
  200. } else if(index == EditBarcodeItem) {
  201. edit_barcode_item(app);
  202. } else if(index == CreateBarcodeItem) {
  203. create_barcode_item(app);
  204. } else if(index == AboutWidgetItem) {
  205. view_dispatcher_switch_to_view(app->view_dispatcher, AboutWidgetView);
  206. } else if(index == ErrorCodesWidgetItem) {
  207. view_dispatcher_switch_to_view(app->view_dispatcher, ErrorCodesWidgetView);
  208. }
  209. }
  210. uint32_t create_view_callback(void* context) {
  211. UNUSED(context);
  212. return CreateBarcodeView;
  213. }
  214. uint32_t main_menu_callback(void* context) {
  215. UNUSED(context);
  216. return MainMenuView;
  217. }
  218. uint32_t exit_callback(void* context) {
  219. UNUSED(context);
  220. return VIEW_NONE;
  221. }
  222. void free_app(BarcodeApp* app) {
  223. FURI_LOG_I(TAG, "Freeing Data");
  224. init_folder();
  225. free_types();
  226. view_dispatcher_remove_view(app->view_dispatcher, TextInputView);
  227. text_input_free(app->text_input);
  228. view_dispatcher_remove_view(app->view_dispatcher, AboutWidgetView);
  229. widget_free(app->about_widget);
  230. view_dispatcher_remove_view(app->view_dispatcher, ErrorCodesWidgetView);
  231. widget_free(app->error_codes_widget);
  232. view_dispatcher_remove_view(app->view_dispatcher, MessageErrorView);
  233. message_view_free(app->message_view);
  234. view_dispatcher_remove_view(app->view_dispatcher, MainMenuView);
  235. submenu_free(app->main_menu);
  236. view_dispatcher_remove_view(app->view_dispatcher, CreateBarcodeView);
  237. create_view_free(app->create_view);
  238. view_dispatcher_remove_view(app->view_dispatcher, BarcodeView);
  239. barcode_free(app->barcode_view);
  240. //free the dispatcher
  241. view_dispatcher_free(app->view_dispatcher);
  242. furi_message_queue_free(app->event_queue);
  243. furi_record_close(RECORD_GUI);
  244. app->gui = NULL;
  245. free(app);
  246. }
  247. void set_backlight_brightness(float brightness) {
  248. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  249. notifications->settings.display_brightness = brightness;
  250. notification_message(notifications, &sequence_display_backlight_on);
  251. }
  252. int32_t barcode_main(void* p) {
  253. UNUSED(p);
  254. BarcodeApp* app = malloc(sizeof(BarcodeApp));
  255. init_types();
  256. app->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent));
  257. // Register view port in GUI
  258. app->gui = furi_record_open(RECORD_GUI);
  259. app->view_dispatcher = view_dispatcher_alloc();
  260. view_dispatcher_enable_queue(app->view_dispatcher);
  261. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  262. app->main_menu = submenu_alloc();
  263. submenu_add_item(app->main_menu, "Load Barcode", SelectBarcodeItem, submenu_callback, app);
  264. view_set_previous_callback(submenu_get_view(app->main_menu), exit_callback);
  265. view_dispatcher_add_view(app->view_dispatcher, MainMenuView, submenu_get_view(app->main_menu));
  266. submenu_add_item(app->main_menu, "Edit Barcode", EditBarcodeItem, submenu_callback, app);
  267. NotificationApp* notifications = furi_record_open(RECORD_NOTIFICATION);
  268. // Save original brightness
  269. float originalBrightness = notifications->settings.display_brightness;
  270. // force backlight and increase brightness
  271. notification_message_block(notifications, &sequence_display_backlight_enforce_on);
  272. set_backlight_brightness(10); // set to highest
  273. /*****************************
  274. * Creating Text Input View
  275. ******************************/
  276. app->text_input = text_input_alloc();
  277. view_set_previous_callback(text_input_get_view(app->text_input), create_view_callback);
  278. view_dispatcher_add_view(
  279. app->view_dispatcher, TextInputView, text_input_get_view(app->text_input));
  280. /*****************************
  281. * Creating Message View
  282. ******************************/
  283. app->message_view = message_view_allocate(app);
  284. view_dispatcher_add_view(
  285. app->view_dispatcher, MessageErrorView, message_get_view(app->message_view));
  286. /*****************************
  287. * Creating Create View
  288. ******************************/
  289. app->create_view = create_view_allocate(app);
  290. submenu_add_item(app->main_menu, "Create Barcode", CreateBarcodeItem, submenu_callback, app);
  291. view_set_previous_callback(create_get_view(app->create_view), main_menu_callback);
  292. view_dispatcher_add_view(
  293. app->view_dispatcher, CreateBarcodeView, create_get_view(app->create_view));
  294. /*****************************
  295. * Creating Error Codes View
  296. ******************************/
  297. app->error_codes_widget = widget_alloc();
  298. widget_add_text_scroll_element(
  299. app->error_codes_widget,
  300. 0,
  301. 0,
  302. 128,
  303. 64,
  304. "\e#Error Codes\n"
  305. "\e#Wrong # Of Characters\n"
  306. "The barcode data has too \nmany or too few characters\n"
  307. "UPC-A: 11-12 characters\n"
  308. "EAN-8: 7-8 characters\n"
  309. "EAN-13: 12-13 characters\n"
  310. "Code128C - even # of \ncharacters\n"
  311. "\n"
  312. "\e#Invalid Characters\n"
  313. "The barcode data has invalid \ncharacters.\n"
  314. "Ex: UPC-A, EAN-8, EAN-13 barcodes can only have \nnumbers while Code128 can \nhave almost any character\n"
  315. "\n"
  316. "\e#Unsupported Type\n"
  317. "The barcode type is not \nsupported by this application\n"
  318. "\n"
  319. "\e#File Opening Error\n"
  320. "The barcode file could not be opened. One reason could be \nthat the file no longer exists\n"
  321. "\n"
  322. "\e#Invalid File Data\n"
  323. "The barcode file could not find the keys \"Type\" or \"Data\". \nThis usually occurs when you edit the file manually and \naccidently change the keys\n"
  324. "\n"
  325. "\e#Missing Encoding Table\n"
  326. "The encoding table files are \nmissing. This only occurs \nwhen you need to handle the \nencoding files manually. If you \ndownload the files from the \napp store this should not \noccur\n"
  327. "\n"
  328. "\e#Encoding Table Error\n"
  329. "This occurs when the \nprogram cannot find a \ncharacter in the encoding \ntable, meaning that either the\ncharacter isn't supported \nor the character is missing \nfrom the encoding table\n"
  330. "");
  331. view_set_previous_callback(widget_get_view(app->error_codes_widget), main_menu_callback);
  332. view_dispatcher_add_view(
  333. app->view_dispatcher, ErrorCodesWidgetView, widget_get_view(app->error_codes_widget));
  334. submenu_add_item(
  335. app->main_menu, "Error Codes Info", ErrorCodesWidgetItem, submenu_callback, app);
  336. /*****************************
  337. * Creating About View
  338. ******************************/
  339. app->about_widget = widget_alloc();
  340. widget_add_text_scroll_element(
  341. app->about_widget,
  342. 0,
  343. 0,
  344. 128,
  345. 64,
  346. "This is a barcode generator\n"
  347. "capable of generating UPC-A,\n"
  348. "EAN-8, EAN-13, Code-39,\n"
  349. "Codabar, and Code-128\n"
  350. "\n"
  351. "author: @Kingal1337\n"
  352. "\n"
  353. "For more information or\n"
  354. "issues, go to\n"
  355. "https://github.com/Kingal1337/flipper-barcode-generator");
  356. view_set_previous_callback(widget_get_view(app->about_widget), main_menu_callback);
  357. view_dispatcher_add_view(
  358. app->view_dispatcher, AboutWidgetView, widget_get_view(app->about_widget));
  359. submenu_add_item(app->main_menu, "About", AboutWidgetItem, submenu_callback, app);
  360. /*****************************
  361. * Creating Barcode View
  362. ******************************/
  363. app->barcode_view = barcode_view_allocate(app);
  364. view_set_previous_callback(barcode_get_view(app->barcode_view), main_menu_callback);
  365. view_dispatcher_add_view(
  366. app->view_dispatcher, BarcodeView, barcode_get_view(app->barcode_view));
  367. //switch view to submenu and run dispatcher
  368. view_dispatcher_switch_to_view(app->view_dispatcher, MainMenuView);
  369. view_dispatcher_run(app->view_dispatcher);
  370. free_app(app);
  371. notification_message_block(notifications, &sequence_display_backlight_enforce_auto);
  372. set_backlight_brightness(originalBrightness);
  373. return 0;
  374. }