barcode_app.c 16 KB

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