diff --git a/applications/system/hex_viewer/application.fam b/applications/system/hex_viewer/application.fam index 821eead69..013ea09e5 100644 --- a/applications/system/hex_viewer/application.fam +++ b/applications/system/hex_viewer/application.fam @@ -1,6 +1,6 @@ App( appid="hex_viewer", - name="Hex Viewer", + name="HEX Viewer", apptype=FlipperAppType.EXTERNAL, entry_point="hex_viewer_app", requires=[ @@ -8,10 +8,11 @@ App( "dialogs", ], stack_size=2 * 1024, + order=20, fap_icon="icons/hex_10px.png", - fap_category="Tools", fap_icon_assets="icons", + fap_category="Tools", fap_author="@QtRoS", - fap_version="1.1", + fap_version="2.0", fap_description="App allows to view various files as HEX.", ) diff --git a/applications/system/hex_viewer/helpers/hex_viewer_custom_event.h b/applications/system/hex_viewer/helpers/hex_viewer_custom_event.h new file mode 100644 index 000000000..f6dde56e6 --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_custom_event.h @@ -0,0 +1,62 @@ +#pragma once + +typedef enum { + HexViewerCustomEventStartscreenUp, + HexViewerCustomEventStartscreenDown, + HexViewerCustomEventStartscreenLeft, + HexViewerCustomEventStartscreenRight, + HexViewerCustomEventStartscreenOk, + HexViewerCustomEventStartscreenBack, + HexViewerCustomEventScene1Up, + HexViewerCustomEventScene1Down, + HexViewerCustomEventScene1Left, + HexViewerCustomEventScene1Right, + HexViewerCustomEventScene1Ok, + HexViewerCustomEventScene1Back, + HexViewerCustomEventScene2Up, + HexViewerCustomEventScene2Down, + HexViewerCustomEventScene2Left, + HexViewerCustomEventScene2Right, + HexViewerCustomEventScene2Ok, + HexViewerCustomEventScene2Back, +} HexViewerCustomEvent; + +enum HexViewerCustomEventType { + // Reserve first 100 events for button types and indexes, starting from 0 + HexViewerCustomEventMenuVoid, + HexViewerCustomEventMenuSelected, + HexViewerCustomEventMenuPercentEntered, +}; + +#pragma pack(push, 1) +typedef union { + uint32_t packed_value; + struct { + uint16_t type; + int16_t value; + } content; +} HexViewerCustomEventMenu; +#pragma pack(pop) + +static inline uint32_t hex_viewer_custom_menu_event_pack(uint16_t type, int16_t value) { + HexViewerCustomEventMenu event = {.content = {.type = type, .value = value}}; + return event.packed_value; +} +static inline void + hex_viewer_custom_menu_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { + HexViewerCustomEventMenu event = {.packed_value = packed_value}; + if(type) *type = event.content.type; + if(value) *value = event.content.value; +} + +static inline uint16_t hex_viewer_custom_menu_event_get_type(uint32_t packed_value) { + uint16_t type; + hex_viewer_custom_menu_event_unpack(packed_value, &type, NULL); + return type; +} + +static inline int16_t hex_viewer_custom_menu_event_get_value(uint32_t packed_value) { + int16_t value; + hex_viewer_custom_menu_event_unpack(packed_value, NULL, &value); + return value; +} \ No newline at end of file diff --git a/applications/system/hex_viewer/helpers/hex_viewer_haptic.c b/applications/system/hex_viewer/helpers/hex_viewer_haptic.c new file mode 100644 index 000000000..b23eabbf1 --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_haptic.c @@ -0,0 +1,36 @@ +#include "hex_viewer_haptic.h" +#include "../hex_viewer.h" + + +void hex_viewer_play_happy_bump(void* context) { + HexViewer* app = context; + if (app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void hex_viewer_play_bad_bump(void* context) { + HexViewer* app = context; + if (app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void hex_viewer_play_long_bump(void* context) { + HexViewer* app = context; + if (app->haptic != 1) { + return; + } + for (int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} diff --git a/applications/system/hex_viewer/helpers/hex_viewer_haptic.h b/applications/system/hex_viewer/helpers/hex_viewer_haptic.h new file mode 100644 index 000000000..c9eccb17e --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_haptic.h @@ -0,0 +1,8 @@ +#include + +void hex_viewer_play_happy_bump(void* context); + +void hex_viewer_play_bad_bump(void* context); + +void hex_viewer_play_long_bump(void* context); + diff --git a/applications/system/hex_viewer/helpers/hex_viewer_led.c b/applications/system/hex_viewer/helpers/hex_viewer_led.c new file mode 100644 index 000000000..454c3c414 --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_led.c @@ -0,0 +1,39 @@ +#include "hex_viewer_led.h" +#include "../hex_viewer.h" + + + +void hex_viewer_led_set_rgb(void* context, int red, int green, int blue) { + HexViewer* app = context; + if (app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + furi_thread_flags_wait(0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set +} + +void hex_viewer_led_reset(void* context) { + HexViewer* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + + furi_thread_flags_wait(0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set +} diff --git a/applications/system/hex_viewer/helpers/hex_viewer_led.h b/applications/system/hex_viewer/helpers/hex_viewer_led.h new file mode 100644 index 000000000..060749f0c --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_led.h @@ -0,0 +1,6 @@ + + +void hex_viewer_led_set_rgb(void* context, int red, int green, int blue); + +void hex_viewer_led_reset(void* context); + diff --git a/applications/system/hex_viewer/helpers/hex_viewer_speaker.c b/applications/system/hex_viewer/helpers/hex_viewer_speaker.c new file mode 100644 index 000000000..f834a565f --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_speaker.c @@ -0,0 +1,27 @@ +#include "hex_viewer_speaker.h" +#include "../hex_viewer.h" + +#define NOTE_INPUT 587.33f + +void hex_viewer_play_input_sound(void* context) { + HexViewer* app = context; + if (app->speaker != 1) { + return; + } + float volume = 1.0f; + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_INPUT, volume); + } + +} + +void hex_viewer_stop_all_sound(void* context) { + HexViewer* app = context; + if (app->speaker != 1) { + return; + } + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} diff --git a/applications/system/hex_viewer/helpers/hex_viewer_speaker.h b/applications/system/hex_viewer/helpers/hex_viewer_speaker.h new file mode 100644 index 000000000..747d79146 --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_speaker.h @@ -0,0 +1,4 @@ +#define NOTE_INPUT 587.33f + +void hex_viewer_play_input_sound(void* context); +void hex_viewer_stop_all_sound(void* context); diff --git a/applications/system/hex_viewer/helpers/hex_viewer_storage.c b/applications/system/hex_viewer/helpers/hex_viewer_storage.c new file mode 100644 index 000000000..6b4217dc2 --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_storage.c @@ -0,0 +1,171 @@ +#include "hex_viewer_storage.h" + +static Storage* hex_viewer_open_storage() { + return furi_record_open(RECORD_STORAGE); +} + +static void hex_viewer_close_storage() { + furi_record_close(RECORD_STORAGE); +} + +static void hex_viewer_close_config_file(FlipperFormat* file) { + if(file == NULL) return; + flipper_format_file_close(file); + flipper_format_free(file); +} + +void hex_viewer_save_settings(void* context) { + HexViewer* app = context; + if(app->save_settings == 0) { + return; + } + + FURI_LOG_D(TAG, "Saving Settings"); + Storage* storage = hex_viewer_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + // Overwrite wont work, so delete first + if(storage_file_exists(storage, HEX_VIEWER_SETTINGS_SAVE_PATH)) { + storage_simply_remove(storage, HEX_VIEWER_SETTINGS_SAVE_PATH); + } + + // Open File, create if not exists + if(!storage_common_stat(storage, HEX_VIEWER_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { + FURI_LOG_D( + TAG, "Config file %s is not found. Will create new.", HEX_VIEWER_SETTINGS_SAVE_PATH); + if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { + FURI_LOG_D( + TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); + if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { + FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); + } + } + } + + if(!flipper_format_file_open_new(fff_file, HEX_VIEWER_SETTINGS_SAVE_PATH)) { + //totp_close_config_file(fff_file); + FURI_LOG_E(TAG, "Error creating new file %s", HEX_VIEWER_SETTINGS_SAVE_PATH); + hex_viewer_close_storage(); + return; + } + + // Store Settings + flipper_format_write_header_cstr( + fff_file, HEX_VIEWER_SETTINGS_HEADER, HEX_VIEWER_SETTINGS_FILE_VERSION); + flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_write_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_write_uint32( + fff_file, HEX_VIEWER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + if(!flipper_format_rewind(fff_file)) { + hex_viewer_close_config_file(fff_file); + FURI_LOG_E(TAG, "Rewind error"); + hex_viewer_close_storage(); + return; + } + + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); +} + +void hex_viewer_read_settings(void* context) { + HexViewer* app = context; + Storage* storage = hex_viewer_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + if(storage_common_stat(storage, HEX_VIEWER_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); + return; + } + uint32_t file_version; + FuriString* temp_str = furi_string_alloc(); + + if(!flipper_format_file_open_existing(fff_file, HEX_VIEWER_SETTINGS_SAVE_PATH)) { + FURI_LOG_E(TAG, "Cannot open file %s", HEX_VIEWER_SETTINGS_SAVE_PATH); + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); + return; + } + + if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { + FURI_LOG_E(TAG, "Missing Header Data"); + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); + return; + } + + if(file_version < HEX_VIEWER_SETTINGS_FILE_VERSION) { + FURI_LOG_I(TAG, "old config version, will be removed."); + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); + return; + } + + flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_read_uint32(fff_file, HEX_VIEWER_SETTINGS_KEY_LED, &app->led, 1); + flipper_format_read_uint32( + fff_file, HEX_VIEWER_SETTINGS_KEY_SAVE_SETTINGS, &app->save_settings, 1); + + flipper_format_rewind(fff_file); + + hex_viewer_close_config_file(fff_file); + hex_viewer_close_storage(); +} + +bool hex_viewer_open_file(void* context, const char* file_path) { + HexViewer* hex_viewer = context; + furi_assert(hex_viewer); + furi_assert(file_path); + + // TODO Separate function? + if(hex_viewer->model->stream) { + buffered_file_stream_close(hex_viewer->model->stream); + stream_free(hex_viewer->model->stream); // TODO Check + hex_viewer->model->file_offset = 0; + } + + hex_viewer->model->stream = buffered_file_stream_alloc(hex_viewer->storage); + bool isOk = true; + + do { + if(!buffered_file_stream_open( + hex_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); + isOk = false; + break; + }; + + hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); + } while(false); + + return isOk; +} + +bool hex_viewer_read_file(void* context) { + HexViewer* hex_viewer = context; + furi_assert(hex_viewer); + furi_assert(hex_viewer->model->stream); + furi_assert(hex_viewer->model->file_offset % HEX_VIEWER_BYTES_PER_LINE == 0); + + memset(hex_viewer->model->file_bytes, 0x0, HEX_VIEWER_BUF_SIZE); + bool isOk = true; + + do { + uint32_t offset = hex_viewer->model->file_offset; + if(!stream_seek(hex_viewer->model->stream, offset, true)) { + FURI_LOG_E(TAG, "Unable to seek stream"); + isOk = false; + break; + } + + hex_viewer->model->file_read_bytes = stream_read( + hex_viewer->model->stream, + (uint8_t*)hex_viewer->model->file_bytes, + HEX_VIEWER_BUF_SIZE); + } while(false); + + return isOk; +} \ No newline at end of file diff --git a/applications/system/hex_viewer/helpers/hex_viewer_storage.h b/applications/system/hex_viewer/helpers/hex_viewer_storage.h new file mode 100644 index 000000000..cd0c5277b --- /dev/null +++ b/applications/system/hex_viewer/helpers/hex_viewer_storage.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include +#include "../hex_viewer.h" + +#define HEX_VIEWER_SETTINGS_FILE_VERSION 1 +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/hex_viewer") +#define HEX_VIEWER_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/hex_viewer.conf" +#define HEX_VIEWER_SETTINGS_SAVE_PATH_TMP HEX_VIEWER_SETTINGS_SAVE_PATH ".tmp" +#define HEX_VIEWER_SETTINGS_HEADER "HexViewer Config File" +#define HEX_VIEWER_SETTINGS_KEY_HAPTIC "Haptic" +#define HEX_VIEWER_SETTINGS_KEY_LED "Led" +#define HEX_VIEWER_SETTINGS_KEY_SPEAKER "Speaker" +#define HEX_VIEWER_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" + +void hex_viewer_save_settings(void* context); +void hex_viewer_read_settings(void* context); + +bool hex_viewer_open_file(void* context, const char* file_path); +bool hex_viewer_read_file(void* context); \ No newline at end of file diff --git a/applications/system/hex_viewer/hex_viewer.c b/applications/system/hex_viewer/hex_viewer.c index b98f2d867..2b5fe68f2 100644 --- a/applications/system/hex_viewer/hex_viewer.c +++ b/applications/system/hex_viewer/hex_viewer.c @@ -1,295 +1,145 @@ -#include -#include +#include "hex_viewer.h" -#include "hex_viewer_icons.h" -#include -#include -#include -#include - -#include -#include -#include -#include - -#define TAG "HexViewer" - -#define HEX_VIEWER_APP_PATH_FOLDER STORAGE_EXT_PATH_PREFIX -#define HEX_VIEWER_APP_EXTENSION "*" - -#define HEX_VIEWER_BYTES_PER_LINE 4u -#define HEX_VIEWER_LINES_ON_SCREEN 4u -#define HEX_VIEWER_BUF_SIZE (HEX_VIEWER_LINES_ON_SCREEN * HEX_VIEWER_BYTES_PER_LINE) - -typedef struct { - uint8_t file_bytes[HEX_VIEWER_LINES_ON_SCREEN][HEX_VIEWER_BYTES_PER_LINE]; - uint32_t file_offset; - uint32_t file_read_bytes; - uint32_t file_size; - Stream* stream; - bool mode; // Print address or content -} HexViewerModel; - -typedef struct { - HexViewerModel* model; - FuriMutex** mutex; - - FuriMessageQueue* input_queue; - - ViewPort* view_port; - Gui* gui; - Storage* storage; -} HexViewer; - -static void render_callback(Canvas* canvas, void* ctx) { - HexViewer* hex_viewer = ctx; - furi_check(furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - - canvas_clear(canvas); - canvas_set_color(canvas, ColorBlack); - - elements_button_left(canvas, hex_viewer->model->mode ? "Addr" : "Text"); - elements_button_right(canvas, "Info"); - - int ROW_HEIGHT = 12; - int TOP_OFFSET = 10; - int LEFT_OFFSET = 3; - - uint32_t line_count = hex_viewer->model->file_size / HEX_VIEWER_BYTES_PER_LINE; - if(hex_viewer->model->file_size % HEX_VIEWER_BYTES_PER_LINE != 0) line_count += 1; - uint32_t first_line_on_screen = hex_viewer->model->file_offset / HEX_VIEWER_BYTES_PER_LINE; - if(line_count > HEX_VIEWER_LINES_ON_SCREEN) { - uint8_t width = canvas_width(canvas); - elements_scrollbar_pos( - canvas, - width, - 0, - ROW_HEIGHT * HEX_VIEWER_LINES_ON_SCREEN, - first_line_on_screen, // TODO - line_count - (HEX_VIEWER_LINES_ON_SCREEN - 1)); - } - - char temp_buf[32]; - uint32_t row_iters = hex_viewer->model->file_read_bytes / HEX_VIEWER_BYTES_PER_LINE; - if(hex_viewer->model->file_read_bytes % HEX_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; - - for(uint32_t i = 0; i < row_iters; ++i) { - uint32_t bytes_left_per_row = - hex_viewer->model->file_read_bytes - i * HEX_VIEWER_BYTES_PER_LINE; - bytes_left_per_row = MIN(bytes_left_per_row, HEX_VIEWER_BYTES_PER_LINE); - - if(hex_viewer->model->mode) { - memcpy(temp_buf, hex_viewer->model->file_bytes[i], bytes_left_per_row); - temp_buf[bytes_left_per_row] = '\0'; - for(uint32_t j = 0; j < bytes_left_per_row; ++j) - if(!isprint((int)temp_buf[j])) temp_buf[j] = '.'; - - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); - } else { - uint32_t addr = hex_viewer->model->file_offset + i * HEX_VIEWER_BYTES_PER_LINE; - snprintf(temp_buf, 32, "%04lX", addr); - - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); - } - - char* p = temp_buf; - for(uint32_t j = 0; j < bytes_left_per_row; ++j) - p += snprintf(p, 32, "%02X ", hex_viewer->model->file_bytes[i][j]); - - canvas_set_font(canvas, FontKeyboard); - canvas_draw_str(canvas, LEFT_OFFSET + 41, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); - } - - furi_mutex_release(hex_viewer->mutex); +bool hex_viewer_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + HexViewer* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); } -static void input_callback(InputEvent* input_event, void* ctx) { - HexViewer* hex_viewer = ctx; - if(input_event->type == InputTypeShort || input_event->type == InputTypeRepeat) { - furi_message_queue_put(hex_viewer->input_queue, input_event, 0); - } +void hex_viewer_tick_event_callback(void* context) { + furi_assert(context); + HexViewer* app = context; + scene_manager_handle_tick_event(app->scene_manager); } -static HexViewer* hex_viewer_alloc() { - HexViewer* instance = malloc(sizeof(HexViewer)); - - instance->model = malloc(sizeof(HexViewerModel)); - memset(instance->model, 0x0, sizeof(HexViewerModel)); - - instance->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - - instance->input_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); - - instance->view_port = view_port_alloc(); - view_port_draw_callback_set(instance->view_port, render_callback, instance); - view_port_input_callback_set(instance->view_port, input_callback, instance); - - instance->gui = furi_record_open(RECORD_GUI); - gui_add_view_port(instance->gui, instance->view_port, GuiLayerFullscreen); - - instance->storage = furi_record_open(RECORD_STORAGE); - - return instance; +//leave app if back button pressed +bool hex_viewer_navigation_event_callback(void* context) { + furi_assert(context); + HexViewer* app = context; + return scene_manager_handle_back_event(app->scene_manager); } -static void hex_viewer_free(HexViewer* instance) { +HexViewer* hex_viewer_app_alloc() { + HexViewer* app = malloc(sizeof(HexViewer)); + + app->model = malloc(sizeof(HexViewerModel)); + memset(app->model, 0, sizeof(HexViewerModel)); + + app->gui = furi_record_open(RECORD_GUI); + app->storage = furi_record_open(RECORD_STORAGE); + app->notification = furi_record_open(RECORD_NOTIFICATION); + + //Turn backlight on, believe me this makes testing your app easier + notification_message(app->notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&hex_viewer_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, hex_viewer_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, hex_viewer_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, hex_viewer_custom_event_callback); + + app->submenu = submenu_alloc(); + app->text_input = text_input_alloc(); + + // Set defaults, in case no config loaded + app->haptic = 1; + app->speaker = 1; + app->led = 1; + app->save_settings = 1; + + // Used for File Browser + app->dialogs = furi_record_open(RECORD_DIALOGS); + app->file_path = furi_string_alloc(); + + // Load configs + hex_viewer_read_settings(app); + + view_dispatcher_add_view( + app->view_dispatcher, HexViewerViewIdMenu, submenu_get_view(app->submenu)); + + app->hex_viewer_startscreen = hex_viewer_startscreen_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + HexViewerViewIdStartscreen, + hex_viewer_startscreen_get_view(app->hex_viewer_startscreen)); + + view_dispatcher_add_view( + app->view_dispatcher, HexViewerViewIdScroll, text_input_get_view(app->text_input)); + + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + HexViewerViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + + //End Scene Additions + + return app; +} + +void hex_viewer_app_free(HexViewer* app) { + furi_assert(app); + + if(app->model->stream) buffered_file_stream_close(app->model->stream); + + // Scene manager + scene_manager_free(app->scene_manager); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, HexViewerViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, HexViewerViewIdScroll); + view_dispatcher_remove_view(app->view_dispatcher, HexViewerViewIdSettings); + + submenu_free(app->submenu); + text_input_free(app->text_input); + + view_dispatcher_free(app->view_dispatcher); furi_record_close(RECORD_STORAGE); - - gui_remove_view_port(instance->gui, instance->view_port); furi_record_close(RECORD_GUI); - view_port_free(instance->view_port); - furi_message_queue_free(instance->input_queue); + app->storage = NULL; + app->gui = NULL; + app->notification = NULL; - furi_mutex_free(instance->mutex); + // Close File Browser + furi_record_close(RECORD_DIALOGS); + furi_string_free(app->file_path); - if(instance->model->stream) buffered_file_stream_close(instance->model->stream); + free(app->model); - free(instance->model); - free(instance); -} - -static bool hex_viewer_open_file(HexViewer* hex_viewer, const char* file_path) { - furi_assert(hex_viewer); - furi_assert(file_path); - - hex_viewer->model->stream = buffered_file_stream_alloc(hex_viewer->storage); - bool isOk = true; - - do { - if(!buffered_file_stream_open( - hex_viewer->model->stream, file_path, FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Unable to open stream: %s", file_path); - isOk = false; - break; - }; - - hex_viewer->model->file_size = stream_size(hex_viewer->model->stream); - } while(false); - - return isOk; -} - -static bool hex_viewer_read_file(HexViewer* hex_viewer) { - furi_assert(hex_viewer); - furi_assert(hex_viewer->model->stream); - furi_assert(hex_viewer->model->file_offset % HEX_VIEWER_BYTES_PER_LINE == 0); - - memset(hex_viewer->model->file_bytes, 0x0, HEX_VIEWER_BUF_SIZE); - bool isOk = true; - - do { - uint32_t offset = hex_viewer->model->file_offset; - if(!stream_seek(hex_viewer->model->stream, offset, true)) { - FURI_LOG_E(TAG, "Unable to seek stream"); - isOk = false; - break; - } - - hex_viewer->model->file_read_bytes = stream_read( - hex_viewer->model->stream, - (uint8_t*)hex_viewer->model->file_bytes, - HEX_VIEWER_BUF_SIZE); - } while(false); - - return isOk; + //Remove whatever is left + free(app); } int32_t hex_viewer_app(void* p) { - HexViewer* hex_viewer = hex_viewer_alloc(); + UNUSED(p); + HexViewer* app = hex_viewer_app_alloc(); - FuriString* file_path; - file_path = furi_string_alloc(); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - do { - if(p && strlen(p)) { - furi_string_set(file_path, (const char*)p); - } else { - furi_string_set(file_path, HEX_VIEWER_APP_PATH_FOLDER); + if(p && strlen(p) && hex_viewer_open_file(app, (const char*)p)) { + hex_viewer_read_file(app); + scene_manager_next_scene(app->scene_manager, HexViewerSceneStartscreen); + } else { + scene_manager_next_scene(app->scene_manager, HexViewerSceneStartscreen); + scene_manager_next_scene(app->scene_manager, HexViewerSceneOpen); + } - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, HEX_VIEWER_APP_EXTENSION, &I_hex_10px); - browser_options.hide_ext = false; + furi_hal_power_suppress_charge_enter(); - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - bool res = dialog_file_browser_show(dialogs, file_path, file_path, &browser_options); + view_dispatcher_run(app->view_dispatcher); - furi_record_close(RECORD_DIALOGS); - if(!res) { - FURI_LOG_I(TAG, "No file selected"); - break; - } - } + hex_viewer_save_settings(app); - FURI_LOG_I(TAG, "File selected: %s", furi_string_get_cstr(file_path)); - - if(!hex_viewer_open_file(hex_viewer, furi_string_get_cstr(file_path))) break; - hex_viewer_read_file(hex_viewer); - - InputEvent input; - while(1) { - if(furi_message_queue_get(hex_viewer->input_queue, &input, 100) == FuriStatusOk) { - if(input.key == InputKeyBack) { - break; - } else if(input.key == InputKeyUp) { - furi_check( - furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - if(hex_viewer->model->file_offset > 0) { - hex_viewer->model->file_offset -= HEX_VIEWER_BYTES_PER_LINE; - if(!hex_viewer_read_file(hex_viewer)) break; - } - furi_mutex_release(hex_viewer->mutex); - } else if(input.key == InputKeyDown) { - furi_check( - furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - uint32_t last_byte_on_screen = - hex_viewer->model->file_offset + hex_viewer->model->file_read_bytes; - - if(hex_viewer->model->file_size > last_byte_on_screen) { - hex_viewer->model->file_offset += HEX_VIEWER_BYTES_PER_LINE; - if(!hex_viewer_read_file(hex_viewer)) break; - } - furi_mutex_release(hex_viewer->mutex); - } else if(input.key == InputKeyLeft) { - furi_check( - furi_mutex_acquire(hex_viewer->mutex, FuriWaitForever) == FuriStatusOk); - hex_viewer->model->mode = !hex_viewer->model->mode; - furi_mutex_release(hex_viewer->mutex); - } else if(input.key == InputKeyRight) { - FuriString* buffer; - buffer = furi_string_alloc(); - furi_string_printf( - buffer, - "File path: %s\nFile size: %lu (0x%lX)", - furi_string_get_cstr(file_path), - hex_viewer->model->file_size, - hex_viewer->model->file_size); - - DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); - DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "Hex Viewer v1.1", 16, 2, AlignLeft, AlignTop); - dialog_message_set_icon(message, &I_hex_10px, 3, 2); - dialog_message_set_text( - message, furi_string_get_cstr(buffer), 3, 16, AlignLeft, AlignTop); - dialog_message_set_buttons(message, NULL, NULL, "Back"); - dialog_message_show(dialogs, message); - - furi_string_free(buffer); - dialog_message_free(message); - furi_record_close(RECORD_DIALOGS); - } - } - - view_port_update(hex_viewer->view_port); - } - } while(false); - - furi_string_free(file_path); - hex_viewer_free(hex_viewer); + furi_hal_power_suppress_charge_exit(); + hex_viewer_app_free(app); return 0; } diff --git a/applications/system/hex_viewer/hex_viewer.h b/applications/system/hex_viewer/hex_viewer.h new file mode 100644 index 000000000..d1c1adba3 --- /dev/null +++ b/applications/system/hex_viewer/hex_viewer.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/hex_viewer_scene.h" +#include "views/hex_viewer_startscreen.h" +#include "helpers/hex_viewer_storage.h" + +#include +#include +#include +#include + +#define TAG "HexViewer" + +#define HEX_VIEWER_APP_PATH_FOLDER "/any" // TODO ANY_PATH +#define HEX_VIEWER_APP_EXTENSION "*" +#define HEX_VIEWER_PERCENT_INPUT 16 + +#define HEX_VIEWER_BYTES_PER_LINE 4u +#define HEX_VIEWER_LINES_ON_SCREEN 4u +#define HEX_VIEWER_BUF_SIZE (HEX_VIEWER_LINES_ON_SCREEN * HEX_VIEWER_BYTES_PER_LINE) + +typedef struct { + uint8_t file_bytes[HEX_VIEWER_LINES_ON_SCREEN][HEX_VIEWER_BYTES_PER_LINE]; + uint32_t file_offset; + uint32_t file_read_bytes; + uint32_t file_size; + + Stream* stream; +} HexViewerModel; + +// TODO Clean +typedef struct { + HexViewerModel* model; + + Gui* gui; + Storage* storage; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + TextInput* text_input; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + HexViewerStartscreen* hex_viewer_startscreen; + DialogsApp* dialogs; // File Browser + FuriString* file_path; // File Browser + uint32_t haptic; + uint32_t speaker; + uint32_t led; + uint32_t save_settings; + char percent_buf[HEX_VIEWER_PERCENT_INPUT]; +} HexViewer; + +typedef enum { + HexViewerViewIdStartscreen, + HexViewerViewIdMenu, + HexViewerViewIdScroll, + HexViewerViewIdSettings, +} HexViewerViewId; + +typedef enum { + HexViewerHapticOff, + HexViewerHapticOn, +} HexViewerHapticState; + +typedef enum { + HexViewerSpeakerOff, + HexViewerSpeakerOn, +} HexViewerSpeakerState; + +typedef enum { + HexViewerLedOff, + HexViewerLedOn, +} HexViewerLedState; + +typedef enum { + HexViewerSettingsOff, + HexViewerSettingsOn, +} HexViewerSettingsStoreState; diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene.c b/applications/system/hex_viewer/scenes/hex_viewer_scene.c new file mode 100644 index 000000000..385828b0d --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene.c @@ -0,0 +1,30 @@ +#include "hex_viewer_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const hex_viewer_on_enter_handlers[])(void*) = { +#include "hex_viewer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const hex_viewer_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "hex_viewer_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const hex_viewer_on_exit_handlers[])(void* context) = { +#include "hex_viewer_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers hex_viewer_scene_handlers = { + .on_enter_handlers = hex_viewer_on_enter_handlers, + .on_event_handlers = hex_viewer_on_event_handlers, + .on_exit_handlers = hex_viewer_on_exit_handlers, + .scene_num = HexViewerSceneNum, +}; diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene.h b/applications/system/hex_viewer/scenes/hex_viewer_scene.h new file mode 100644 index 000000000..e1f322fca --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) HexViewerScene##id, +typedef enum { +#include "hex_viewer_scene_config.h" + HexViewerSceneNum, +} HexViewerScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers hex_viewer_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "hex_viewer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "hex_viewer_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "hex_viewer_scene_config.h" +#undef ADD_SCENE diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_config.h b/applications/system/hex_viewer/scenes/hex_viewer_scene_config.h new file mode 100644 index 000000000..7db382bc5 --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(hex_viewer, startscreen, Startscreen) +ADD_SCENE(hex_viewer, menu, Menu) +ADD_SCENE(hex_viewer, scroll, Scroll) +ADD_SCENE(hex_viewer, info, Info) +ADD_SCENE(hex_viewer, open, Open) +ADD_SCENE(hex_viewer, settings, Settings) \ No newline at end of file diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_info.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_info.c new file mode 100644 index 000000000..39d51b8c5 --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_info.c @@ -0,0 +1,42 @@ +#include "../hex_viewer.h" + +void hex_viewer_scene_info_on_enter(void* context) { + furi_assert(context); + HexViewer* app = context; + + FuriString* buffer; + buffer = furi_string_alloc(); + furi_string_printf( + buffer, + "File path: %s\nFile size: %lu (0x%lX)", + furi_string_get_cstr(app->file_path), + app->model->file_size, + app->model->file_size); + + DialogMessage* message = dialog_message_alloc(); + dialog_message_set_header(message, "Hex Viewer v2.0", 16, 2, AlignLeft, AlignTop); + dialog_message_set_icon(message, &I_hex_10px, 3, 2); + dialog_message_set_text(message, furi_string_get_cstr(buffer), 3, 16, AlignLeft, AlignTop); + dialog_message_set_buttons(message, NULL, NULL, "Back"); + dialog_message_show(app->dialogs, message); + + furi_string_free(buffer); + dialog_message_free(message); + + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, HexViewerViewIdStartscreen); +} + +bool hex_viewer_scene_info_on_event(void* context, SceneManagerEvent event) { + HexViewer* app = context; + UNUSED(app); + UNUSED(event); + bool consumed = true; + + return consumed; +} + +void hex_viewer_scene_info_on_exit(void* context) { + HexViewer* app = context; + UNUSED(app); +} diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_menu.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_menu.c new file mode 100644 index 000000000..50e0b55ba --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_menu.c @@ -0,0 +1,83 @@ +#include "../hex_viewer.h" + +enum SubmenuIndex { + SubmenuIndexScroll = 10, + SubmenuIndexInfo, + SubmenuIndexOpen, + // SubmenuIndexSettings, +}; + +void hex_viewer_scene_menu_submenu_callback(void* context, uint32_t index) { + HexViewer* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void hex_viewer_scene_menu_on_enter(void* context) { + HexViewer* app = context; + + submenu_set_header(app->submenu, "Select action"); + submenu_add_item( + app->submenu, + "Open file ...", + SubmenuIndexOpen, + hex_viewer_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Scroll to ...", + SubmenuIndexScroll, + hex_viewer_scene_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Show info ...", + SubmenuIndexInfo, + hex_viewer_scene_menu_submenu_callback, + app); + // submenu_add_item(app->submenu, "Settings", SubmenuIndexSettings, hex_viewer_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, HexViewerSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, HexViewerViewIdMenu); +} + +bool hex_viewer_scene_menu_on_event(void* context, SceneManagerEvent event) { + HexViewer* app = context; + + if(event.type == SceneManagerEventTypeBack) { + //exit app + // scene_manager_stop(app->scene_manager); + // view_dispatcher_stop(app->view_dispatcher); + scene_manager_previous_scene(app->scene_manager); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexScroll) { + scene_manager_set_scene_state( + app->scene_manager, HexViewerSceneMenu, SubmenuIndexScroll); + scene_manager_next_scene(app->scene_manager, HexViewerSceneScroll); + return true; + } else if(event.event == SubmenuIndexInfo) { + scene_manager_set_scene_state( + app->scene_manager, HexViewerSceneMenu, SubmenuIndexInfo); + scene_manager_next_scene(app->scene_manager, HexViewerSceneInfo); + return true; + } else if(event.event == SubmenuIndexOpen) { + scene_manager_set_scene_state( + app->scene_manager, HexViewerSceneMenu, SubmenuIndexOpen); + scene_manager_next_scene(app->scene_manager, HexViewerSceneOpen); + // } else if (event.event == SubmenuIndexSettings) { + // scene_manager_set_scene_state( + // app->scene_manager, HexViewerSceneMenu, SubmenuIndexSettings); + // scene_manager_next_scene(app->scene_manager, HexViewerSceneSettings); + // return true; + } + } + + return false; +} + +void hex_viewer_scene_menu_on_exit(void* context) { + HexViewer* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_open.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_open.c new file mode 100644 index 000000000..cd9e90b7f --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_open.c @@ -0,0 +1,42 @@ +#include "../hex_viewer.h" + +void hex_viewer_scene_open_on_enter(void* context) { + furi_assert(context); + HexViewer* app = context; + + FuriString* initial_path; + initial_path = furi_string_alloc(); + furi_string_set(initial_path, HEX_VIEWER_APP_PATH_FOLDER); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, HEX_VIEWER_APP_EXTENSION, &I_hex_10px); + browser_options.hide_ext = false; + + bool success = + dialog_file_browser_show(app->dialogs, app->file_path, initial_path, &browser_options); + furi_string_free(initial_path); + + if(success) { + success = hex_viewer_open_file(app, furi_string_get_cstr(app->file_path)); + if(success) hex_viewer_read_file(app); + } + + if(success) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, HexViewerViewIdStartscreen); + } else { + scene_manager_previous_scene(app->scene_manager); + } +} + +bool hex_viewer_scene_open_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + bool consumed = true; + + return consumed; +} + +void hex_viewer_scene_open_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c new file mode 100644 index 000000000..d20e9cfcb --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_scroll.c @@ -0,0 +1,61 @@ +#include "../hex_viewer.h" +#include "../helpers/hex_viewer_custom_event.h" + +void hex_viewer_scene_scroll_callback(void* context) { + HexViewer* app = (HexViewer*)context; + view_dispatcher_send_custom_event( + app->view_dispatcher, HexViewerCustomEventMenuPercentEntered); +} + +void hex_viewer_scene_scroll_on_enter(void* context) { + furi_assert(context); + HexViewer* app = context; + + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Scroll to percent (0..100)"); + text_input_set_result_callback( + text_input, + hex_viewer_scene_scroll_callback, + app, + app->percent_buf, + HEX_VIEWER_PERCENT_INPUT, + false); + + view_dispatcher_switch_to_view(app->view_dispatcher, HexViewerSceneScroll); +} + +bool hex_viewer_scene_scroll_on_event(void* context, SceneManagerEvent event) { + HexViewer* app = (HexViewer*)context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == HexViewerCustomEventMenuPercentEntered) { + int ipercent = atoi(app->percent_buf); + ipercent = MIN(ipercent, 100); + ipercent = MAX(ipercent, 0); + float percent = ipercent / 100.0; + + uint32_t line_count = app->model->file_size / HEX_VIEWER_BYTES_PER_LINE; + if(app->model->file_size % HEX_VIEWER_BYTES_PER_LINE != 0) line_count += 1; + uint32_t scrollable_lines = line_count - HEX_VIEWER_LINES_ON_SCREEN; + uint32_t target_line = (uint32_t)(percent * scrollable_lines); + + uint32_t new_file_offset = target_line * HEX_VIEWER_BYTES_PER_LINE; + if(app->model->file_size > new_file_offset) { + app->model->file_offset = new_file_offset; + if(!hex_viewer_read_file(app)) new_file_offset = new_file_offset; // TODO Do smth + } + + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, HexViewerViewIdStartscreen); + + consumed = true; + } + } + return consumed; +} + +void hex_viewer_scene_scroll_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_settings.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_settings.c new file mode 100644 index 000000000..f9c4cafbc --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_settings.c @@ -0,0 +1,147 @@ +#include "../hex_viewer.h" +#include + +enum SettingsIndex { + SettingsIndexHaptic = 10, + SettingsIndexValue1, + SettingsIndexValue2, +}; + +const char* const haptic_text[2] = { + "OFF", + "ON", +}; +const uint32_t haptic_value[2] = { + HexViewerHapticOff, + HexViewerHapticOn, +}; + +const char* const speaker_text[2] = { + "OFF", + "ON", +}; +const uint32_t speaker_value[2] = { + HexViewerSpeakerOff, + HexViewerSpeakerOn, +}; + +const char* const led_text[2] = { + "OFF", + "ON", +}; +const uint32_t led_value[2] = { + HexViewerLedOff, + HexViewerLedOn, +}; + +const char* const settings_text[2] = { + "OFF", + "ON", +}; +const uint32_t settings_value[2] = { + HexViewerSettingsOff, + HexViewerSettingsOn, +}; + + +static void hex_viewer_scene_settings_set_haptic(VariableItem* item) { + HexViewer* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +static void hex_viewer_scene_settings_set_speaker(VariableItem* item) { + HexViewer* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, speaker_text[index]); + app->speaker = speaker_value[index]; +} + +static void hex_viewer_scene_settings_set_led(VariableItem* item) { + HexViewer* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +static void hex_viewer_scene_settings_set_save_settings(VariableItem* item) { + HexViewer* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, settings_text[index]); + app->save_settings = settings_value[index]; +} + +void hex_viewer_scene_settings_submenu_callback(void* context, uint32_t index) { + HexViewer* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void hex_viewer_scene_settings_on_enter(void* context) { + HexViewer* app = context; + VariableItem* item; + uint8_t value_index; + + // Vibro on/off + item = variable_item_list_add( + app->variable_item_list, + "Vibro/Haptic:", + 2, + hex_viewer_scene_settings_set_haptic, + app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // Sound on/off + item = variable_item_list_add( + app->variable_item_list, + "Sound:", + 2, + hex_viewer_scene_settings_set_speaker, + app); + value_index = value_index_uint32(app->speaker, speaker_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + + // LED Effects on/off + item = variable_item_list_add( + app->variable_item_list, + "LED FX:", + 2, + hex_viewer_scene_settings_set_led, + app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + // Save Settings to File + item = variable_item_list_add( + app->variable_item_list, + "Save Settings", + 2, + hex_viewer_scene_settings_set_save_settings, + app); + value_index = value_index_uint32(app->save_settings, settings_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, settings_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, HexViewerViewIdSettings); +} + +bool hex_viewer_scene_settings_on_event(void* context, SceneManagerEvent event) { + HexViewer* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + + } + return consumed; +} + +void hex_viewer_scene_settings_on_exit(void* context) { + HexViewer* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/applications/system/hex_viewer/scenes/hex_viewer_scene_startscreen.c b/applications/system/hex_viewer/scenes/hex_viewer_scene_startscreen.c new file mode 100644 index 000000000..6793655af --- /dev/null +++ b/applications/system/hex_viewer/scenes/hex_viewer_scene_startscreen.c @@ -0,0 +1,65 @@ +#include "../hex_viewer.h" +#include "../helpers/hex_viewer_custom_event.h" +#include "../views/hex_viewer_startscreen.h" + +void hex_viewer_scene_startscreen_callback(HexViewerCustomEvent event, void* context) { + furi_assert(context); + HexViewer* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void hex_viewer_scene_startscreen_on_enter(void* context) { + furi_assert(context); + HexViewer* app = context; + hex_viewer_startscreen_set_callback( + app->hex_viewer_startscreen, hex_viewer_scene_startscreen_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, HexViewerViewIdStartscreen); +} + +bool hex_viewer_scene_startscreen_on_event(void* context, SceneManagerEvent event) { + HexViewer* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case HexViewerCustomEventStartscreenLeft: + //app->model->mode = !app->model->mode; + consumed = true; + break; + case HexViewerCustomEventStartscreenRight: + consumed = true; + break; + case HexViewerCustomEventStartscreenUp: + consumed = true; + break; + case HexViewerCustomEventStartscreenDown: + consumed = true; + break; + case HexViewerCustomEventStartscreenOk: + if(!app->model->file_size) + scene_manager_next_scene(app->scene_manager, HexViewerSceneOpen); + else + scene_manager_next_scene(app->scene_manager, HexViewerSceneMenu); + consumed = true; + break; + case HexViewerCustomEventStartscreenBack: // TODO Delete + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, HexViewerSceneStartscreen)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void hex_viewer_scene_startscreen_on_exit(void* context) { + HexViewer* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/system/hex_viewer/views/hex_viewer_startscreen.c b/applications/system/hex_viewer/views/hex_viewer_startscreen.c new file mode 100644 index 000000000..ed40de8a7 --- /dev/null +++ b/applications/system/hex_viewer/views/hex_viewer_startscreen.c @@ -0,0 +1,245 @@ +#include "../hex_viewer.h" +#include +#include +#include +#include + +struct HexViewerStartscreen { + View* view; + HexViewerStartscreenCallback callback; + void* context; +}; + +typedef struct { + uint8_t file_bytes[HEX_VIEWER_LINES_ON_SCREEN][HEX_VIEWER_BYTES_PER_LINE]; + uint32_t file_offset; + uint32_t file_read_bytes; + uint32_t file_size; + bool mode; + uint32_t dbg; +} HexViewerStartscreenModel; + +void hex_viewer_startscreen_set_callback( + HexViewerStartscreen* instance, + HexViewerStartscreenCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void hex_viewer_startscreen_draw(Canvas* canvas, HexViewerStartscreenModel* model) { + canvas_clear(canvas); + + if(!model->file_size) { + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "HexViewer v2.0"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Basic hex viewer"); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "for your Flipper"); + elements_button_center(canvas, "Open"); + } else { + canvas_set_color(canvas, ColorBlack); + + elements_button_left(canvas, model->mode ? "Addr" : "Text"); + //elements_button_right(canvas, "Info"); + elements_button_center(canvas, "Menu"); + + int ROW_HEIGHT = 12; + int TOP_OFFSET = 10; + int LEFT_OFFSET = 3; + + uint32_t line_count = model->file_size / HEX_VIEWER_BYTES_PER_LINE; + if(model->file_size % HEX_VIEWER_BYTES_PER_LINE != 0) line_count += 1; + uint32_t first_line_on_screen = model->file_offset / HEX_VIEWER_BYTES_PER_LINE; + if(line_count > HEX_VIEWER_LINES_ON_SCREEN) { + uint8_t width = canvas_width(canvas); + elements_scrollbar_pos( + canvas, + width, + 0, + ROW_HEIGHT * HEX_VIEWER_LINES_ON_SCREEN, + first_line_on_screen, // TODO + line_count - (HEX_VIEWER_LINES_ON_SCREEN - 1)); + } + + char temp_buf[32]; + uint32_t row_iters = model->file_read_bytes / HEX_VIEWER_BYTES_PER_LINE; + if(model->file_read_bytes % HEX_VIEWER_BYTES_PER_LINE != 0) row_iters += 1; + + for(uint32_t i = 0; i < row_iters; ++i) { + uint32_t bytes_left_per_row = model->file_read_bytes - i * HEX_VIEWER_BYTES_PER_LINE; + bytes_left_per_row = MIN(bytes_left_per_row, HEX_VIEWER_BYTES_PER_LINE); + + if(model->mode) { + memcpy(temp_buf, model->file_bytes[i], bytes_left_per_row); + temp_buf[bytes_left_per_row] = '\0'; + for(uint32_t j = 0; j < bytes_left_per_row; ++j) + if(!isprint((int)temp_buf[j])) temp_buf[j] = '.'; + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } else { + uint32_t addr = model->file_offset + i * HEX_VIEWER_BYTES_PER_LINE; + snprintf(temp_buf, 32, "%04lX", addr); + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } + + char* p = temp_buf; + for(uint32_t j = 0; j < bytes_left_per_row; ++j) + p += snprintf(p, 32, "%02X ", model->file_bytes[i][j]); + + canvas_set_font(canvas, FontKeyboard); + canvas_draw_str(canvas, LEFT_OFFSET + 41, TOP_OFFSET + i * ROW_HEIGHT, temp_buf); + } + + // Poor man's debug + // snprintf(temp_buf, 32, "D %02lX", model->dbg); + // elements_button_right(canvas, temp_buf); + } +} + +static void hex_viewer_startscreen_model_init(HexViewerStartscreenModel* const model) { + memset(model->file_bytes, 0, sizeof(model->file_bytes)); + model->file_offset = 0; + model->file_read_bytes = 0; + model->file_size = 0; + model->mode = false; + model->dbg = 0; +} + +static void + update_local_model_from_app(HexViewer* const app, HexViewerStartscreenModel* const model) { + memcpy(model->file_bytes, app->model->file_bytes, sizeof(model->file_bytes)); + model->file_offset = app->model->file_offset; + model->file_read_bytes = app->model->file_read_bytes; + model->file_size = app->model->file_size; + //model->mode = app->model->mode; +} + +bool hex_viewer_startscreen_input(InputEvent* event, void* context) { + furi_assert(context); + HexViewerStartscreen* instance = context; + HexViewer* app = instance->context; // TO so good, but works + // TODO InputTypeShort? + if(event->type == InputTypeRelease || event->type == InputTypeRepeat) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { + instance->callback(HexViewerCustomEventStartscreenBack, instance->context); + update_local_model_from_app(instance->context, model); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { model->mode = !model->mode; }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, HexViewerStartscreenModel * model, { model->dbg = 0; }, true); + break; + case InputKeyUp: + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { + if(app->model->file_offset > 0) { + app->model->file_offset -= HEX_VIEWER_BYTES_PER_LINE; + if(!hex_viewer_read_file(app)) break; // TODO Do smth + } + + update_local_model_from_app(instance->context, model); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { + uint32_t last_byte_on_screen = + app->model->file_offset + app->model->file_read_bytes; + if(app->model->file_size > last_byte_on_screen) { + app->model->file_offset += HEX_VIEWER_BYTES_PER_LINE; + if(!hex_viewer_read_file(app)) break; // TODO Do smth + } + + update_local_model_from_app(instance->context, model); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { + instance->callback(HexViewerCustomEventStartscreenOk, instance->context); + update_local_model_from_app(instance->context, model); + }, + true); + break; + case InputKeyMAX: + break; + } + } + + return true; +} + +void hex_viewer_startscreen_exit(void* context) { + furi_assert(context); +} + +void hex_viewer_startscreen_enter(void* context) { + furi_assert(context); + HexViewerStartscreen* instance = (HexViewerStartscreen*)context; + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { update_local_model_from_app(instance->context, model); }, + true); +} + +HexViewerStartscreen* hex_viewer_startscreen_alloc() { + HexViewerStartscreen* instance = malloc(sizeof(HexViewerStartscreen)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(HexViewerStartscreenModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)hex_viewer_startscreen_draw); + view_set_input_callback(instance->view, hex_viewer_startscreen_input); + view_set_enter_callback(instance->view, hex_viewer_startscreen_enter); + view_set_exit_callback(instance->view, hex_viewer_startscreen_exit); + + with_view_model( + instance->view, + HexViewerStartscreenModel * model, + { hex_viewer_startscreen_model_init(model); }, + true); + + return instance; +} + +void hex_viewer_startscreen_free(HexViewerStartscreen* instance) { + furi_assert(instance); + + with_view_model( + instance->view, HexViewerStartscreenModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* hex_viewer_startscreen_get_view(HexViewerStartscreen* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/system/hex_viewer/views/hex_viewer_startscreen.h b/applications/system/hex_viewer/views/hex_viewer_startscreen.h new file mode 100644 index 000000000..3bda82744 --- /dev/null +++ b/applications/system/hex_viewer/views/hex_viewer_startscreen.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/hex_viewer_custom_event.h" + +typedef struct HexViewerStartscreen HexViewerStartscreen; + +typedef void (*HexViewerStartscreenCallback)(HexViewerCustomEvent event, void* context); + +void hex_viewer_startscreen_set_callback( + HexViewerStartscreen* hex_viewer_startscreen, + HexViewerStartscreenCallback callback, + void* context); + +View* hex_viewer_startscreen_get_view(HexViewerStartscreen* hex_viewer_static); + +HexViewerStartscreen* hex_viewer_startscreen_alloc(); + +void hex_viewer_startscreen_free(HexViewerStartscreen* hex_viewer_static); \ No newline at end of file diff --git a/applications/system/hid_app/assets/BtnFrameLeft_3x18.png b/applications/system/hid_app/assets/BtnFrameLeft_3x18.png new file mode 100644 index 000000000..f39e89f8b Binary files /dev/null and b/applications/system/hid_app/assets/BtnFrameLeft_3x18.png differ diff --git a/applications/system/hid_app/assets/BtnFrameRight_2x18.png b/applications/system/hid_app/assets/BtnFrameRight_2x18.png new file mode 100644 index 000000000..d6edbb713 Binary files /dev/null and b/applications/system/hid_app/assets/BtnFrameRight_2x18.png differ diff --git a/applications/system/hid_app/assets/Mic_btn_8x10.png b/applications/system/hid_app/assets/Mic_btn_8x10.png new file mode 100644 index 000000000..a08bb35dd Binary files /dev/null and b/applications/system/hid_app/assets/Mic_btn_8x10.png differ diff --git a/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png b/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png new file mode 100644 index 000000000..929992022 Binary files /dev/null and b/applications/system/hid_app/assets/Pin_back_arrow_rotated_8x10.png differ diff --git a/applications/system/hid_app/hid.c b/applications/system/hid_app/hid.c index 4ab10c907..4792211cb 100644 --- a/applications/system/hid_app/hid.c +++ b/applications/system/hid_app/hid.c @@ -11,10 +11,12 @@ enum HidDebugSubmenuIndex { HidSubmenuIndexKeyboard, HidSubmenuIndexNumpad, HidSubmenuIndexMedia, + HidSubmenuIndexMovie, HidSubmenuIndexTikTok, HidSubmenuIndexMouse, HidSubmenuIndexMouseClicker, HidSubmenuIndexMouseJiggler, + HidSubmenuIndexPtt, }; static void hid_submenu_callback(void* context, uint32_t index) { @@ -37,6 +39,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMedia) { app->view_id = HidViewMedia; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMedia); + } else if(index == HidSubmenuIndexMovie) { + app->view_id = HidViewMovie; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMovie); } else if(index == HidSubmenuIndexMouse) { app->view_id = HidViewMouse; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouse); @@ -49,6 +54,9 @@ static void hid_submenu_callback(void* context, uint32_t index) { } else if(index == HidSubmenuIndexMouseJiggler) { app->view_id = HidViewMouseJiggler; view_dispatcher_switch_to_view(app->view_dispatcher, HidViewMouseJiggler); + } else if(index == HidSubmenuIndexPtt) { + app->view_id = HidViewPtt; + view_dispatcher_switch_to_view(app->view_dispatcher, HidViewPtt); } } @@ -67,27 +75,17 @@ static void bt_hid_connection_status_changed_callback(BtStatus status, void* con hid_keyboard_set_connected_status(hid->hid_keyboard, connected); hid_numpad_set_connected_status(hid->hid_numpad, connected); hid_media_set_connected_status(hid->hid_media, connected); + hid_movie_set_connected_status(hid->hid_movie, connected); hid_mouse_set_connected_status(hid->hid_mouse, connected); hid_mouse_clicker_set_connected_status(hid->hid_mouse_clicker, connected); hid_mouse_jiggler_set_connected_status(hid->hid_mouse_jiggler, connected); + hid_ptt_set_connected_status(hid->hid_ptt, connected); hid_tiktok_set_connected_status(hid->hid_tiktok, connected); } -static void hid_dialog_callback(DialogExResult result, void* context) { - furi_assert(context); - Hid* app = context; - if(result == DialogExResultLeft) { - view_dispatcher_stop(app->view_dispatcher); - } else if(result == DialogExResultRight) { - view_dispatcher_switch_to_view(app->view_dispatcher, app->view_id); // Show last view - } else if(result == DialogExResultCenter) { - view_dispatcher_switch_to_view(app->view_dispatcher, HidViewSubmenu); - } -} - -static uint32_t hid_exit_confirm_view(void* context) { +static uint32_t hid_menu_view(void* context) { UNUSED(context); - return HidViewExitConfirm; + return HidViewSubmenu; } static uint32_t hid_exit(void* context) { @@ -128,6 +126,8 @@ Hid* hid_alloc(HidTransport transport) { app->device_type_submenu, "Numpad", HidSubmenuIndexNumpad, hid_submenu_callback, app); submenu_add_item( app->device_type_submenu, "Media", HidSubmenuIndexMedia, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "Movie", HidSubmenuIndexMovie, hid_submenu_callback, app); submenu_add_item( app->device_type_submenu, "Mouse", HidSubmenuIndexMouse, hid_submenu_callback, app); if(app->transport == HidTransportBle) { @@ -150,6 +150,8 @@ Hid* hid_alloc(HidTransport transport) { HidSubmenuIndexMouseJiggler, hid_submenu_callback, app); + submenu_add_item( + app->device_type_submenu, "PTT", HidSubmenuIndexPtt, hid_submenu_callback, app); view_set_previous_callback(submenu_get_view(app->device_type_submenu), hid_exit); view_dispatcher_add_view( app->view_dispatcher, HidViewSubmenu, submenu_get_view(app->device_type_submenu)); @@ -162,56 +164,52 @@ Hid* hid_app_alloc_view(void* context) { furi_assert(context); Hid* app = context; // Dialog view - app->dialog = dialog_ex_alloc(); - dialog_ex_set_result_callback(app->dialog, hid_dialog_callback); - dialog_ex_set_context(app->dialog, app); - dialog_ex_set_left_button_text(app->dialog, "Exit"); - dialog_ex_set_right_button_text(app->dialog, "Stay"); - dialog_ex_set_center_button_text(app->dialog, "Menu"); - dialog_ex_set_header(app->dialog, "Close Current App?", 16, 12, AlignLeft, AlignTop); - view_dispatcher_add_view( - app->view_dispatcher, HidViewExitConfirm, dialog_ex_get_view(app->dialog)); // Keynote view app->hid_keynote = hid_keynote_alloc(app); - view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_exit_confirm_view); + view_set_previous_callback(hid_keynote_get_view(app->hid_keynote), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewKeynote, hid_keynote_get_view(app->hid_keynote)); // Keyboard view app->hid_keyboard = hid_keyboard_alloc(app); - view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_exit_confirm_view); + view_set_previous_callback(hid_keyboard_get_view(app->hid_keyboard), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewKeyboard, hid_keyboard_get_view(app->hid_keyboard)); //Numpad keyboard view app->hid_numpad = hid_numpad_alloc(app); - view_set_previous_callback(hid_numpad_get_view(app->hid_numpad), hid_exit_confirm_view); + view_set_previous_callback(hid_numpad_get_view(app->hid_numpad), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewNumpad, hid_numpad_get_view(app->hid_numpad)); // Media view app->hid_media = hid_media_alloc(app); - view_set_previous_callback(hid_media_get_view(app->hid_media), hid_exit_confirm_view); + view_set_previous_callback(hid_media_get_view(app->hid_media), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMedia, hid_media_get_view(app->hid_media)); + // Movie view + app->hid_movie = hid_movie_alloc(app); + view_set_previous_callback(hid_movie_get_view(app->hid_movie), hid_menu_view); + view_dispatcher_add_view( + app->view_dispatcher, HidViewMovie, hid_movie_get_view(app->hid_movie)); + // TikTok view app->hid_tiktok = hid_tiktok_alloc(app); - view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_exit_confirm_view); + view_set_previous_callback(hid_tiktok_get_view(app->hid_tiktok), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, BtHidViewTikTok, hid_tiktok_get_view(app->hid_tiktok)); // Mouse view app->hid_mouse = hid_mouse_alloc(app); - view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_exit_confirm_view); + view_set_previous_callback(hid_mouse_get_view(app->hid_mouse), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouse, hid_mouse_get_view(app->hid_mouse)); // Mouse clicker view app->hid_mouse_clicker = hid_mouse_clicker_alloc(app); - view_set_previous_callback( - hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_exit_confirm_view); + view_set_previous_callback(hid_mouse_clicker_get_view(app->hid_mouse_clicker), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseClicker, @@ -219,13 +217,17 @@ Hid* hid_app_alloc_view(void* context) { // Mouse jiggler view app->hid_mouse_jiggler = hid_mouse_jiggler_alloc(app); - view_set_previous_callback( - hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_exit_confirm_view); + view_set_previous_callback(hid_mouse_jiggler_get_view(app->hid_mouse_jiggler), hid_menu_view); view_dispatcher_add_view( app->view_dispatcher, HidViewMouseJiggler, hid_mouse_jiggler_get_view(app->hid_mouse_jiggler)); + // Ptt view + app->hid_ptt = hid_ptt_alloc(app); + view_set_previous_callback(hid_ptt_get_view(app->hid_ptt), hid_menu_view); + view_dispatcher_add_view(app->view_dispatcher, HidViewPtt, hid_ptt_get_view(app->hid_ptt)); + return app; } @@ -240,8 +242,6 @@ void hid_free(Hid* app) { // Free views view_dispatcher_remove_view(app->view_dispatcher, HidViewSubmenu); submenu_free(app->device_type_submenu); - view_dispatcher_remove_view(app->view_dispatcher, HidViewExitConfirm); - dialog_ex_free(app->dialog); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeynote); hid_keynote_free(app->hid_keynote); view_dispatcher_remove_view(app->view_dispatcher, HidViewKeyboard); @@ -250,12 +250,16 @@ void hid_free(Hid* app) { hid_numpad_free(app->hid_numpad); view_dispatcher_remove_view(app->view_dispatcher, HidViewMedia); hid_media_free(app->hid_media); + view_dispatcher_remove_view(app->view_dispatcher, HidViewMovie); + hid_movie_free(app->hid_movie); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouse); hid_mouse_free(app->hid_mouse); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseClicker); hid_mouse_clicker_free(app->hid_mouse_clicker); view_dispatcher_remove_view(app->view_dispatcher, HidViewMouseJiggler); hid_mouse_jiggler_free(app->hid_mouse_jiggler); + view_dispatcher_remove_view(app->view_dispatcher, HidViewPtt); + hid_ptt_free(app->hid_ptt); view_dispatcher_remove_view(app->view_dispatcher, BtHidViewTikTok); hid_tiktok_free(app->hid_tiktok); view_dispatcher_free(app->view_dispatcher); @@ -437,9 +441,7 @@ int32_t hid_ble_app(void* p) { furi_record_close(RECORD_STORAGE); - if(!bt_set_profile(app->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - } + furi_check(bt_set_profile(app->bt, BtProfileHidKeyboard)); furi_hal_bt_start_advertising(); bt_set_status_changed_callback(app->bt, bt_hid_connection_status_changed_callback, app); @@ -457,9 +459,7 @@ int32_t hid_ble_app(void* p) { bt_keys_storage_set_default_path(app->bt); - if(!bt_set_profile(app->bt, BtProfileSerial)) { - FURI_LOG_E(TAG, "Failed to switch to Serial profile"); - } + furi_check(bt_set_profile(app->bt, BtProfileSerial)); hid_free(app); diff --git a/applications/system/hid_app/hid.h b/applications/system/hid_app/hid.h index 6b32f2167..2eb224bd5 100644 --- a/applications/system/hid_app/hid.h +++ b/applications/system/hid_app/hid.h @@ -14,16 +14,19 @@ #include #include -#include #include #include "views/hid_keynote.h" #include "views/hid_keyboard.h" #include "views/hid_numpad.h" #include "views/hid_media.h" +#include "views/hid_movie.h" #include "views/hid_mouse.h" #include "views/hid_mouse_clicker.h" #include "views/hid_mouse_jiggler.h" #include "views/hid_tiktok.h" +#include "views/hid_ptt.h" + +#include #define HID_BT_KEYS_STORAGE_NAME ".bt_hid.keys" @@ -40,15 +43,16 @@ struct Hid { NotificationApp* notifications; ViewDispatcher* view_dispatcher; Submenu* device_type_submenu; - DialogEx* dialog; HidKeynote* hid_keynote; HidKeyboard* hid_keyboard; HidNumpad* hid_numpad; HidMedia* hid_media; + HidMovie* hid_movie; HidMouse* hid_mouse; HidMouseClicker* hid_mouse_clicker; HidMouseJiggler* hid_mouse_jiggler; HidTikTok* hid_tiktok; + HidPtt* hid_ptt; HidTransport transport; uint32_t view_id; @@ -66,4 +70,4 @@ void hid_hal_mouse_move(Hid* instance, int8_t dx, int8_t dy); void hid_hal_mouse_scroll(Hid* instance, int8_t delta); void hid_hal_mouse_press(Hid* instance, uint16_t event); void hid_hal_mouse_release(Hid* instance, uint16_t event); -void hid_hal_mouse_release_all(Hid* instance); \ No newline at end of file +void hid_hal_mouse_release_all(Hid* instance); diff --git a/applications/system/hid_app/hid_usb_10px.png b/applications/system/hid_app/hid_usb_10px.png index 415de7d23..7649138eb 100644 Binary files a/applications/system/hid_app/hid_usb_10px.png and b/applications/system/hid_app/hid_usb_10px.png differ diff --git a/applications/system/hid_app/views.h b/applications/system/hid_app/views.h index 71ced9369..94279b9c4 100644 --- a/applications/system/hid_app/views.h +++ b/applications/system/hid_app/views.h @@ -4,9 +4,10 @@ typedef enum { HidViewKeyboard, HidViewNumpad, HidViewMedia, + HidViewMovie, HidViewMouse, HidViewMouseClicker, HidViewMouseJiggler, BtHidViewTikTok, - HidViewExitConfirm, -} HidView; \ No newline at end of file + HidViewPtt, +} HidView; diff --git a/applications/system/hid_app/views/hid_keyboard.c b/applications/system/hid_app/views/hid_keyboard.c index 34e34f9e7..1ce0285b2 100644 --- a/applications/system/hid_app/views/hid_keyboard.c +++ b/applications/system/hid_app/views/hid_keyboard.c @@ -5,8 +5,6 @@ #include "../hid.h" #include "hid_icons.h" -#include - #define TAG "HidKeyboard" struct HidKeyboard { diff --git a/applications/system/hid_app/views/hid_keynote.c b/applications/system/hid_app/views/hid_keynote.c index c9af01de1..7d0e125d7 100644 --- a/applications/system/hid_app/views/hid_keynote.c +++ b/applications/system/hid_app/views/hid_keynote.c @@ -4,8 +4,6 @@ #include "hid_icons.h" -#include - #define TAG "HidKeynote" struct HidKeynote { @@ -118,16 +116,16 @@ static void hid_keynote_draw_vertical_callback(Canvas* canvas, void* context) { HidKeynoteModel* model = context; // Header + canvas_set_font(canvas, FontPrimary); if(model->transport == HidTransportBle) { if(model->connected) { canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); } else { canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); } - canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 20, 3, AlignLeft, AlignTop, "Keynote"); } else { - canvas_set_font(canvas, FontPrimary); elements_multiline_text_aligned(canvas, 12, 3, AlignLeft, AlignTop, "Keynote"); } diff --git a/applications/system/hid_app/views/hid_media.c b/applications/system/hid_app/views/hid_media.c index 646f2849b..2fc0cfb1a 100644 --- a/applications/system/hid_app/views/hid_media.c +++ b/applications/system/hid_app/views/hid_media.c @@ -7,8 +7,6 @@ #include "hid_icons.h" -#include - #define TAG "HidMedia" struct HidMedia { @@ -87,8 +85,9 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_bitmap_mode(canvas, 0); canvas_set_color(canvas, ColorWhite); } - hid_media_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft); + hid_media_draw_arrow(canvas, 67, 28, CanvasDirectionRightToLeft); hid_media_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_draw_line(canvas, 64, 26, 64, 30); canvas_set_color(canvas, ColorBlack); // Right @@ -99,7 +98,8 @@ static void hid_media_draw_callback(Canvas* canvas, void* context) { canvas_set_color(canvas, ColorWhite); } hid_media_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); - hid_media_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight); + hid_media_draw_arrow(canvas, 99, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 102, 26, 102, 30); canvas_set_color(canvas, ColorBlack); // Ok diff --git a/applications/system/hid_app/views/hid_mouse.c b/applications/system/hid_app/views/hid_mouse.c index 5e2dab476..3ae7c8145 100644 --- a/applications/system/hid_app/views/hid_mouse.c +++ b/applications/system/hid_app/views/hid_mouse.c @@ -4,8 +4,6 @@ #include "hid_icons.h" -#include - #define TAG "HidMouse" struct HidMouse { diff --git a/applications/system/hid_app/views/hid_mouse_clicker.c b/applications/system/hid_app/views/hid_mouse_clicker.c index d3b78dd54..d85affc43 100644 --- a/applications/system/hid_app/views/hid_mouse_clicker.c +++ b/applications/system/hid_app/views/hid_mouse_clicker.c @@ -4,8 +4,6 @@ #include "hid_icons.h" -#include - #define TAG "HidMouseClicker" #define DEFAULT_CLICK_RATE 1 #define MAXIMUM_CLICK_RATE 60 diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.c b/applications/system/hid_app/views/hid_mouse_jiggler.c index 5e1d8aabd..3da969aae 100644 --- a/applications/system/hid_app/views/hid_mouse_jiggler.c +++ b/applications/system/hid_app/views/hid_mouse_jiggler.c @@ -4,8 +4,6 @@ #include "hid_icons.h" -#include - #define TAG "HidMouseJiggler" struct HidMouseJiggler { @@ -84,7 +82,7 @@ static void hid_mouse_jiggler_timer_callback(void* context) { model->counter++; hid_hal_mouse_move( hid_mouse_jiggler->hid, - (model->counter % 2 == 0) ? MOUSE_MOVE_SHORT : -MOUSE_MOVE_SHORT, + (model->counter % 2 == 0) ? MOUSE_MOVE_TINY : -MOUSE_MOVE_TINY, 0); } }, diff --git a/applications/system/hid_app/views/hid_mouse_jiggler.h b/applications/system/hid_app/views/hid_mouse_jiggler.h index 0813b4351..c1f77251e 100644 --- a/applications/system/hid_app/views/hid_mouse_jiggler.h +++ b/applications/system/hid_app/views/hid_mouse_jiggler.h @@ -2,8 +2,7 @@ #include -#define MOUSE_MOVE_SHORT 5 -#define MOUSE_MOVE_LONG 20 +#define MOUSE_MOVE_TINY 1 typedef struct Hid Hid; typedef struct HidMouseJiggler HidMouseJiggler; diff --git a/applications/system/hid_app/views/hid_movie.c b/applications/system/hid_app/views/hid_movie.c new file mode 100644 index 000000000..866a4c626 --- /dev/null +++ b/applications/system/hid_app/views/hid_movie.c @@ -0,0 +1,229 @@ +#include "hid_movie.h" +#include +#include +#include +#include +#include "../hid.h" + +#include "hid_icons.h" + +#define TAG "HidMovie" + +struct HidMovie { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool ok_pressed; + bool connected; + bool back_pressed; + HidTransport transport; +} HidMovieModel; + +static void hid_movie_draw_arrow(Canvas* canvas, uint8_t x, uint8_t y, CanvasDirection dir) { + canvas_draw_triangle(canvas, x, y, 5, 3, dir); + if(dir == CanvasDirectionBottomToTop) { + canvas_draw_dot(canvas, x, y - 1); + } else if(dir == CanvasDirectionTopToBottom) { + canvas_draw_dot(canvas, x, y + 1); + } else if(dir == CanvasDirectionRightToLeft) { + canvas_draw_dot(canvas, x - 1, y); + } else if(dir == CanvasDirectionLeftToRight) { + canvas_draw_dot(canvas, x + 1, y); + } +} + +static void hid_movie_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidMovieModel* model = context; + + // Header + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, 17, 3, AlignLeft, AlignTop, "Movie"); + canvas_set_font(canvas, FontSecondary); + + // Keypad circles + canvas_draw_icon(canvas, 58, 3, &I_OutCircles_70x51); + + // Up + if(model->up_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 6, &I_S_UP_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 79, 9, &I_Volup_8x6); + canvas_set_color(canvas, ColorBlack); + + // Down + if(model->down_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 68, 36, &I_S_DOWN_31x15); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 80, 41, &I_Voldwn_6x6); + canvas_set_color(canvas, ColorBlack); + + // Left + if(model->left_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 61, 13, &I_S_LEFT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 65, 28, CanvasDirectionRightToLeft); + hid_movie_draw_arrow(canvas, 70, 28, CanvasDirectionRightToLeft); + canvas_set_color(canvas, ColorBlack); + + // Right + if(model->right_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 91, 13, &I_S_RIGHT_15x31); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 96, 28, CanvasDirectionLeftToRight); + hid_movie_draw_arrow(canvas, 101, 28, CanvasDirectionLeftToRight); + canvas_set_color(canvas, ColorBlack); + + // Ok + if(model->ok_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 74, 19, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + hid_movie_draw_arrow(canvas, 80, 28, CanvasDirectionLeftToRight); + canvas_draw_line(canvas, 84, 26, 84, 30); + canvas_draw_line(canvas, 86, 26, 86, 30); + canvas_set_color(canvas, ColorBlack); + + // Exit + if(model->back_pressed) { + canvas_set_bitmap_mode(canvas, 1); + canvas_draw_icon(canvas, 107, 33, &I_Pressed_Button_19x19); + canvas_set_bitmap_mode(canvas, 0); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, 111, 38, &I_Pin_back_arrow_10x10); + canvas_set_color(canvas, ColorBlack); + + canvas_draw_icon(canvas, 0, 54, &I_Pin_back_arrow_10x8); + canvas_set_font(canvas, FontSecondary); + elements_multiline_text_aligned(canvas, 13, 62, AlignLeft, AlignBottom, "Hold to exit"); +} + +static void hid_movie_process_press(HidMovie* hid_movie, InputEvent* event) { + with_view_model( + hid_movie->view, + HidMovieModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + hid_hal_keyboard_press(hid_movie->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + hid_hal_keyboard_press(hid_movie->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = true; + hid_hal_consumer_key_press(hid_movie->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = true; + } + }, + true); +} + +static void hid_movie_process_release(HidMovie* hid_movie, InputEvent* event) { + with_view_model( + hid_movie->view, + HidMovieModel * model, + { + if(event->key == InputKeyUp) { + model->up_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + hid_hal_keyboard_release(hid_movie->hid, HID_KEYBOARD_LEFT_ARROW); + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + hid_hal_keyboard_release(hid_movie->hid, HID_KEYBOARD_RIGHT_ARROW); + } else if(event->key == InputKeyOk) { + model->ok_pressed = false; + hid_hal_consumer_key_release(hid_movie->hid, HID_CONSUMER_PLAY_PAUSE); + } else if(event->key == InputKeyBack) { + model->back_pressed = false; + } + }, + true); +} + +static bool hid_movie_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidMovie* hid_movie = context; + bool consumed = false; + + if(event->type == InputTypePress) { + hid_movie_process_press(hid_movie, event); + consumed = true; + } else if(event->type == InputTypeRelease) { + hid_movie_process_release(hid_movie, event); + consumed = true; + } + return consumed; +} + +HidMovie* hid_movie_alloc(Hid* hid) { + HidMovie* hid_movie = malloc(sizeof(HidMovie)); + hid_movie->view = view_alloc(); + hid_movie->hid = hid; + view_set_context(hid_movie->view, hid_movie); + view_allocate_model(hid_movie->view, ViewModelTypeLocking, sizeof(HidMovieModel)); + view_set_draw_callback(hid_movie->view, hid_movie_draw_callback); + view_set_input_callback(hid_movie->view, hid_movie_input_callback); + + with_view_model( + hid_movie->view, HidMovieModel * model, { model->transport = hid->transport; }, true); + + return hid_movie; +} + +void hid_movie_free(HidMovie* hid_movie) { + furi_assert(hid_movie); + view_free(hid_movie->view); + free(hid_movie); +} + +View* hid_movie_get_view(HidMovie* hid_movie) { + furi_assert(hid_movie); + return hid_movie->view; +} + +void hid_movie_set_connected_status(HidMovie* hid_movie, bool connected) { + furi_assert(hid_movie); + with_view_model( + hid_movie->view, HidMovieModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_movie.h b/applications/system/hid_app/views/hid_movie.h new file mode 100644 index 000000000..52dedc988 --- /dev/null +++ b/applications/system/hid_app/views/hid_movie.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidMovie HidMovie; + +HidMovie* hid_movie_alloc(Hid* bt_hid); + +void hid_movie_free(HidMovie* hid_movie); + +View* hid_movie_get_view(HidMovie* hid_movie); + +void hid_movie_set_connected_status(HidMovie* hid_movie, bool connected); diff --git a/applications/system/hid_app/views/hid_numpad.c b/applications/system/hid_app/views/hid_numpad.c index 4829f3c4b..bd4788b83 100644 --- a/applications/system/hid_app/views/hid_numpad.c +++ b/applications/system/hid_app/views/hid_numpad.c @@ -5,8 +5,6 @@ #include "../hid.h" #include "hid_icons.h" -#include - #define TAG "HidNumpad" struct HidNumpad { diff --git a/applications/system/hid_app/views/hid_ptt.c b/applications/system/hid_app/views/hid_ptt.c new file mode 100644 index 000000000..2506b55c8 --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt.c @@ -0,0 +1,431 @@ +#include "hid_ptt.h" +#include +#include +#include "../hid.h" +#include "../views.h" + +#include "hid_icons.h" + +#define TAG "HidPtt" + +struct HidPtt { + View* view; + Hid* hid; +}; + +typedef struct { + bool left_pressed; + bool up_pressed; + bool right_pressed; + bool down_pressed; + bool muted; + bool ptt_pressed; + bool mic_pressed; + bool connected; + bool is_mac_os; + uint32_t appIndex; + HidTransport transport; +} HidPttModel; + +enum HidPttAppIndex { + HidPttAppIndexGoogleMeet, + HidPttAppIndexZoom, + HidPttAppIndexFaceTime, + HidPttAppIndexSkype, + HidPttAppIndexSize, +}; + +static void hid_ptt_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + HidPttModel* model = context; + + // Header + canvas_set_font(canvas, FontPrimary); + if(model->transport == HidTransportBle) { + if(model->connected) { + canvas_draw_icon(canvas, 0, 0, &I_Ble_connected_15x15); + } else { + canvas_draw_icon(canvas, 0, 0, &I_Ble_disconnected_15x15); + } + } + + // App selection + const uint8_t y_app = 78; + canvas_set_font(canvas, FontSecondary); + canvas_draw_icon(canvas, 0, y_app, &I_ButtonLeft_4x7); + if(model->appIndex == HidPttAppIndexGoogleMeet) { + elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Google Meet"); + } else if(model->appIndex == HidPttAppIndexZoom) { + elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Zoom"); + } else if(model->appIndex == HidPttAppIndexFaceTime) { + elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "FaceTime"); + } else if(model->appIndex == HidPttAppIndexSkype) { + elements_multiline_text_aligned(canvas, 7, y_app, AlignLeft, AlignTop, "Skype"); + } + canvas_draw_icon(canvas, 60, y_app, &I_ButtonRight_4x7); + + // OS selection + const uint8_t y_os = 88; + const uint8_t x_os = 7; + // elements_slightly_rounded_box(canvas, model->is_mac_os ? 0 : 26, y_os, model->is_mac_os ? 21 : 26, 11); + elements_slightly_rounded_box(canvas, model->is_mac_os ? x_os : x_os + 26, y_os, model->is_mac_os ? 21 : 26, 11); + canvas_set_color(canvas, model->is_mac_os ? ColorWhite : ColorBlack); + elements_multiline_text_aligned(canvas, x_os + 2, y_os + 1, AlignLeft, AlignTop, "Mac"); + canvas_set_color(canvas, ColorBlack); + if (model->appIndex != HidPttAppIndexFaceTime) { + elements_multiline_text_aligned(canvas, x_os + 23, y_os + 2, AlignLeft, AlignTop, "|"); + canvas_set_color(canvas, model->is_mac_os ? ColorBlack : ColorWhite); + elements_multiline_text_aligned(canvas, x_os + 28, y_os + 2, AlignLeft, AlignTop, "Linux"); + canvas_set_color(canvas, ColorBlack); + } + + // Mic label + const uint8_t y_mic = 102; + canvas_draw_icon(canvas, 19, y_mic - 1, &I_Pin_back_arrow_rotated_8x10); + elements_multiline_text_aligned(canvas, 0, y_mic, AlignLeft, AlignTop, "Hold to sync"); + elements_multiline_text_aligned(canvas, 20, y_mic+10, AlignLeft, AlignTop, "mic status"); + + // Exit label + canvas_draw_icon(canvas, 20, 121, &I_ButtonLeft_4x7); + elements_multiline_text_aligned(canvas, 0, 121, AlignLeft, AlignTop, "Hold to exit"); + + const uint8_t x_1 = 0; + const uint8_t x_2 = x_1 + 19 + 4; + const uint8_t x_3 = x_1 + 19 * 2 + 8; + + const uint8_t y_1 = 19; + const uint8_t y_2 = y_1 + 19; + const uint8_t y_3 = y_2 + 19; + + // Up + canvas_draw_icon(canvas, x_2, y_1, &I_Button_18x18); + if(model->up_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_1 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if(model->ptt_pressed) { + if (model->appIndex != HidPttAppIndexFaceTime) { + elements_multiline_text_aligned(canvas, x_2 + 4, y_1 + 5, AlignLeft, AlignTop, "OS"); + } + } else { + canvas_draw_icon(canvas, x_2 + 5, y_1 + 5, &I_Volup_8x6); + } + canvas_set_color(canvas, ColorBlack); + + // Down + canvas_draw_icon(canvas, x_2, y_3, &I_Button_18x18); + if(model->down_pressed) { + elements_slightly_rounded_box(canvas, x_2 + 3, y_3 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if(!model->ptt_pressed) { + canvas_draw_icon(canvas, x_2 + 6, y_3 + 5, &I_Voldwn_6x6); + } + canvas_set_color(canvas, ColorBlack); + + // Left + canvas_draw_icon(canvas, x_1, y_2, &I_Button_18x18); + if(model->left_pressed) { + elements_slightly_rounded_box(canvas, x_1 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if (model->ptt_pressed) { + canvas_draw_icon(canvas, x_1 + 7, y_2 + 5, &I_ButtonLeft_4x7); + } else { + canvas_draw_icon(canvas, x_1 + 4, y_2 + 5, &I_Pin_back_arrow_10x8); + } + canvas_set_color(canvas, ColorBlack); + + // Right / Camera + canvas_draw_icon(canvas, x_3, y_2, &I_Button_18x18); + if(model->right_pressed) { + elements_slightly_rounded_box(canvas, x_3 + 3, y_2 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + if(!model->ptt_pressed) { + if (model->appIndex != HidPttAppIndexFaceTime) { + canvas_draw_icon(canvas, x_3 + 11, y_2 + 5, &I_ButtonLeft_4x7); + canvas_draw_box(canvas, x_3 + 4, y_2 + 5, 7, 7); + } + } else { + canvas_draw_icon(canvas, x_3 + 8, y_2 + 5, &I_ButtonRight_4x7); + } + canvas_set_color(canvas, ColorBlack); + + // Back / Mic + const uint8_t x_mic = x_3; + canvas_draw_icon(canvas, x_mic, 0, &I_Button_18x18); + if(model->mic_pressed) { + elements_slightly_rounded_box(canvas, x_mic + 3, 0 + 2, 13, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_icon(canvas, x_mic + 5, 0 + 4, &I_Mic_btn_8x10); + if(model->muted && !model->ptt_pressed) { + canvas_draw_line(canvas, x_mic + 3, 2 , x_mic + 3 + 13, 2 + 13); + canvas_draw_line(canvas, x_mic + 2, 2 , x_mic + 2 + 13, 2 + 13); + canvas_draw_line(canvas, x_mic + 3, 2 + 13, x_mic + 3 + 13, 2); + canvas_draw_line(canvas, x_mic + 2, 2 + 13, x_mic + 2 + 13, 2); + } + canvas_set_color(canvas, ColorBlack); + + // Ok / PTT + const uint8_t x_ptt_margin = 4; + const uint8_t x_ptt_width = 17; + const uint8_t x_ptt = x_1 + 19; + canvas_draw_icon(canvas, x_ptt , y_2 , &I_BtnFrameLeft_3x18); + canvas_draw_icon(canvas, x_ptt + x_ptt_width + 3 + x_ptt_margin, y_2 , &I_BtnFrameRight_2x18); + canvas_draw_line(canvas, x_ptt + 3 , y_2 , x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2); + canvas_draw_line(canvas, x_ptt + 3 , y_2 + 16, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 16); + canvas_draw_line(canvas, x_ptt + 3 , y_2 + 17, x_ptt + x_ptt_width + 2 + x_ptt_margin, y_2 + 17); + if(model->ptt_pressed) { + elements_slightly_rounded_box(canvas, x_ptt + 3, y_2 + 2, x_ptt_width + x_ptt_margin, 13); + canvas_set_color(canvas, ColorWhite); + } + canvas_set_font(canvas, FontPrimary); + elements_multiline_text_aligned(canvas, x_ptt + 2 + x_ptt_margin / 2, y_2 + 13, AlignLeft, AlignBottom, "PTT"); + canvas_set_font(canvas, FontSecondary); +} + +static void hid_ptt_trigger_mute(HidPtt* hid_ptt, HidPttModel * model) { + if(model->appIndex == HidPttAppIndexGoogleMeet && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_D ); + } else if(model->appIndex == HidPttAppIndexGoogleMeet && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_D ); + } else if(model->appIndex == HidPttAppIndexZoom && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_A ); + } else if(model->appIndex == HidPttAppIndexFaceTime) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); + } +} + +static void hid_ptt_trigger_camera(HidPtt* hid_ptt, HidPttModel * model) { + if(model->appIndex == HidPttAppIndexGoogleMeet && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI | HID_KEYBOARD_E ); + } else if(model->appIndex == HidPttAppIndexGoogleMeet && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_E ); + } else if(model->appIndex == HidPttAppIndexZoom && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_V ); + } else if(model->appIndex == HidPttAppIndexZoom && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_ALT | HID_KEYBOARD_V ); + } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K ); + } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_K ); + } +} + +static void hid_ptt_start_ptt(HidPtt* hid_ptt, HidPttModel * model) { + if(model->appIndex == HidPttAppIndexGoogleMeet) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); + } else if(model->appIndex == HidPttAppIndexZoom) { + hid_hal_keyboard_press(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); + } else if(model->appIndex == HidPttAppIndexFaceTime) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); + } +} + +static void hid_ptt_stop_ptt(HidPtt* hid_ptt, HidPttModel * model) { + if(model->appIndex == HidPttAppIndexGoogleMeet) { + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); + } else if(model->appIndex == HidPttAppIndexZoom) { + hid_hal_keyboard_release(hid_ptt->hid, HID_KEYBOARD_SPACEBAR); + } else if(model->appIndex == HidPttAppIndexFaceTime) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_GUI| KEY_MOD_LEFT_SHIFT | HID_KEYBOARD_M ); + } else if(model->appIndex == HidPttAppIndexSkype && !model->is_mac_os) { + hid_hal_keyboard_press( hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M); + hid_hal_keyboard_release(hid_ptt->hid, KEY_MOD_LEFT_CTRL | HID_KEYBOARD_M ); + } +} + +// Supports only ±1 +static void hid_ptt_shift_app(HidPttModel * model, int shift) { + int i = (short) model->appIndex; + if (i + shift >= HidPttAppIndexSize) { + model->appIndex = 0; + } else if(i + shift <= 0) { + model->appIndex = HidPttAppIndexSize - 1; + } else { + model->appIndex += shift; + } + // Avoid showing facetime if not macos + if (model->appIndex == HidPttAppIndexFaceTime && !model->is_mac_os) { + hid_ptt_shift_app(model, shift); + } +} + +static void hid_ptt_process(HidPtt* hid_ptt, InputEvent* event) { + with_view_model( + hid_ptt->view, + HidPttModel * model, + { + if(event->type == InputTypePress) { + if(event->key == InputKeyUp) { + model->up_pressed = true; + if (!model->ptt_pressed){ + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); + } else { + if (model->appIndex != HidPttAppIndexFaceTime) { + model->is_mac_os = !model->is_mac_os; + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + } + } else if(event->key == InputKeyDown) { + model->down_pressed = true; + if (!model->ptt_pressed){ + hid_hal_consumer_key_press(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); + } else { + hid_ptt_shift_app(model, - 1); + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + } else if(event->key == InputKeyLeft) { + model->left_pressed = true; + if (model->ptt_pressed){ + hid_ptt_shift_app(model, 1); + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + } else if(event->key == InputKeyRight) { + model->right_pressed = true; + if (model->ptt_pressed){ + hid_ptt_shift_app(model, - 1); + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + } else if(event->key == InputKeyOk) { + model->ptt_pressed = true; + if (model->muted) { + hid_ptt_start_ptt(hid_ptt, model); + } + } else if(event->key == InputKeyBack) { + model->mic_pressed = true; + } + } else if(event->type == InputTypeRelease) { + if(event->key == InputKeyUp) { + model->up_pressed = false; + if (!model->ptt_pressed){ + hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_INCREMENT); + } + } else if(event->key == InputKeyDown) { + model->down_pressed = false; + if (!model->ptt_pressed){ + hid_hal_consumer_key_release(hid_ptt->hid, HID_CONSUMER_VOLUME_DECREMENT); + } + } else if(event->key == InputKeyLeft) { + model->left_pressed = false; + } else if(event->key == InputKeyRight) { + model->right_pressed = false; + + } else if(event->key == InputKeyOk) { + model->ptt_pressed = false; + if (model->muted) { + hid_ptt_stop_ptt(hid_ptt, model); + } else { + hid_ptt_trigger_mute(hid_ptt, model); + model->muted = true; + } + } else if(event->key == InputKeyBack) { + model->mic_pressed = false; + } + } else if(event->type == InputTypeShort) { + if(event->key == InputKeyBack && !model->ptt_pressed ) { // no changes if PTT is pressed + model->muted = !model->muted; + hid_ptt_trigger_mute(hid_ptt, model); + } else if(event->key == InputKeyRight) { + if (!model->ptt_pressed){ + hid_ptt_trigger_camera(hid_ptt, model); + } + } + } else if(event->type == InputTypeLong) { + if(event->key == InputKeyLeft) { + model->left_pressed = false; + if (!model->ptt_pressed){ + hid_hal_keyboard_release_all(hid_ptt->hid); + view_dispatcher_switch_to_view(hid_ptt->hid->view_dispatcher, HidViewSubmenu); + // sequence_double_vibro to notify that we quit PTT + notification_message(hid_ptt->hid->notifications, &sequence_double_vibro); + } + } else if(event->key == InputKeyBack && !model->ptt_pressed ) { // no changes if PTT is pressed + // Change local mic status + model->muted = !model->muted; + notification_message(hid_ptt->hid->notifications, &sequence_single_vibro); + } + } + //LED + if (model->muted && !model->ptt_pressed) { + notification_message(hid_ptt->hid->notifications, &sequence_reset_red); + } else { + notification_message(hid_ptt->hid->notifications, &sequence_set_red_255); + } + }, + true); +} + +static bool hid_ptt_input_callback(InputEvent* event, void* context) { + furi_assert(context); + HidPtt* hid_ptt = context; + bool consumed = true; + hid_ptt_process(hid_ptt, event); + return consumed; +} + +HidPtt* hid_ptt_alloc(Hid* hid) { + HidPtt* hid_ptt = malloc(sizeof(HidPtt)); + hid_ptt->view = view_alloc(); + hid_ptt->hid = hid; + view_set_context(hid_ptt->view, hid_ptt); + view_allocate_model(hid_ptt->view, ViewModelTypeLocking, sizeof(HidPttModel)); + view_set_draw_callback(hid_ptt->view, hid_ptt_draw_callback); + view_set_input_callback(hid_ptt->view, hid_ptt_input_callback); + view_set_orientation(hid_ptt->view, ViewOrientationVerticalFlip); + + with_view_model( + hid_ptt->view, HidPttModel * model, { + model->transport = hid->transport; + model->muted = true; // assume we're muted + model->is_mac_os = true; + }, true); + return hid_ptt; +} + +void hid_ptt_free(HidPtt* hid_ptt) { + furi_assert(hid_ptt); + notification_message(hid_ptt->hid->notifications, &sequence_reset_red); + view_free(hid_ptt->view); + free(hid_ptt); +} + +View* hid_ptt_get_view(HidPtt* hid_ptt) { + furi_assert(hid_ptt); + return hid_ptt->view; +} + +void hid_ptt_set_connected_status(HidPtt* hid_ptt, bool connected) { + furi_assert(hid_ptt); + with_view_model( + hid_ptt->view, HidPttModel * model, { model->connected = connected; }, true); +} diff --git a/applications/system/hid_app/views/hid_ptt.h b/applications/system/hid_app/views/hid_ptt.h new file mode 100644 index 000000000..eb205c20e --- /dev/null +++ b/applications/system/hid_app/views/hid_ptt.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef struct Hid Hid; +typedef struct HidPtt HidPtt; + +HidPtt* hid_ptt_alloc(Hid* bt_hid); + +void hid_ptt_free(HidPtt* hid_ptt); + +View* hid_ptt_get_view(HidPtt* hid_ptt); + +void hid_ptt_set_connected_status(HidPtt* hid_ptt, bool connected); diff --git a/applications/system/hid_app/views/hid_tiktok.c b/applications/system/hid_app/views/hid_tiktok.c index 047563131..0bfcaa2f4 100644 --- a/applications/system/hid_app/views/hid_tiktok.c +++ b/applications/system/hid_app/views/hid_tiktok.c @@ -4,8 +4,6 @@ #include "hid_icons.h" -#include - #define TAG "HidTikTok" struct HidTikTok { diff --git a/applications/system/nightstand/clock_app.c b/applications/system/nightstand/clock_app.c index 787352e93..bb7e5759d 100644 --- a/applications/system/nightstand/clock_app.c +++ b/applications/system/nightstand/clock_app.c @@ -346,8 +346,8 @@ int32_t clock_app(void* p) { furi_hal_rtc_get_datetime(&plugin_state->datetime); }*/ - view_port_update(view_port); furi_mutex_release(plugin_state->mutex); + view_port_update(view_port); } furi_timer_free(timer); diff --git a/applications/system/subghz_remote/application.fam b/applications/system/subghz_remote/application.fam index 1a99a2025..2249931b9 100644 --- a/applications/system/subghz_remote/application.fam +++ b/applications/system/subghz_remote/application.fam @@ -3,8 +3,17 @@ App( name="Sub-GHz Remote", apptype=FlipperAppType.EXTERNAL, entry_point="subghz_remote_app", + requires=[ + "gui", + "dialogs", + ], stack_size=2 * 1024, + targets=["f7"], fap_icon="icon.png", + fap_author="@gid9798 and @xMasterX", fap_description="SubGhz Remote, uses up to 5 .sub files", fap_category="Sub-GHz", + fap_icon_assets="icons", + fap_version="1.3", + fap_weburl="https://github.com/DarkFlippers/SubGHz_Remote", ) diff --git a/applications/system/subghz_remote/helpers/subrem_custom_event.h b/applications/system/subghz_remote/helpers/subrem_custom_event.h index e6b9e8ac6..810df6a89 100644 --- a/applications/system/subghz_remote/helpers/subrem_custom_event.h +++ b/applications/system/subghz_remote/helpers/subrem_custom_event.h @@ -48,4 +48,11 @@ typedef enum { SubRemCustomEventSceneEditPreviewSaved, SubRemCustomEventSceneNewName, + +#ifdef FW_ORIGIN_Official + SubRemCustomEventSceneFwWarningExit, + SubRemCustomEventSceneFwWarningNext, + SubRemCustomEventSceneFwWarningContinue, +#endif + } SubRemCustomEvent; \ No newline at end of file diff --git a/applications/system/subghz_remote/helpers/subrem_presets.c b/applications/system/subghz_remote/helpers/subrem_presets.c index ca91ccbaf..bca888b96 100644 --- a/applications/system/subghz_remote/helpers/subrem_presets.c +++ b/applications/system/subghz_remote/helpers/subrem_presets.c @@ -149,7 +149,9 @@ SubRemLoadSubState subrem_sub_preset_load( if(protocol->flag & SubGhzProtocolFlag_Send) { if((protocol->type == SubGhzProtocolTypeStatic) || (protocol->type == SubGhzProtocolTypeDynamic) || +#ifndef FW_ORIGIN_Official (protocol->type == SubGhzProtocolTypeBinRAW) || +#endif (protocol->type == SubGhzProtocolTypeRAW)) { sub_preset->type = protocol->type; } else { diff --git a/applications/system/subghz_remote/helpers/txrx/subghz_txrx.c b/applications/system/subghz_remote/helpers/txrx/subghz_txrx.c index aa713c7a8..8c8c3b56d 100644 --- a/applications/system/subghz_remote/helpers/txrx/subghz_txrx.c +++ b/applications/system/subghz_remote/helpers/txrx/subghz_txrx.c @@ -1,10 +1,12 @@ #include "subghz_txrx_i.h" -#include +#include #include #include +#ifndef FW_ORIGIN_Official #include +#endif #define TAG "SubGhz" @@ -657,12 +659,14 @@ bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance) { return instance->debug_pin_state; } +#ifndef FW_ORIGIN_Official void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance) { furi_assert(instance); subghz_environment_reset_keeloq(instance->environment); subghz_custom_btns_reset(); } +#endif SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance) { furi_assert(instance); diff --git a/applications/system/subghz_remote/helpers/txrx/subghz_txrx.h b/applications/system/subghz_remote/helpers/txrx/subghz_txrx.h index 4593ea20c..93c4a2276 100644 --- a/applications/system/subghz_remote/helpers/txrx/subghz_txrx.h +++ b/applications/system/subghz_remote/helpers/txrx/subghz_txrx.h @@ -369,7 +369,7 @@ bool subghz_txrx_radio_device_is_tx_allowed(SubGhzTxRx* instance, uint32_t frequ void subghz_txrx_set_debug_pin_state(SubGhzTxRx* instance, bool state); bool subghz_txrx_get_debug_pin_state(SubGhzTxRx* instance); - +#ifndef FW_ORIGIN_Official void subghz_txrx_reset_dynamic_and_custom_btns(SubGhzTxRx* instance); - +#endif SubGhzReceiver* subghz_txrx_get_receiver(SubGhzTxRx* instance); // TODO use only in DecodeRaw diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/down.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/down.png new file mode 100644 index 000000000..198e22ea2 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/down.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/down_hover.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/down_hover.png new file mode 100644 index 000000000..39ac087e7 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/down_hover.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/left.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/left.png new file mode 100644 index 000000000..38b83c79b Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/left.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/left_hover.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/left_hover.png new file mode 100644 index 000000000..45f58b8b6 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/left_hover.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/ok.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/ok.png new file mode 100644 index 000000000..dfd3d2fd1 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/ok.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/ok_hover.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/ok_hover.png new file mode 100644 index 000000000..9107c1a79 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/ok_hover.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/right.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/right.png new file mode 100644 index 000000000..ca6748f9a Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/right.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/right_hover.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/right_hover.png new file mode 100644 index 000000000..2d53ce701 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/right_hover.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/up.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/up.png new file mode 100644 index 000000000..cd032ed12 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/up.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/Dpad/up_hover.png b/applications/system/subghz_remote/icons/remote_scene/Dpad/up_hover.png new file mode 100644 index 000000000..8bc334750 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/Dpad/up_hover.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/statusbar/External_antenna_20x12.png b/applications/system/subghz_remote/icons/remote_scene/statusbar/External_antenna_20x12.png new file mode 100644 index 000000000..940087071 Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/statusbar/External_antenna_20x12.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/statusbar/Internal_antenna_20x12.png b/applications/system/subghz_remote/icons/remote_scene/statusbar/Internal_antenna_20x12.png new file mode 100644 index 000000000..a8a5be09f Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/statusbar/Internal_antenna_20x12.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/statusbar/Status_cube_14x14.png b/applications/system/subghz_remote/icons/remote_scene/statusbar/Status_cube_14x14.png new file mode 100644 index 000000000..f1e72cbbd Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/statusbar/Status_cube_14x14.png differ diff --git a/applications/system/subghz_remote/icons/remote_scene/statusbar/status_bar.png b/applications/system/subghz_remote/icons/remote_scene/statusbar/status_bar.png new file mode 100644 index 000000000..8136fe32e Binary files /dev/null and b/applications/system/subghz_remote/icons/remote_scene/statusbar/status_bar.png differ diff --git a/applications/system/subghz_remote/scenes/subrem_scene_config.h b/applications/system/subghz_remote/scenes/subrem_scene_config.h index 08486be74..56fe641a6 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_config.h +++ b/applications/system/subghz_remote/scenes/subrem_scene_config.h @@ -6,4 +6,7 @@ ADD_SCENE(subrem, edit_submenu, EditSubMenu) ADD_SCENE(subrem, edit_label, EditLabel) ADD_SCENE(subrem, open_sub_file, OpenSubFile) ADD_SCENE(subrem, edit_preview, EditPreview) -ADD_SCENE(subrem, enter_new_name, EnterNewName) \ No newline at end of file +ADD_SCENE(subrem, enter_new_name, EnterNewName) +#ifdef FW_ORIGIN_Official +ADD_SCENE(subrem, fw_warning, FwWarning) +#endif \ No newline at end of file diff --git a/applications/system/subghz_remote/scenes/subrem_scene_edit_label.c b/applications/system/subghz_remote/scenes/subrem_scene_edit_label.c index baec60145..25c10bb46 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_edit_label.c +++ b/applications/system/subghz_remote/scenes/subrem_scene_edit_label.c @@ -29,7 +29,7 @@ void subrem_scene_edit_label_widget_callback(GuiButtonType result, InputType typ void subrem_scene_edit_label_on_enter(void* context) { SubGhzRemoteApp* app = context; - SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub]; + SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chosen_sub]; FuriString* temp_str = furi_string_alloc(); @@ -53,9 +53,9 @@ void subrem_scene_edit_label_on_enter(void* context) { app->file_name_tmp, 25, false); - +#ifndef FW_ORIGIN_Official text_input_set_minimum_length(app->text_input, 0); - +#endif widget_add_string_element( app->widget, 63, 12, AlignCenter, AlignCenter, FontPrimary, "Empty Label Name"); widget_add_string_element( @@ -76,7 +76,7 @@ void subrem_scene_edit_label_on_enter(void* context) { bool subrem_scene_edit_label_on_event(void* context, SceneManagerEvent event) { SubGhzRemoteApp* app = context; - FuriString* label = app->map_preset->subs_preset[app->chusen_sub]->label; + FuriString* label = app->map_preset->subs_preset[app->chosen_sub]->label; if(event.type == SceneManagerEventTypeBack) { if(scene_manager_get_scene_state(app->scene_manager, SubRemSceneEditLabel) == diff --git a/applications/system/subghz_remote/scenes/subrem_scene_edit_menu.c b/applications/system/subghz_remote/scenes/subrem_scene_edit_menu.c index a8882009a..bc54311c5 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_edit_menu.c +++ b/applications/system/subghz_remote/scenes/subrem_scene_edit_menu.c @@ -101,7 +101,7 @@ bool subrem_scene_edit_menu_on_event(void* context, SceneManagerEvent event) { return true; } else if(event.event == SubRemCustomEventViewEditMenuEdit) { - app->chusen_sub = subrem_view_edit_menu_get_index(app->subrem_edit_menu); + app->chosen_sub = subrem_view_edit_menu_get_index(app->subrem_edit_menu); scene_manager_set_scene_state( app->scene_manager, SubRemSceneEditSubMenu, EditSubmenuIndexEditLabel); scene_manager_next_scene(app->scene_manager, SubRemSceneEditSubMenu); diff --git a/applications/system/subghz_remote/scenes/subrem_scene_fw_warning.c b/applications/system/subghz_remote/scenes/subrem_scene_fw_warning.c new file mode 100644 index 000000000..de473b72c --- /dev/null +++ b/applications/system/subghz_remote/scenes/subrem_scene_fw_warning.c @@ -0,0 +1,129 @@ +#include "../subghz_remote_app_i.h" +#include "../helpers/subrem_custom_event.h" +#ifdef FW_ORIGIN_Official +typedef enum { + SceneFwWarningStateAttention, + SceneFwWarningStateAccept, +} SceneFwWarningState; + +static void subrem_scene_fw_warning_widget_render(SubGhzRemoteApp* app, SceneFwWarningState state); + +static void + subrem_scene_fw_warning_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + SubGhzRemoteApp* app = context; + + if(type == InputTypeShort) { + SubRemCustomEvent event = SubRemCustomEventSceneFwWarningExit; + + switch(scene_manager_get_scene_state(app->scene_manager, SubRemSceneFwWarning)) { + case SceneFwWarningStateAttention: + if(result == GuiButtonTypeRight) { + event = SubRemCustomEventSceneFwWarningNext; + } + break; + + case SceneFwWarningStateAccept: + if(result == GuiButtonTypeRight) { + event = SubRemCustomEventSceneFwWarningContinue; + } + + break; + } + + view_dispatcher_send_custom_event(app->view_dispatcher, event); + } +} + +static void + subrem_scene_fw_warning_widget_render(SubGhzRemoteApp* app, SceneFwWarningState state) { + furi_assert(app); + Widget* widget = app->widget; + + widget_reset(widget); + + switch(state) { + case SceneFwWarningStateAttention: + widget_add_button_element( + widget, GuiButtonTypeLeft, "Exit", subrem_scene_fw_warning_widget_callback, app); + widget_add_button_element( + widget, GuiButtonTypeRight, "Continue", subrem_scene_fw_warning_widget_callback, app); + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, "Not official FW"); + widget_add_string_multiline_element( + widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "You are using custom firmware\nPlease download a compatible\nversion of the application"); + break; + + case SceneFwWarningStateAccept: + widget_add_button_element( + widget, GuiButtonTypeLeft, "Exit", subrem_scene_fw_warning_widget_callback, app); + widget_add_button_element( + widget, GuiButtonTypeRight, "Accept", subrem_scene_fw_warning_widget_callback, app); + widget_add_string_element( + widget, 64, 12, AlignCenter, AlignBottom, FontPrimary, "Not official FW"); + widget_add_string_multiline_element( + widget, + 64, + 32, + AlignCenter, + AlignCenter, + FontSecondary, + "Yes, I understand that\nthe application can\nbreak my subghz key file"); + break; + } +} + +void subrem_scene_fw_warning_on_enter(void* context) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + + scene_manager_set_scene_state( + app->scene_manager, SubRemSceneFwWarning, SceneFwWarningStateAttention); + + subrem_scene_fw_warning_widget_render(app, SceneFwWarningStateAttention); + + view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget); +} + +bool subrem_scene_fw_warning_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeBack) { + consumed = true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubRemCustomEventSceneFwWarningExit) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } else if(event.event == SubRemCustomEventSceneFwWarningNext) { + scene_manager_set_scene_state( + app->scene_manager, SubRemSceneFwWarning, SceneFwWarningStateAccept); + subrem_scene_fw_warning_widget_render(app, SceneFwWarningStateAccept); + view_dispatcher_switch_to_view(app->view_dispatcher, SubRemViewIDWidget); + consumed = true; + } else if(event.event == SubRemCustomEventSceneFwWarningContinue) { + scene_manager_previous_scene(app->scene_manager); + consumed = true; + } + } + + return consumed; +} + +void subrem_scene_fw_warning_on_exit(void* context) { + furi_assert(context); + + SubGhzRemoteApp* app = context; + widget_reset(app->widget); +} +#endif \ No newline at end of file diff --git a/applications/system/subghz_remote/scenes/subrem_scene_open_sub_file.c b/applications/system/subghz_remote/scenes/subrem_scene_open_sub_file.c index 663e80ba7..d34f01b99 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_open_sub_file.c +++ b/applications/system/subghz_remote/scenes/subrem_scene_open_sub_file.c @@ -9,7 +9,7 @@ void subrem_scene_open_sub_file_error_popup_callback(void* context) { SubRemLoadSubState subrem_scene_open_sub_file_dialog(SubGhzRemoteApp* app) { furi_assert(app); - SubRemSubFilePreset* sub = app->map_preset->subs_preset[app->chusen_sub]; + SubRemSubFilePreset* sub = app->map_preset->subs_preset[app->chosen_sub]; FuriString* temp_file_path = furi_string_alloc(); @@ -48,8 +48,8 @@ SubRemLoadSubState subrem_scene_open_sub_file_dialog(SubGhzRemoteApp* app) { furi_record_close(RECORD_STORAGE); if(ret == SubRemLoadSubStateOK) { - subrem_sub_file_preset_free(app->map_preset->subs_preset[app->chusen_sub]); - app->map_preset->subs_preset[app->chusen_sub] = sub_candidate; + subrem_sub_file_preset_free(app->map_preset->subs_preset[app->chosen_sub]); + app->map_preset->subs_preset[app->chosen_sub] = sub_candidate; app->map_not_saved = true; } else { subrem_sub_file_preset_free(sub_candidate); diff --git a/applications/system/subghz_remote/scenes/subrem_scene_remote.c b/applications/system/subghz_remote/scenes/subrem_scene_remote.c index e8d57dae7..efe289871 100644 --- a/applications/system/subghz_remote/scenes/subrem_scene_remote.c +++ b/applications/system/subghz_remote/scenes/subrem_scene_remote.c @@ -66,19 +66,19 @@ bool subrem_scene_remote_on_event(void* context, SceneManagerEvent event) { // Start sending sub subrem_tx_stop_sub(app, true); - uint8_t chusen_sub = subrem_scene_remote_event_to_index(event.event); - app->chusen_sub = chusen_sub; + uint8_t chosen_sub = subrem_scene_remote_event_to_index(event.event); + app->chosen_sub = chosen_sub; subrem_view_remote_set_state( - app->subrem_remote_view, SubRemViewRemoteStateLoading, chusen_sub); + app->subrem_remote_view, SubRemViewRemoteStateLoading, chosen_sub); - if(subrem_tx_start_sub(app, app->map_preset->subs_preset[chusen_sub])) { - if(app->map_preset->subs_preset[chusen_sub]->type == SubGhzProtocolTypeRAW) { + if(subrem_tx_start_sub(app, app->map_preset->subs_preset[chosen_sub])) { + if(app->map_preset->subs_preset[chosen_sub]->type == SubGhzProtocolTypeRAW) { subghz_txrx_set_raw_file_encoder_worker_callback_end( app->txrx, subrem_scene_remote_raw_callback_end_tx, app); } subrem_view_remote_set_state( - app->subrem_remote_view, SubRemViewRemoteStateSending, chusen_sub); + app->subrem_remote_view, SubRemViewRemoteStateSending, chosen_sub); notification_message(app->notifications, &sequence_blink_start_magenta); } else { subrem_view_remote_set_state( diff --git a/applications/system/subghz_remote/subghz_remote_app.c b/applications/system/subghz_remote/subghz_remote_app.c index 1af39f57d..473e3c012 100644 --- a/applications/system/subghz_remote/subghz_remote_app.c +++ b/applications/system/subghz_remote/subghz_remote_app.c @@ -1,4 +1,5 @@ #include "subghz_remote_app_i.h" +#include static bool subghz_remote_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); @@ -174,7 +175,9 @@ int32_t subghz_remote_app(void* arg) { subghz_remote_make_app_folder(subghz_remote_app); bool map_loaded = false; - +#ifdef FW_ORIGIN_Official + const bool fw_ofw = strcmp(version_get_firmware_origin(version_get()), "Official") == 0; +#endif if((arg != NULL) && (strlen(arg) != 0)) { furi_string_set(subghz_remote_app->file_path, (const char*)arg); SubRemLoadMapState load_state = subrem_map_file_load( @@ -193,8 +196,19 @@ int32_t subghz_remote_app(void* arg) { } else { furi_string_set(subghz_remote_app->file_path, SUBREM_APP_FOLDER); scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneStart); +#ifdef FW_ORIGIN_Official + if(fw_ofw) { + scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile); + } + } + + if(!fw_ofw) { + scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneFwWarning); + } +#else scene_manager_next_scene(subghz_remote_app->scene_manager, SubRemSceneOpenMapFile); } +#endif view_dispatcher_run(subghz_remote_app->view_dispatcher); diff --git a/applications/system/subghz_remote/subghz_remote_app_i.c b/applications/system/subghz_remote/subghz_remote_app_i.c index 82e762c2a..45321441d 100644 --- a/applications/system/subghz_remote/subghz_remote_app_i.c +++ b/applications/system/subghz_remote/subghz_remote_app_i.c @@ -3,12 +3,9 @@ #include #include "helpers/txrx/subghz_txrx.h" - -// #include -// #include - -#include +#ifndef FW_ORIGIN_Official #include +#endif #define TAG "SubGhzRemote" @@ -199,7 +196,7 @@ void subrem_save_active_sub(void* context) { furi_assert(context); SubGhzRemoteApp* app = context; - SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub]; + SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chosen_sub]; subrem_save_protocol_to_file( sub_preset->fff_data, furi_string_get_cstr(sub_preset->file_path)); } @@ -225,9 +222,9 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset) sub_preset->freq_preset.frequency, NULL, 0); - +#ifndef FW_ORIGIN_Official subghz_custom_btns_reset(); - +#endif if(subghz_txrx_tx_start(app->txrx, sub_preset->fff_data) == SubGhzTxRxStartTxStateOk) { ret = true; } @@ -238,16 +235,16 @@ bool subrem_tx_start_sub(SubGhzRemoteApp* app, SubRemSubFilePreset* sub_preset) bool subrem_tx_stop_sub(SubGhzRemoteApp* app, bool forced) { furi_assert(app); - SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chusen_sub]; + SubRemSubFilePreset* sub_preset = app->map_preset->subs_preset[app->chosen_sub]; if(forced || (sub_preset->type != SubGhzProtocolTypeRAW)) { subghz_txrx_stop(app->txrx); - +#ifndef FW_ORIGIN_Official if(sub_preset->type == SubGhzProtocolTypeDynamic) { subghz_txrx_reset_dynamic_and_custom_btns(app->txrx); } subghz_custom_btns_reset(); - +#endif return true; } diff --git a/applications/system/subghz_remote/subghz_remote_app_i.h b/applications/system/subghz_remote/subghz_remote_app_i.h index 42e9576fd..a02c9fd78 100644 --- a/applications/system/subghz_remote/subghz_remote_app_i.h +++ b/applications/system/subghz_remote/subghz_remote_app_i.h @@ -6,6 +6,8 @@ #include "helpers/txrx/subghz_txrx.h" +#include "subghz_remote_icons.h" + #include #include "views/remote.h" @@ -50,7 +52,7 @@ typedef struct { bool map_not_saved; - uint8_t chusen_sub; + uint8_t chosen_sub; } SubGhzRemoteApp; SubRemLoadMapState subrem_load_from_file(SubGhzRemoteApp* app); @@ -65,4 +67,4 @@ void subrem_map_preset_reset(SubRemMapPreset* map_preset); bool subrem_save_map_to_file(SubGhzRemoteApp* app); -void subrem_save_active_sub(void* context); +void subrem_save_active_sub(void* context); \ No newline at end of file diff --git a/applications/system/subghz_remote/views/edit_menu.c b/applications/system/subghz_remote/views/edit_menu.c index 9b88182b5..6dd590ed0 100644 --- a/applications/system/subghz_remote/views/edit_menu.c +++ b/applications/system/subghz_remote/views/edit_menu.c @@ -19,7 +19,7 @@ typedef struct { FuriString* file_path; SubRemLoadSubState sub_state; - uint8_t chusen; + uint8_t chosen; } SubRemViewEditMenuModel; void subrem_view_edit_menu_set_callback( @@ -44,7 +44,7 @@ void subrem_view_edit_menu_add_data_to_show( subrem_view_edit_remote->view, SubRemViewEditMenuModel * model, { - model->chusen = index; + model->chosen = index; if(!furi_string_empty(label)) { furi_string_set(model->label, label); } else { @@ -63,7 +63,7 @@ uint8_t subrem_view_edit_menu_get_index(SubRemViewEditMenu* subrem_view_edit_rem with_view_model( subrem_view_edit_remote->view, SubRemViewEditMenuModel * model, - { index = model->chusen; }, + { index = model->chosen; }, true); return index; } @@ -89,7 +89,7 @@ void subrem_view_edit_menu_draw(Canvas* canvas, SubRemViewEditMenuModel* model) // Draw btn name canvas_set_font(canvas, FontPrimary); - switch(model->chusen) { + switch(model->chosen) { case SubRemSubKeyNameUp: canvas_draw_str(canvas, 3, FRAME_HEIGHT - 2, "UP"); break; @@ -129,10 +129,10 @@ void subrem_view_edit_menu_draw(Canvas* canvas, SubRemViewEditMenuModel* model) // Draw arrow canvas_set_color(canvas, ColorBlack); - if(model->chusen != 0) { + if(model->chosen != 0) { canvas_draw_icon(canvas, 119, 13, &I_Pin_arrow_up_7x9); } - if(model->chusen != 4) { + if(model->chosen != 4) { canvas_draw_icon_ex(canvas, 119, 42, &I_Pin_arrow_up_7x9, IconRotation180); } @@ -200,8 +200,8 @@ bool subrem_view_edit_menu_input(InputEvent* event, void* context) { subrem_view_edit_menu->view, SubRemViewEditMenuModel * model, { - if(model->chusen > 0) { - model->chusen -= 1; + if(model->chosen > 0) { + model->chosen -= 1; }; }, true); @@ -213,8 +213,8 @@ bool subrem_view_edit_menu_input(InputEvent* event, void* context) { subrem_view_edit_menu->view, SubRemViewEditMenuModel * model, { - if(model->chusen < 4) { - model->chusen += 1; + if(model->chosen < 4) { + model->chosen += 1; }; }, true); @@ -263,7 +263,7 @@ SubRemViewEditMenu* subrem_view_edit_menu_alloc() { model->label = furi_string_alloc(); // furi_string_alloc_set_str("LABEL"); model->file_path = furi_string_alloc(); // furi_string_alloc_set_str("FILE_PATH"); - model->chusen = 0; + model->chosen = 0; }, true); return subrem_view_edit_menu; diff --git a/applications/system/subghz_remote/views/remote.c b/applications/system/subghz_remote/views/remote.c index bdf4b89c3..2523d1977 100644 --- a/applications/system/subghz_remote/views/remote.c +++ b/applications/system/subghz_remote/views/remote.c @@ -8,7 +8,7 @@ #define SUBREM_VIEW_REMOTE_MAX_LABEL_LENGTH 30 #define SUBREM_VIEW_REMOTE_LEFT_OFFSET 10 -#define SUBREM_VIEW_REMOTE_RIGHT_OFFSET 22 +#define SUBREM_VIEW_REMOTE_RIGHT_OFFSET 0 struct SubRemViewRemote { View* view; @@ -120,26 +120,43 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); - canvas_clear(canvas); + // Statusbar + canvas_draw_icon(canvas, 0, 0, &I_status_bar); + if(model->state == SubRemViewRemoteStateOFF) { + canvas_invert_color(canvas); + canvas_draw_rbox(canvas, 12, 0, 52 - 12, 13, 2); + canvas_invert_color(canvas); + canvas_draw_rframe(canvas, 12, 0, 52 - 12, 13, 2); + canvas_draw_str_aligned(canvas, 32, 3, AlignCenter, AlignTop, "Preview"); + } else { + canvas_draw_icon( + canvas, + 0, + 2, + (model->is_external) ? &I_External_antenna_20x12 : &I_Internal_antenna_20x12); + canvas_draw_icon(canvas, 50, 0, &I_Status_cube_14x14); + if(model->state == SubRemViewRemoteStateSending) { + canvas_draw_icon_ex(canvas, 52, 3, &I_Pin_arrow_up_7x9, IconRotation90); + } + } //Icons for Labels - //canvas_draw_icon(canvas, 0, 0, &I_SubGHzRemote_LeftAlignedButtons_9x64); - canvas_draw_icon(canvas, 1, 5, &I_ButtonUp_7x4); - canvas_draw_icon(canvas, 1, 15, &I_ButtonDown_7x4); - canvas_draw_icon(canvas, 2, 23, &I_ButtonLeft_4x7); - canvas_draw_icon(canvas, 2, 33, &I_ButtonRight_4x7); - canvas_draw_icon(canvas, 0, 42, &I_Ok_btn_9x9); - canvas_draw_icon(canvas, 0, 53, &I_back_10px); + const uint8_t list_y = 14; + canvas_draw_icon(canvas, 1, list_y + 5, &I_ButtonUp_7x4); + canvas_draw_icon(canvas, 1, list_y + 15, &I_ButtonDown_7x4); + canvas_draw_icon(canvas, 2, list_y + 23, &I_ButtonLeft_4x7); + canvas_draw_icon(canvas, 2, list_y + 33, &I_ButtonRight_4x7); + canvas_draw_icon(canvas, 0, list_y + 42, &I_Ok_btn_9x9); //Labels canvas_set_font(canvas, FontSecondary); - uint8_t y = 0; + uint8_t y = list_y; for(uint8_t i = 0; i < SubRemSubKeyNameMaxCount; i++) { elements_text_box( canvas, SUBREM_VIEW_REMOTE_LEFT_OFFSET, y + 2, - 126 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET, + 64 - SUBREM_VIEW_REMOTE_LEFT_OFFSET - SUBREM_VIEW_REMOTE_RIGHT_OFFSET, 12, AlignLeft, AlignBottom, @@ -148,52 +165,50 @@ void subrem_view_remote_draw(Canvas* canvas, SubRemViewRemoteModel* model) { y += 10; } - if(model->state == SubRemViewRemoteStateOFF) { - elements_button_left(canvas, "Back"); - elements_button_right(canvas, "Save"); - } else { - canvas_draw_str_aligned(canvas, 11, 62, AlignLeft, AlignBottom, "Hold=Exit."); - canvas_draw_str_aligned( - canvas, 126, 62, AlignRight, AlignBottom, ((model->is_external) ? "Ext" : "Int")); - } + if(model->state != SubRemViewRemoteStateOFF) { + // D-pad 59x62 + const uint8_t d_pad_x = 3; + const uint8_t d_pad_y = 66; - //Status text and indicator - canvas_draw_icon(canvas, 113, 15, &I_Pin_cell_13x13); + canvas_draw_icon(canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 0 * (20 + 1), &I_up); - if(model->state == SubRemViewRemoteStateIdle || model->state == SubRemViewRemoteStateOFF) { - canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Idle"); - } else { - switch(model->state) { - case SubRemViewRemoteStateSending: - canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Send"); - break; - case SubRemViewRemoteStateLoading: - canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Load"); - break; - default: -#if FURI_DEBUG - canvas_draw_str_aligned(canvas, 126, 10, AlignRight, AlignBottom, "Wrong_state"); -#endif - break; + canvas_draw_icon(canvas, d_pad_x + 0 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_left); + canvas_draw_icon(canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_ok); + canvas_draw_icon(canvas, d_pad_x + 2 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_right); + + canvas_draw_icon(canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 2 * (20 + 1), &I_down); + if(model->state == SubRemViewRemoteStateSending) { + switch(model->pressed_btn) { + case SubRemSubKeyNameUp: + canvas_draw_icon( + canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 0 * (20 + 1), &I_up_hover); + break; + case SubRemSubKeyNameDown: + canvas_draw_icon( + canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 2 * (20 + 1), &I_down_hover); + break; + case SubRemSubKeyNameLeft: + canvas_draw_icon( + canvas, d_pad_x + 0 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_left_hover); + break; + case SubRemSubKeyNameRight: + canvas_draw_icon( + canvas, d_pad_x + 2 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_right_hover); + break; + case SubRemSubKeyNameOk: + canvas_draw_icon( + canvas, d_pad_x + 1 * (19 + 1), d_pad_y + 1 * (20 + 1), &I_ok_hover); + break; + default: + break; + } } + } else { + canvas_draw_icon(canvas, 2, 128 - 11, &I_ButtonLeft_4x7); + canvas_draw_str_aligned(canvas, 8, 128 - 4, AlignLeft, AlignBottom, "Back"); - switch(model->pressed_btn) { - case SubRemSubKeyNameUp: - canvas_draw_icon(canvas, 116, 17, &I_Pin_arrow_up_7x9); - break; - case SubRemSubKeyNameDown: - canvas_draw_icon_ex(canvas, 116, 17, &I_Pin_arrow_up_7x9, IconRotation180); - break; - case SubRemSubKeyNameLeft: - canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation270); - break; - case SubRemSubKeyNameRight: - canvas_draw_icon_ex(canvas, 115, 18, &I_Pin_arrow_up_7x9, IconRotation90); - break; - case SubRemSubKeyNameOk: - canvas_draw_icon(canvas, 116, 18, &I_Pin_star_7x7); - break; - } + canvas_draw_icon(canvas, 58, 128 - 11, &I_ButtonRight_4x7); + canvas_draw_str_aligned(canvas, 56, 128 - 4, AlignRight, AlignBottom, "Save"); } } @@ -271,6 +286,7 @@ SubRemViewRemote* subrem_view_remote_alloc() { view_allocate_model( subrem_view_remote->view, ViewModelTypeLocking, sizeof(SubRemViewRemoteModel)); view_set_context(subrem_view_remote->view, subrem_view_remote); + view_set_orientation(subrem_view_remote->view, ViewOrientationVertical); view_set_draw_callback(subrem_view_remote->view, (ViewDrawCallback)subrem_view_remote_draw); view_set_input_callback(subrem_view_remote->view, subrem_view_remote_input); view_set_enter_callback(subrem_view_remote->view, subrem_view_remote_enter); diff --git a/applications/system/text_viewer/application.fam b/applications/system/text_viewer/application.fam index 6bdfe7c89..cbbe8b2d4 100644 --- a/applications/system/text_viewer/application.fam +++ b/applications/system/text_viewer/application.fam @@ -12,6 +12,6 @@ App( fap_category="Tools", fap_icon_assets="icons", fap_author="@Willy-JL", # Original by @kowalski7cc & @kyhwana, new has code borrowed from archive > show - fap_version="1.0", + fap_version="1.5", fap_description="Text viewer application", )