| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757 |
- #include <furi.h>
- #include <furi_hal.h>
- #include <gui/gui.h>
- #include <gui/view.h>
- #include <gui/view_dispatcher.h>
- #include <gui/modules/submenu.h>
- #include <gui/modules/text_input.h>
- #include <gui/modules/widget.h>
- #include <gui/modules/variable_item_list.h>
- #include <notification/notification.h>
- #include <notification/notification_messages.h>
- #include <applications/services/storage/storage.h>
- #include <applications/services/dialogs/dialogs.h>
- #include <stdbool.h>
- #include <math.h>
- #include <flipper_format.h>
- #include "key_copier_icons.h"
- #include "key_formats.h"
- #include "key_copier.h"
- #define TAG "KeyCopier"
- #define INCHES_PER_PX 0.00978
- // Change this to BACKLIGHT_AUTO if you don't want the backlight to be continuously on.
- #define BACKLIGHT_ON 1
- // Our application menu has 3 items. You can add more items if you want.
- typedef enum {
- KeyCopierSubmenuIndexMeasure,
- KeyCopierSubmenuIndexConfigure,
- KeyCopierSubmenuIndexSave,
- KeyCopierSubmenuIndexLoad,
- KeyCopierSubmenuIndexAbout,
- } KeyCopierSubmenuIndex;
- // Each view is a screen we show the user.
- typedef enum {
- KeyCopierViewSubmenu, // The menu when the app starts
- KeyCopierViewTextInput, // Input for configuring text settings
- KeyCopierViewConfigure_i, // The configuration screen
- KeyCopierViewConfigure_e, // The configuration screen
- KeyCopierViewSave,
- KeyCopierViewLoad,
- KeyCopierViewMeasure, // The main screen
- KeyCopierViewAbout, // The about screen with directions, link to social channel, etc.
- } KeyCopierView;
- typedef enum {
- KeyCopierEventIdRedrawScreen = 0, // Custom event to redraw the screen
- } KeyCopierEventId;
- typedef struct {
- ViewDispatcher* view_dispatcher; // Switches between our views
- NotificationApp* notifications; // Used for controlling the backlight
- Submenu* submenu; // The application menu
- TextInput* text_input; // The text input screen
- VariableItemList* variable_item_list_config; // The configuration screen
- View* view_measure; // The main screen
- View* view_config_e; // The configuration screen
- View* view_save;
- View* view_load; // The load view
- Widget* widget_about; // The about screen
- VariableItem* key_name_item; // The name setting item (so we can update the text)
- VariableItem* format_item;
- char* temp_buffer; // Temporary buffer for text input
- uint32_t temp_buffer_size; // Size of temporary buffer
- DialogsApp* dialogs;
- FuriString* file_path;
- FuriTimer* timer; // Timer for redrawing the screen
- } KeyCopierApp;
- typedef struct {
- uint32_t format_index; // The index for total number of pins
- FuriString* key_name_str; // The name setting
- uint8_t pin_slc; // The pin that is being adjusted
- uint8_t* depth; // The cutting depth
- bool data_loaded;
- KeyFormat format;
- } KeyCopierModel;
- void initialize_model(KeyCopierModel* model) {
- if(model->depth != NULL) {
- free(model->depth);
- }
- model->format_index = 0;
- memcpy(&model->format, &all_formats[model->format_index], sizeof(KeyFormat));
- model->depth = (uint8_t*)malloc((model->format.pin_num + 1) * sizeof(uint8_t));
- for(uint8_t i = 0; i <= model->format.pin_num; i++) {
- model->depth[i] = model->format.min_depth_ind;
- }
- model->pin_slc = 1;
- model->data_loaded = 0;
- model->key_name_str = furi_string_alloc();
- }
- /**
- * @brief Callback for exiting the application.
- * @details This function is called when user press back button. We return VIEW_NONE to
- * indicate that we want to exit the application.
- * @param _context The context - unused
- * @return next view id
- */
- static uint32_t key_copier_navigation_exit_callback(void* _context) {
- UNUSED(_context);
- return VIEW_NONE;
- }
- /**
- * @brief Callback for returning to submenu.
- * @details This function is called when user press back button. We return VIEW_NONE to
- * indicate that we want to navigate to the submenu.
- * @param _context The context - unused
- * @return next view id
- */
- static uint32_t key_copier_navigation_submenu_callback(void* _context) {
- UNUSED(_context);
- return KeyCopierViewSubmenu;
- }
- /**
- * @brief Handle submenu item selection.
- * @details This function is called when user selects an item from the submenu.
- * @param context The context - KeyCopierApp object.
- * @param index The KeyCopierSubmenuIndex item that was clicked.
- */
- static void key_copier_submenu_callback(void* context, uint32_t index) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- switch(index) {
- case KeyCopierSubmenuIndexMeasure:
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewMeasure);
- break;
- case KeyCopierSubmenuIndexConfigure:
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewConfigure_e);
- break;
- case KeyCopierSubmenuIndexSave:
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSave);
- break;
- case KeyCopierSubmenuIndexLoad:
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewLoad);
- break;
- case KeyCopierSubmenuIndexAbout:
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewAbout);
- break;
- default:
- break;
- }
- }
- char* format_names[COUNT_OF(all_formats)];
- void initialize_format_names(char** format_names) {
- // Populate the format_names array
- for(size_t i = 0; i < COUNT_OF(all_formats); i++) {
- format_names[i] = all_formats[i].format_name;
- }
- }
- static const char* format_config_label = "Key Format";
- static void key_copier_format_change(VariableItem* item) {
- KeyCopierApp* app = variable_item_get_context(item);
- KeyCopierModel* model = view_get_model(app->view_measure);
- if(model->data_loaded) {
- variable_item_set_current_value_index(item, model->format_index);
- } else {
- uint8_t format_index = variable_item_get_current_value_index(item);
- model->format_index = format_index;
- model->format = all_formats[format_index];
- }
- model->data_loaded = false;
- variable_item_set_current_value_text(item, model->format.format_name);
- model->format = all_formats[model->format_index];
- if(model->depth != NULL) {
- free(model->depth);
- }
- model->depth = (uint8_t*)malloc((model->format.pin_num + 1) * sizeof(uint8_t));
- for(uint8_t i = 0; i <= model->format.pin_num; i++) {
- model->depth[i] = model->format.min_depth_ind;
- }
- }
- static void key_copier_config_enter_callback(void* context) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- KeyCopierModel* my_model = view_get_model(app->view_measure);
- variable_item_list_reset(app->variable_item_list_config);
- // Recreate this view every time we enter it so that it's always updated
- app->format_item = variable_item_list_add(
- app->variable_item_list_config,
- format_config_label,
- COUNT_OF(all_formats),
- key_copier_format_change,
- app);
- View* view_config_i = variable_item_list_get_view(app->variable_item_list_config);
- variable_item_set_current_value_index(app->format_item, my_model->format_index);
- key_copier_format_change(app->format_item);
- view_set_previous_callback(view_config_i, key_copier_navigation_submenu_callback);
- view_dispatcher_remove_view(
- app->view_dispatcher, KeyCopierViewConfigure_i); // delete the last one
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_i, view_config_i);
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewConfigure_i); // recreate it
- }
- /**
- * Our 2nd sample setting is a text field. When the user clicks OK on the configuration
- * setting we use a text input screen to allow the user to enter a name. This function is
- * called when the user clicks OK on the text input screen.
- */
- static const char* key_name_entry_text = "Enter name";
- static void key_copier_file_saver(void* context) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- KeyCopierModel* model = view_get_model(app->view_measure);
- bool redraw = true;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- { furi_string_set(model->key_name_str, app->temp_buffer); },
- redraw);
- FuriString* file_path = furi_string_alloc();
- furi_string_printf(
- file_path,
- "%s/%s%s",
- STORAGE_APP_DATA_PATH_PREFIX,
- furi_string_get_cstr(model->key_name_str),
- KEY_COPIER_FILE_EXTENSION);
- Storage* storage = furi_record_open(RECORD_STORAGE);
- storage_simply_mkdir(storage, STORAGE_APP_DATA_PATH_PREFIX);
- FURI_LOG_D(TAG, "mkdir finished");
- FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
- do {
- const uint32_t version = 1;
- const uint32_t pin_num_buffer = (uint32_t)model->format.pin_num;
- const uint32_t macs_buffer = (uint32_t)model->format.macs;
- FuriString* buffer = furi_string_alloc();
- if(!flipper_format_file_open_always(flipper_format, furi_string_get_cstr(file_path)))
- break;
- if(!flipper_format_write_header_cstr(flipper_format, "Flipper Key Copier File", version))
- break;
- if(!flipper_format_write_string_cstr(
- flipper_format, "Format Name", model->format.format_name))
- break;
- if(!flipper_format_write_string_cstr(
- flipper_format, "Format Short Name", model->format.format_short_name))
- break;
- if(!flipper_format_write_uint32(flipper_format, "Number of Pins", &pin_num_buffer, 1))
- break;
- if(!flipper_format_write_uint32(
- flipper_format, "Maximum Adjacent Cut Specification (MACS)", &macs_buffer, 1))
- break;
- for(int i = 0; i < model->format.pin_num; i++) {
- if(i < model->format.pin_num - 1) {
- furi_string_cat_printf(buffer, "%d-", model->depth[i]);
- } else {
- furi_string_cat_printf(buffer, "%d", model->depth[i]);
- }
- }
- if(!flipper_format_write_string(flipper_format, "Bitting Pattern", buffer)) break;
- furi_string_free(buffer);
- // signal that the file was written successfully
- } while(0);
- flipper_format_free(flipper_format);
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
- }
- /**
- * @brief Callback when item in configuration screen is clicked.
- * @details This function is called when user clicks OK on an item in the configuration screen.
- * If the item clicked is our text field then we switch to the text input screen.
- * @param context The context - KeyCopierApp object.
- * @param index - The index of the item that was clicked.
- */
- static void key_copier_view_save_callback(void* context) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- // Header to display on the text input screen.
- text_input_set_header_text(app->text_input, key_name_entry_text);
- // Copy the current name into the temporary buffer.
- bool redraw = false;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- strncpy(
- app->temp_buffer,
- furi_string_get_cstr(model->key_name_str),
- app->temp_buffer_size);
- },
- redraw);
- // Configure the text input. When user enters text and clicks OK, key_copier_setting_text_updated be called.
- bool clear_previous_text = false;
- text_input_set_result_callback(
- app->text_input,
- key_copier_file_saver,
- app,
- app->temp_buffer,
- app->temp_buffer_size,
- clear_previous_text);
- // Pressing the BACK button will reload the configure screen.
- view_set_previous_callback(
- text_input_get_view(app->text_input), key_copier_navigation_submenu_callback);
- // Show text input dialog.
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewTextInput);
- }
- static void t5577_writer_view_load_callback(void* context) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- KeyCopierModel* model = view_get_model(app->view_measure);
- DialogsFileBrowserOptions browser_options;
- Storage* storage = furi_record_open(RECORD_STORAGE);
- storage_simply_mkdir(storage, STORAGE_APP_DATA_PATH_PREFIX);
- dialog_file_browser_set_basic_options(&browser_options, KEY_COPIER_FILE_EXTENSION, &I_icon);
- browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX;
- furi_string_set(app->file_path, browser_options.base_path);
- if(dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options)) {
- FlipperFormat* flipper_format = flipper_format_file_alloc(storage);
- do {
- if(!flipper_format_file_open_existing(
- flipper_format, furi_string_get_cstr(app->file_path)))
- break;
- FuriString* format_buffer = furi_string_alloc();
- FuriString* depth_buffer = furi_string_alloc();
- if(!flipper_format_read_string(flipper_format, "Format Name", format_buffer)) break;
- if(!flipper_format_read_string(flipper_format, "Bitting Pattern", depth_buffer)) break;
- for(size_t i = 0; i < COUNT_OF(all_formats); i++) {
- if(!strcmp(furi_string_get_cstr(format_buffer), all_formats[i].format_name)) {
- model->format_index = (uint32_t)i;
- model->format = all_formats[model->format_index];
- }
- }
- for(int i = 0; i < model->format.pin_num; i++) {
- model->depth[i] = (uint8_t)(furi_string_get_char(depth_buffer, i * 2) - '0');
- }
- model->data_loaded = true;
- // signal that the file was read successfully
- } while(0);
- flipper_format_free(flipper_format);
- furi_record_close(RECORD_STORAGE);
- }
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
- }
- /**
- * @brief Callback for drawing the game screen.
- * @details This function is called when the screen needs to be redrawn, like when the model gets updated.
- * @param canvas The canvas to draw on.
- * @param model The model - MyModel object.
- */
- static void key_copier_view_measure_draw_callback(Canvas* canvas, void* model) {
- static double inches_per_px = (double)INCHES_PER_PX;
- KeyCopierModel* my_model = (KeyCopierModel*)model;
- KeyFormat my_format = my_model->format;
- int pin_half_width_px = (int)round((my_format.pin_width_inch / inches_per_px) / 2);
- int pin_step_px = (int)round(my_format.pin_increment_inch / inches_per_px);
- double drill_radians =
- (180 - my_format.drill_angle) / 2 / 180 * (double)M_PI; // Convert angle to radians
- double tangent = tan(drill_radians);
- int post_extra_x_px = 0;
- int pre_extra_x_px = 0;
- for(int current_pin = 1; current_pin <= my_model->format.pin_num; current_pin += 1) {
- double current_center_px =
- my_format.first_pin_inch + (current_pin - 1) * my_format.pin_increment_inch;
- int pin_center_px = (int)round(current_center_px / inches_per_px);
- int top_contour_px = (int)round(63 - my_format.uncut_depth_inch / inches_per_px);
- canvas_draw_line(canvas, pin_center_px, 25, pin_center_px, 50);
- int current_depth = my_model->depth[current_pin - 1] - my_format.min_depth_ind;
- int current_depth_px =
- (int)round(current_depth * my_format.depth_step_inch / inches_per_px);
- canvas_draw_line(
- canvas,
- pin_center_px - pin_half_width_px,
- top_contour_px + current_depth_px,
- pin_center_px + pin_half_width_px,
- top_contour_px + current_depth_px);
- int last_depth = my_model->depth[current_pin - 2] - my_format.min_depth_ind;
- int next_depth = my_model->depth[current_pin] - my_format.min_depth_ind;
- if(current_pin == 1) {
- canvas_draw_line(
- canvas,
- 0,
- top_contour_px,
- pin_center_px - pin_half_width_px - current_depth_px,
- top_contour_px);
- last_depth = 0;
- pre_extra_x_px = max(current_depth_px + pin_half_width_px, 0);
- }
- if(current_pin == my_model->format.pin_num) {
- next_depth = 0;
- }
- if((last_depth + current_depth) > my_format.clearance &&
- current_depth != 0) { //yes intersection
- if(current_pin != 1) {
- pre_extra_x_px =
- min(max(pin_step_px - post_extra_x_px, pin_half_width_px),
- pin_step_px - pin_half_width_px);
- }
- canvas_draw_line(
- canvas,
- pin_center_px - pre_extra_x_px,
- top_contour_px +
- max((int)round(
- (current_depth_px - (pre_extra_x_px - pin_half_width_px)) * tangent),
- 0),
- pin_center_px - pin_half_width_px,
- top_contour_px + (int)round(current_depth_px * tangent));
- } else {
- int last_depth_px = (int)round(last_depth * my_format.depth_step_inch / inches_per_px);
- int down_slope_start_x_px = pin_center_px - pin_half_width_px - current_depth_px;
- canvas_draw_line(
- canvas,
- pin_center_px - pin_half_width_px - current_depth_px,
- top_contour_px,
- pin_center_px - pin_half_width_px,
- top_contour_px + (int)round(current_depth_px * tangent));
- canvas_draw_line(
- canvas,
- min(pin_center_px - pin_step_px + pin_half_width_px + last_depth_px,
- down_slope_start_x_px),
- top_contour_px,
- down_slope_start_x_px,
- top_contour_px);
- }
- if((current_depth + next_depth) > my_format.clearance &&
- current_depth != 0) { //yes intersection
- double numerator = (double)current_depth;
- double denominator = (double)(current_depth + next_depth);
- double product = (numerator / denominator) * pin_step_px;
- post_extra_x_px =
- (int)min(max(product, pin_half_width_px), pin_step_px - pin_half_width_px);
- canvas_draw_line(
- canvas,
- pin_center_px + pin_half_width_px,
- top_contour_px + current_depth_px,
- pin_center_px + post_extra_x_px,
- top_contour_px +
- max(current_depth_px -
- (int)round((post_extra_x_px - pin_half_width_px) * tangent),
- 0));
- } else { // no intersection
- canvas_draw_line(
- canvas,
- pin_center_px + pin_half_width_px,
- top_contour_px + (int)round(current_depth_px * tangent),
- pin_center_px + pin_half_width_px + current_depth_px,
- top_contour_px);
- }
- }
- int level_contour_px =
- (int)round((my_format.last_pin_inch + my_format.pin_increment_inch) / inches_per_px - 4);
- canvas_draw_line(canvas, 0, 62, level_contour_px, 62);
- int slc_pin_px = (int)round(
- (my_format.first_pin_inch + (my_model->pin_slc - 1) * my_format.pin_increment_inch) /
- inches_per_px);
- canvas_draw_str(canvas, slc_pin_px - 2, 23, "*");
- FuriString* xstr = furi_string_alloc();
- int buffer_size = my_model->format.pin_num + 1;
- char depth_str[buffer_size];
- depth_str[0] = '\0'; // Initialize the string
- // Manual string concatenation
- char* pos = depth_str;
- for(int i = 0; i < my_model->format.pin_num; i++) {
- int written = snprintf(pos, buffer_size - (pos - depth_str), "%u", my_model->depth[i]);
- if(written < 0 || written >= buffer_size - (pos - depth_str)) {
- // Handle error
- break;
- }
- pos += written;
- }
- furi_string_printf(xstr, "bitting: %s", depth_str);
- canvas_draw_str(canvas, 0, 10, furi_string_get_cstr(xstr));
- furi_string_printf(xstr, "%s", my_format.format_short_name);
- canvas_draw_str(canvas, 110, 10, furi_string_get_cstr(xstr));
- //furi_string_printf(xstr, "Num of Pins: %s", format_names[my_model->format_index]);
- //canvas_draw_str(canvas, 44, 24, furi_string_get_cstr(xstr));
- furi_string_free(xstr);
- }
- /**
- * @brief Callback for game screen input.
- * @details This function is called when the user presses a button while on the game screen.
- * @param event The event - InputEvent object.
- * @param context The context - KeyCopierApp object.
- * @return true if the event was handled, false otherwise.
- */
- static bool key_copier_view_measure_input_callback(InputEvent* event, void* context) {
- KeyCopierApp* app = (KeyCopierApp*)context;
- if(event->type == InputTypeShort) {
- switch(event->key) {
- case InputKeyLeft: {
- // Left button clicked, reduce x coordinate.
- bool redraw = true;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- if(model->pin_slc > 1) {
- model->pin_slc--;
- }
- },
- redraw);
- break;
- }
- case InputKeyRight: {
- // Left button clicked, reduce x coordinate.
- bool redraw = true;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- if(model->pin_slc < model->format.pin_num) {
- model->pin_slc++;
- }
- },
- redraw);
- break;
- }
- case InputKeyUp: {
- // Left button clicked, reduce x coordinate.
- bool redraw = true;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- if(model->depth[model->pin_slc - 1] > model->format.min_depth_ind) {
- if(model->pin_slc == 1) { //first pin only limited by the next one
- if(model->depth[model->pin_slc] - model->depth[model->pin_slc - 1] <
- model->format.macs)
- model->depth[model->pin_slc - 1]--;
- } else if(
- model->pin_slc ==
- model->format.pin_num) { //last pin only limited by the previous one
- if(model->depth[model->pin_slc - 2] -
- model->depth[model->pin_slc - 1] <
- model->format.macs) {
- model->depth[model->pin_slc - 1]--;
- }
- } else {
- if(model->depth[model->pin_slc] - model->depth[model->pin_slc - 1] <
- model->format.macs &&
- model->depth[model->pin_slc - 2] -
- model->depth[model->pin_slc - 1] <
- model->format.macs) {
- model->depth[model->pin_slc - 1]--;
- }
- }
- }
- },
- redraw);
- break;
- }
- case InputKeyDown: {
- // Right button clicked, increase x coordinate.
- bool redraw = true;
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- if(model->depth[model->pin_slc - 1] < model->format.max_depth_ind) {
- if(model->pin_slc == 1) { //first pin only limited by the next one
- if(model->depth[model->pin_slc - 1] - model->depth[model->pin_slc] <
- model->format.macs)
- model->depth[model->pin_slc - 1]++;
- } else if(
- model->pin_slc ==
- model->format.pin_num) { //last pin only limited by the previous one
- if(model->depth[model->pin_slc - 1] -
- model->depth[model->pin_slc - 2] <
- model->format.macs) {
- model->depth[model->pin_slc - 1]++;
- }
- } else {
- if(model->depth[model->pin_slc - 1] - model->depth[model->pin_slc] <
- model->format.macs &&
- model->depth[model->pin_slc - 1] -
- model->depth[model->pin_slc - 2] <
- model->format.macs) {
- model->depth[model->pin_slc - 1]++;
- }
- }
- }
- },
- redraw);
- break;
- }
- default:
- // Handle other keys or do nothing
- break;
- }
- }
- return false;
- }
- /**
- * @brief Allocate the key_copier application.
- * @details This function allocates the key_copier application resources.
- * @return KeyCopierApp object.
- */
- static KeyCopierApp* key_copier_app_alloc() {
- KeyCopierApp* app = (KeyCopierApp*)malloc(sizeof(KeyCopierApp));
- Gui* gui = furi_record_open(RECORD_GUI);
- app->view_dispatcher = view_dispatcher_alloc();
- view_dispatcher_enable_queue(app->view_dispatcher);
- view_dispatcher_attach_to_gui(app->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
- view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
- app->dialogs = furi_record_open(RECORD_DIALOGS);
- app->file_path = furi_string_alloc();
- app->submenu = submenu_alloc();
- submenu_add_item(
- app->submenu, "Measure", KeyCopierSubmenuIndexMeasure, key_copier_submenu_callback, app);
- submenu_add_item(
- app->submenu, "Config", KeyCopierSubmenuIndexConfigure, key_copier_submenu_callback, app);
- submenu_add_item(
- app->submenu, "Save", KeyCopierSubmenuIndexSave, key_copier_submenu_callback, app);
- submenu_add_item(
- app->submenu, "Load", KeyCopierSubmenuIndexLoad, key_copier_submenu_callback, app);
- submenu_add_item(
- app->submenu, "About", KeyCopierSubmenuIndexAbout, key_copier_submenu_callback, app);
- view_set_previous_callback(
- submenu_get_view(app->submenu), key_copier_navigation_exit_callback);
- view_dispatcher_add_view(
- app->view_dispatcher, KeyCopierViewSubmenu, submenu_get_view(app->submenu));
- view_dispatcher_switch_to_view(app->view_dispatcher, KeyCopierViewSubmenu);
- app->text_input = text_input_alloc();
- view_dispatcher_add_view(
- app->view_dispatcher, KeyCopierViewTextInput, text_input_get_view(app->text_input));
- app->temp_buffer_size = 32;
- app->temp_buffer = (char*)malloc(app->temp_buffer_size);
- app->temp_buffer = "";
- app->view_measure = view_alloc();
- view_set_draw_callback(app->view_measure, key_copier_view_measure_draw_callback);
- view_set_input_callback(app->view_measure, key_copier_view_measure_input_callback);
- view_set_previous_callback(app->view_measure, key_copier_navigation_submenu_callback);
- view_set_context(app->view_measure, app);
- view_allocate_model(app->view_measure, ViewModelTypeLockFree, sizeof(KeyCopierModel));
- KeyCopierModel* model = view_get_model(app->view_measure);
- initialize_model(model);
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewMeasure, app->view_measure);
- app->variable_item_list_config = variable_item_list_alloc();
- app->view_config_e = view_alloc();
- view_set_context(app->view_config_e, app);
- view_set_previous_callback(app->view_config_e, key_copier_navigation_submenu_callback);
- view_set_enter_callback(app->view_config_e, key_copier_config_enter_callback);
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_e, app->view_config_e);
-
- View* view_buffer = view_alloc();
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewConfigure_i, view_buffer);
- app->view_save = view_alloc();
- view_set_context(app->view_save, app);
- view_set_enter_callback(app->view_save, key_copier_view_save_callback);
- view_set_previous_callback(app->view_save, key_copier_navigation_submenu_callback);
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewSave, app->view_save);
- app->view_load = view_alloc();
- view_set_context(app->view_load, app);
- view_set_enter_callback(app->view_load, t5577_writer_view_load_callback);
- view_set_previous_callback(app->view_load, key_copier_navigation_submenu_callback);
- view_dispatcher_add_view(app->view_dispatcher, KeyCopierViewLoad, app->view_load);
- app->widget_about = widget_alloc();
- widget_add_text_scroll_element(
- app->widget_about,
- 0,
- 0,
- 128,
- 64,
- "Key Maker App 0.3\n\nTo measure your key:\n\n1. Place it on top of the screen.\n\n2. Use the horizontal lines to align your key.\n\n3. Adjust each pin's bitting depth until they match. It's easier if you close one eye.\n\n4. Save the bitting pattern and key format by pressing the OK button or in the Config menu.\n\nGithub: github.com/zinongli/KeyCopier \n\nSpecial thanks to Derek Jamison's Skeleton App Template.");
- view_set_previous_callback(
- widget_get_view(app->widget_about), key_copier_navigation_submenu_callback);
- view_dispatcher_add_view(
- app->view_dispatcher, KeyCopierViewAbout, widget_get_view(app->widget_about));
- app->notifications = furi_record_open(RECORD_NOTIFICATION);
- #ifdef BACKLIGHT_ON
- notification_message(app->notifications, &sequence_display_backlight_enforce_on);
- #endif
- return app;
- }
- /**
- * @brief Free the key_copier application.
- * @details This function frees the key_copier application resources.
- * @param app The key_copier application object.
- */
- static void key_copier_app_free(KeyCopierApp* app) {
- #ifdef BACKLIGHT_ON
- notification_message(app->notifications, &sequence_display_backlight_enforce_auto);
- #endif
- furi_record_close(RECORD_NOTIFICATION);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewTextInput);
- text_input_free(app->text_input);
- free(app->temp_buffer);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewAbout);
- widget_free(app->widget_about);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewMeasure);
- with_view_model(
- app->view_measure,
- KeyCopierModel * model,
- {
- if(model->depth != NULL) {
- free(model->depth);
- }
- },
- false);
- view_free(app->view_measure);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewConfigure_e);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewConfigure_i);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewSave);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewLoad);
- variable_item_list_free(app->variable_item_list_config);
- view_dispatcher_remove_view(app->view_dispatcher, KeyCopierViewSubmenu);
- submenu_free(app->submenu);
- view_dispatcher_free(app->view_dispatcher);
- furi_record_close(RECORD_GUI);
- free(app);
- }
- /**
- * @brief Main function for key_copier application.
- * @details This function is the entry point for the key_copier application. It should be defined in
- * application.fam as the entry_point setting.
- * @param _p Input parameter - unused
- * @return 0 - Success
- */
- int32_t main_key_copier_app(void* _p) {
- UNUSED(_p);
- KeyCopierApp* app = key_copier_app_alloc();
- view_dispatcher_run(app->view_dispatcher);
- key_copier_app_free(app);
- return 0;
- }
|