diff --git a/applications/external/wifi_marauder_companion/application.fam b/applications/external/wifi_marauder_companion/application.fam index 66255706a..d7bde185e 100644 --- a/applications/external/wifi_marauder_companion/application.fam +++ b/applications/external/wifi_marauder_companion/application.fam @@ -8,4 +8,5 @@ App( order=90, fap_icon="wifi_10px.png", fap_category="GPIO", + fap_icon_assets="assets", ) diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png new file mode 100644 index 000000000..7cc0759a8 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyBackspaceSelected_16x9.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png new file mode 100644 index 000000000..9946232d9 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeyBackspace_16x9.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png new file mode 100644 index 000000000..eeb3569d3 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeySaveSelected_24x11.png differ diff --git a/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png new file mode 100644 index 000000000..e7dba987a Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/KeySave_24x11.png differ diff --git a/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png new file mode 100644 index 000000000..d766ffbb4 Binary files /dev/null and b/applications/external/wifi_marauder_companion/assets/WarningDolphin_45x42.png differ diff --git a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c index b721e868d..e6091a410 100644 --- a/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c +++ b/applications/external/wifi_marauder_companion/scenes/wifi_marauder_scene_text_input.c @@ -44,24 +44,24 @@ void wifi_marauder_scene_text_input_on_enter(void* context) { } // Setup view - TextInput* text_input = app->text_input; + WIFI_TextInput* text_input = app->text_input; // Add help message to header if(app->special_case_input_step == 1) { - text_input_set_header_text(text_input, "Enter source MAC"); + wifi_text_input_set_header_text(text_input, "Enter source MAC"); } else if(0 == strncmp("ssid -a -g", app->selected_tx_string, strlen("ssid -a -g"))) { - text_input_set_header_text(text_input, "Enter # SSIDs to generate"); + wifi_text_input_set_header_text(text_input, "Enter # SSIDs to generate"); } else if(0 == strncmp("ssid -a -n", app->selected_tx_string, strlen("ssid -a -n"))) { - text_input_set_header_text(text_input, "Enter SSID name to add"); + wifi_text_input_set_header_text(text_input, "Enter SSID name to add"); } else if(0 == strncmp("ssid -r", app->selected_tx_string, strlen("ssid -r"))) { - text_input_set_header_text(text_input, "Remove target from SSID list"); + wifi_text_input_set_header_text(text_input, "Remove target from SSID list"); } else if(0 == strncmp("select -a", app->selected_tx_string, strlen("select -a"))) { - text_input_set_header_text(text_input, "Add target from AP list"); + wifi_text_input_set_header_text(text_input, "Add target from AP list"); } else if(0 == strncmp("select -s", app->selected_tx_string, strlen("select -s"))) { - text_input_set_header_text(text_input, "Add target from SSID list"); + wifi_text_input_set_header_text(text_input, "Add target from SSID list"); } else { - text_input_set_header_text(text_input, "Add command arguments"); + wifi_text_input_set_header_text(text_input, "Add command arguments"); } - text_input_set_result_callback( + wifi_text_input_set_result_callback( text_input, wifi_marauder_scene_text_input_callback, app, @@ -84,7 +84,7 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev consumed = true; } else if(event.event == WifiMarauderEventSaveSourceMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_src_addr, @@ -106,12 +106,12 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev // Advance scene to input destination MAC, clear text input app->special_case_input_step = 2; bzero(app->text_input_store, WIFI_MARAUDER_TEXT_INPUT_STORE_SIZE); - text_input_set_header_text(app->text_input, "Enter destination MAC"); + wifi_text_input_set_header_text(app->text_input, "Enter destination MAC"); } consumed = true; } else if(event.event == WifiMarauderEventSaveDestinationMac) { if(12 != strlen(app->text_input_store)) { - text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); + wifi_text_input_set_header_text(app->text_input, "MAC must be 12 hex chars!"); } else { snprintf( app->special_case_input_dst_addr, @@ -150,5 +150,5 @@ bool wifi_marauder_scene_text_input_on_event(void* context, SceneManagerEvent ev void wifi_marauder_scene_text_input_on_exit(void* context) { WifiMarauderApp* app = context; - text_input_reset(app->text_input); + wifi_text_input_reset(app->text_input); } diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app.c b/applications/external/wifi_marauder_companion/wifi_marauder_app.c index a5521d49a..9958b09eb 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app.c +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app.c @@ -64,9 +64,11 @@ WifiMarauderApp* wifi_marauder_app_alloc() { app->text_box_store = furi_string_alloc(); furi_string_reserve(app->text_box_store, WIFI_MARAUDER_TEXT_BOX_STORE_SIZE); - app->text_input = text_input_alloc(); + app->text_input = wifi_text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, WifiMarauderAppViewTextInput, text_input_get_view(app->text_input)); + app->view_dispatcher, + WifiMarauderAppViewTextInput, + wifi_text_input_get_view(app->text_input)); app->widget = widget_alloc(); view_dispatcher_add_view( @@ -135,7 +137,7 @@ void wifi_marauder_app_free(WifiMarauderApp* app) { widget_free(app->widget); text_box_free(app->text_box); furi_string_free(app->text_box_store); - text_input_free(app->text_input); + wifi_text_input_free(app->text_input); storage_file_free(app->capture_file); storage_file_free(app->log_file); storage_file_free(app->save_pcap_setting_file); diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h index 39b48fae3..1bc8f78fe 100644 --- a/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h +++ b/applications/external/wifi_marauder_companion/wifi_marauder_app_i.h @@ -12,9 +12,9 @@ #include #include #include -#include #include #include +#include "wifi_marauder_text_input.h" #include #include @@ -42,7 +42,7 @@ struct WifiMarauderApp { FuriString* text_box_store; size_t text_box_store_strlen; TextBox* text_box; - TextInput* text_input; + WIFI_TextInput* text_input; Storage* storage; File* capture_file; File* log_file; diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c new file mode 100644 index 000000000..d9eee83c9 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.c @@ -0,0 +1,625 @@ +#include "wifi_marauder_text_input.h" +#include +#include "ESP32_WiFi_Marauder_icons.h" +#include "wifi_marauder_app_i.h" +#include + +struct WIFI_TextInput { + View* view; + FuriTimer* timer; +}; + +typedef struct { + const char text; + const uint8_t x; + const uint8_t y; +} WIFI_TextInputKey; + +typedef struct { + const char* header; + char* text_buffer; + size_t text_buffer_size; + bool clear_default_text; + + WIFI_TextInputCallback callback; + void* callback_context; + + uint8_t selected_row; + uint8_t selected_column; + + WIFI_TextInputValidatorCallback validator_callback; + void* validator_callback_context; + FuriString* validator_text; + bool valadator_message_visible; +} WIFI_TextInputModel; + +static const uint8_t keyboard_origin_x = 1; +static const uint8_t keyboard_origin_y = 29; +static const uint8_t keyboard_row_count = 4; + +#define ENTER_KEY '\r' +#define BACKSPACE_KEY '\b' + +static const WIFI_TextInputKey keyboard_keys_row_1[] = { + {'{', 1, 0}, + {'(', 9, 0}, + {'[', 17, 0}, + {'|', 25, 0}, + {'@', 33, 0}, + {'&', 41, 0}, + {'#', 49, 0}, + {';', 57, 0}, + {'^', 65, 0}, + {'*', 73, 0}, + {'`', 81, 0}, + {'"', 89, 0}, + {'~', 97, 0}, + {'\'', 105, 0}, + {'.', 113, 0}, + {'/', 120, 0}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_2[] = { + {'q', 1, 10}, + {'w', 9, 10}, + {'e', 17, 10}, + {'r', 25, 10}, + {'t', 33, 10}, + {'y', 41, 10}, + {'u', 49, 10}, + {'i', 57, 10}, + {'o', 65, 10}, + {'p', 73, 10}, + {'0', 81, 10}, + {'1', 89, 10}, + {'2', 97, 10}, + {'3', 105, 10}, + {'=', 113, 10}, + {'-', 120, 10}, +}; + +static const WIFI_TextInputKey keyboard_keys_row_3[] = { + {'a', 1, 21}, + {'s', 9, 21}, + {'d', 18, 21}, + {'f', 25, 21}, + {'g', 33, 21}, + {'h', 41, 21}, + {'j', 49, 21}, + {'k', 57, 21}, + {'l', 65, 21}, + {BACKSPACE_KEY, 72, 13}, + {'4', 89, 21}, + {'5', 97, 21}, + {'6', 105, 21}, + {'$', 113, 21}, + {'%', 120, 21}, + +}; + +static const WIFI_TextInputKey keyboard_keys_row_4[] = { + {'z', 1, 33}, + {'x', 9, 33}, + {'c', 18, 33}, + {'v', 25, 33}, + {'b', 33, 33}, + {'n', 41, 33}, + {'m', 49, 33}, + {'_', 57, 33}, + {ENTER_KEY, 64, 24}, + {'7', 89, 33}, + {'8', 97, 33}, + {'9', 105, 33}, + {'!', 113, 33}, + {'+', 120, 33}, +}; + +static uint8_t get_row_size(uint8_t row_index) { + uint8_t row_size = 0; + + switch(row_index + 1) { + case 1: + row_size = sizeof(keyboard_keys_row_1) / sizeof(WIFI_TextInputKey); + break; + case 2: + row_size = sizeof(keyboard_keys_row_2) / sizeof(WIFI_TextInputKey); + break; + case 3: + row_size = sizeof(keyboard_keys_row_3) / sizeof(WIFI_TextInputKey); + break; + case 4: + row_size = sizeof(keyboard_keys_row_4) / sizeof(WIFI_TextInputKey); + break; + } + + return row_size; +} + +static const WIFI_TextInputKey* get_row(uint8_t row_index) { + const WIFI_TextInputKey* row = NULL; + + switch(row_index + 1) { + case 1: + row = keyboard_keys_row_1; + break; + case 2: + row = keyboard_keys_row_2; + break; + case 3: + row = keyboard_keys_row_3; + break; + case 4: + row = keyboard_keys_row_4; + break; + } + + return row; +} + +static char get_selected_char(WIFI_TextInputModel* model) { + return get_row(model->selected_row)[model->selected_column].text; +} + +static bool char_is_lowercase(char letter) { + return (letter >= 0x61 && letter <= 0x7A); +} + +static char char_to_uppercase(const char letter) { + switch(letter) { + case '_': + return 0x20; + break; + case '(': + return 0x29; + break; + case '{': + return 0x7d; + break; + case '[': + return 0x5d; + break; + case '/': + return 0x5c; + break; + case ';': + return 0x3a; + break; + case '.': + return 0x2c; + break; + case '!': + return 0x3f; + break; + case '<': + return 0x3e; + break; + } + if(char_is_lowercase(letter)) { + return (letter - 0x20); + } else { + return letter; + } +} + +static void wifi_text_input_backspace_cb(WIFI_TextInputModel* model) { + uint8_t text_length = model->clear_default_text ? 1 : strlen(model->text_buffer); + if(text_length > 0) { + model->text_buffer[text_length - 1] = 0; + } +} + +static void wifi_text_input_view_draw_callback(Canvas* canvas, void* _model) { + WIFI_TextInputModel* model = _model; + //uint8_t text_length = model->text_buffer ? strlen(model->text_buffer) : 0; + uint8_t needed_string_width = canvas_width(canvas) - 8; + uint8_t start_pos = 4; + + const char* text = model->text_buffer; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_str(canvas, 2, 7, model->header); + elements_slightly_rounded_frame(canvas, 1, 8, 126, 12); + + if(canvas_string_width(canvas, text) > needed_string_width) { + canvas_draw_str(canvas, start_pos, 17, "..."); + start_pos += 6; + needed_string_width -= 8; + } + + while(text != 0 && canvas_string_width(canvas, text) > needed_string_width) { + text++; + } + + if(model->clear_default_text) { + elements_slightly_rounded_box( + canvas, start_pos - 1, 14, canvas_string_width(canvas, text) + 2, 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 1, 18, "|"); + canvas_draw_str(canvas, start_pos + canvas_string_width(canvas, text) + 2, 18, "|"); + } + canvas_draw_str(canvas, start_pos, 17, text); + + canvas_set_font(canvas, FontKeyboard); + + for(uint8_t row = 0; row <= keyboard_row_count; row++) { + const uint8_t column_count = get_row_size(row); + const WIFI_TextInputKey* keys = get_row(row); + + for(size_t column = 0; column < column_count; column++) { + if(keys[column].text == ENTER_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySaveSelected_24x11); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeySave_24x11); + } + } else if(keys[column].text == BACKSPACE_KEY) { + canvas_set_color(canvas, ColorBlack); + if(model->selected_row == row && model->selected_column == column) { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspaceSelected_16x9); + } else { + canvas_draw_icon( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + &I_KeyBackspace_16x9); + } + } else { + if(model->selected_row == row && model->selected_column == column) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, + keyboard_origin_x + keys[column].x - 1, + keyboard_origin_y + keys[column].y - 8, + 7, + 10); + canvas_set_color(canvas, ColorWhite); + } else { + canvas_set_color(canvas, ColorBlack); + } + + canvas_draw_glyph( + canvas, + keyboard_origin_x + keys[column].x, + keyboard_origin_y + keys[column].y, + keys[column].text); + } + } + } + if(model->valadator_message_visible) { + canvas_set_font(canvas, FontSecondary); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, 8, 10, 110, 48); + canvas_set_color(canvas, ColorBlack); + canvas_draw_icon(canvas, 10, 14, &I_WarningDolphin_45x42); + canvas_draw_rframe(canvas, 8, 8, 112, 50, 3); + canvas_draw_rframe(canvas, 9, 9, 110, 48, 2); + elements_multiline_text(canvas, 62, 20, furi_string_get_cstr(model->validator_text)); + canvas_set_font(canvas, FontKeyboard); + } +} + +static void + wifi_text_input_handle_up(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row > 0) { + model->selected_row--; + if(model->selected_column > get_row_size(model->selected_row) - 6) { + model->selected_column = model->selected_column + 1; + } + } +} + +static void + wifi_text_input_handle_down(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_row < keyboard_row_count - 1) { + model->selected_row++; + if(model->selected_column > get_row_size(model->selected_row) - 4) { + model->selected_column = model->selected_column - 1; + } + } +} + +static void + wifi_text_input_handle_left(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column > 0) { + model->selected_column--; + } else { + model->selected_column = get_row_size(model->selected_row) - 1; + } +} + +static void + wifi_text_input_handle_right(WIFI_TextInput* wifi_text_input, WIFI_TextInputModel* model) { + UNUSED(wifi_text_input); + if(model->selected_column < get_row_size(model->selected_row) - 1) { + model->selected_column++; + } else { + model->selected_column = 0; + } +} + +static void wifi_text_input_handle_ok( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputModel* model, + bool shift) { + char selected = get_selected_char(model); + uint8_t text_length = strlen(model->text_buffer); + + if(shift) { + selected = char_to_uppercase(selected); + } + + if(selected == ENTER_KEY) { + if(model->validator_callback && + (!model->validator_callback( + model->text_buffer, model->validator_text, model->validator_callback_context))) { + model->valadator_message_visible = true; + furi_timer_start(wifi_text_input->timer, furi_kernel_get_tick_frequency() * 4); + } else if(model->callback != 0 && text_length > 0) { + model->callback(model->callback_context); + } + } else if(selected == BACKSPACE_KEY) { + wifi_text_input_backspace_cb(model); + } else { + if(model->clear_default_text) { + text_length = 0; + } + if(text_length < (model->text_buffer_size - 1)) { + model->text_buffer[text_length] = selected; + model->text_buffer[text_length + 1] = 0; + } + } + model->clear_default_text = false; +} + +static bool wifi_text_input_view_input_callback(InputEvent* event, void* context) { + WIFI_TextInput* wifi_text_input = context; + furi_assert(wifi_text_input); + + bool consumed = false; + + // Acquire model + WIFI_TextInputModel* model = view_get_model(wifi_text_input->view); + + if((!(event->type == InputTypePress) && !(event->type == InputTypeRelease)) && + model->valadator_message_visible) { + model->valadator_message_visible = false; + consumed = true; + } else if(event->type == InputTypeShort) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, false); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeLong) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyOk: + wifi_text_input_handle_ok(wifi_text_input, model, true); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } else if(event->type == InputTypeRepeat) { + consumed = true; + switch(event->key) { + case InputKeyUp: + wifi_text_input_handle_up(wifi_text_input, model); + break; + case InputKeyDown: + wifi_text_input_handle_down(wifi_text_input, model); + break; + case InputKeyLeft: + wifi_text_input_handle_left(wifi_text_input, model); + break; + case InputKeyRight: + wifi_text_input_handle_right(wifi_text_input, model); + break; + case InputKeyBack: + wifi_text_input_backspace_cb(model); + break; + default: + consumed = false; + break; + } + } + + // Commit model + view_commit_model(wifi_text_input->view, consumed); + + return consumed; +} + +void wifi_text_input_timer_callback(void* context) { + furi_assert(context); + WIFI_TextInput* wifi_text_input = context; + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->valadator_message_visible = false; }, + true); +} + +WIFI_TextInput* wifi_text_input_alloc() { + WIFI_TextInput* wifi_text_input = malloc(sizeof(WIFI_TextInput)); + wifi_text_input->view = view_alloc(); + view_set_context(wifi_text_input->view, wifi_text_input); + view_allocate_model(wifi_text_input->view, ViewModelTypeLocking, sizeof(WIFI_TextInputModel)); + view_set_draw_callback(wifi_text_input->view, wifi_text_input_view_draw_callback); + view_set_input_callback(wifi_text_input->view, wifi_text_input_view_input_callback); + + wifi_text_input->timer = + furi_timer_alloc(wifi_text_input_timer_callback, FuriTimerTypeOnce, wifi_text_input); + + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { model->validator_text = furi_string_alloc(); }, + false); + + wifi_text_input_reset(wifi_text_input); + + return wifi_text_input; +} + +void wifi_text_input_free(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { furi_string_free(model->validator_text); }, + false); + + // Send stop command + furi_timer_stop(wifi_text_input->timer); + // Release allocated memory + furi_timer_free(wifi_text_input->timer); + + view_free(wifi_text_input->view); + + free(wifi_text_input); +} + +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->text_buffer_size = 0; + model->header = ""; + model->selected_row = 0; + model->selected_column = 0; + model->clear_default_text = false; + model->text_buffer = NULL; + model->text_buffer_size = 0; + model->callback = NULL; + model->callback_context = NULL; + model->validator_callback = NULL; + model->validator_callback_context = NULL; + furi_string_reset(model->validator_text); + model->valadator_message_visible = false; + }, + true); +} + +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input) { + furi_assert(wifi_text_input); + return wifi_text_input->view; +} + +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->callback = callback; + model->callback_context = callback_context; + model->text_buffer = text_buffer; + model->text_buffer_size = text_buffer_size; + model->clear_default_text = clear_default_text; + if(text_buffer && text_buffer[0] != '\0') { + // Set focus on Save + model->selected_row = 2; + model->selected_column = 8; + } + }, + true); +} + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context) { + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { + model->validator_callback = callback; + model->validator_callback_context = callback_context; + }, + true); +} + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input) { + WIFI_TextInputValidatorCallback validator_callback = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback = model->validator_callback; }, + false); + return validator_callback; +} + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input) { + void* validator_callback_context = NULL; + with_view_model( + wifi_text_input->view, + WIFI_TextInputModel * model, + { validator_callback_context = model->validator_callback_context; }, + false); + return validator_callback_context; +} + +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text) { + with_view_model( + wifi_text_input->view, WIFI_TextInputModel * model, { model->header = text; }, true); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h new file mode 100644 index 000000000..b6b1f7bdf --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_text_input.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include "wifi_marauder_validators.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Text input anonymous structure */ +typedef struct WIFI_TextInput WIFI_TextInput; +typedef void (*WIFI_TextInputCallback)(void* context); +typedef bool (*WIFI_TextInputValidatorCallback)(const char* text, FuriString* error, void* context); + +/** Allocate and initialize text input + * + * This text input is used to enter string + * + * @return WIFI_TextInput instance + */ +WIFI_TextInput* wifi_text_input_alloc(); + +/** Deinitialize and free text input + * + * @param wifi_text_input WIFI_TextInput instance + */ +void wifi_text_input_free(WIFI_TextInput* wifi_text_input); + +/** Clean text input view Note: this function does not free memory + * + * @param wifi_text_input Text input instance + */ +void wifi_text_input_reset(WIFI_TextInput* wifi_text_input); + +/** Get text input view + * + * @param wifi_text_input WIFI_TextInput instance + * + * @return View instance that can be used for embedding + */ +View* wifi_text_input_get_view(WIFI_TextInput* wifi_text_input); + +/** Set text input result callback + * + * @param wifi_text_input WIFI_TextInput instance + * @param callback callback fn + * @param callback_context callback context + * @param text_buffer pointer to YOUR text buffer, that we going + * to modify + * @param text_buffer_size YOUR text buffer size in bytes. Max string + * length will be text_buffer_size-1. + * @param clear_default_text clear text from text_buffer on first OK + * event + */ +void wifi_text_input_set_result_callback( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputCallback callback, + void* callback_context, + char* text_buffer, + size_t text_buffer_size, + bool clear_default_text); + +void wifi_text_input_set_validator( + WIFI_TextInput* wifi_text_input, + WIFI_TextInputValidatorCallback callback, + void* callback_context); + +WIFI_TextInputValidatorCallback + wifi_text_input_get_validator_callback(WIFI_TextInput* wifi_text_input); + +void* wifi_text_input_get_validator_callback_context(WIFI_TextInput* wifi_text_input); + +/** Set text input header text + * + * @param wifi_text_input WIFI_TextInput instance + * @param text text to be shown + */ +void wifi_text_input_set_header_text(WIFI_TextInput* wifi_text_input, const char* text); + +#ifdef __cplusplus +} +#endif diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.c b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c new file mode 100644 index 000000000..5bec88269 --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.c @@ -0,0 +1,57 @@ +#include +#include "wifi_marauder_validators.h" +#include + +struct ValidatorIsFile { + char* app_path_folder; + const char* app_extension; + char* current_name; +}; + +bool validator_is_file_callback(const char* text, FuriString* error, void* context) { + furi_assert(context); + ValidatorIsFile* instance = context; + + if(instance->current_name != NULL) { + if(strcmp(instance->current_name, text) == 0) { + return true; + } + } + + bool ret = true; + FuriString* path = furi_string_alloc_printf( + "%s/%s%s", instance->app_path_folder, text, instance->app_extension); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), NULL) == FSE_OK) { + ret = false; + furi_string_printf(error, "This name\nexists!\nChoose\nanother one."); + } else { + ret = true; + } + furi_string_free(path); + furi_record_close(RECORD_STORAGE); + + return ret; +} + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name) { + ValidatorIsFile* instance = malloc(sizeof(ValidatorIsFile)); + + instance->app_path_folder = strdup(app_path_folder); + instance->app_extension = app_extension; + if(current_name != NULL) { + instance->current_name = strdup(current_name); + } + + return instance; +} + +void validator_is_file_free(ValidatorIsFile* instance) { + furi_assert(instance); + free(instance->app_path_folder); + free(instance->current_name); + free(instance); +} diff --git a/applications/external/wifi_marauder_companion/wifi_marauder_validators.h b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h new file mode 100644 index 000000000..d9200b6db --- /dev/null +++ b/applications/external/wifi_marauder_companion/wifi_marauder_validators.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif +typedef struct ValidatorIsFile ValidatorIsFile; + +ValidatorIsFile* validator_is_file_alloc_init( + const char* app_path_folder, + const char* app_extension, + const char* current_name); + +void validator_is_file_free(ValidatorIsFile* instance); + +bool validator_is_file_callback(const char* text, FuriString* error, void* context); + +#ifdef __cplusplus +} +#endif