Update system apps

This commit is contained in:
Willy-JL
2023-11-19 23:15:17 +00:00
parent 2c8a2006c1
commit 3b898ece92
76 changed files with 2390 additions and 440 deletions

View File

@@ -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.",
)

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,8 @@
#include <notification/notification_messages.h>
void hex_viewer_play_happy_bump(void* context);
void hex_viewer_play_bad_bump(void* context);
void hex_viewer_play_long_bump(void* context);

View File

@@ -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 = {
&notification_led_message_1,
&notification_led_message_2,
&notification_led_message_3,
&message_do_not_reset,
NULL,
};
notification_message(app->notification, &notification_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
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stdlib.h>
#include <string.h>
#include <storage/storage.h>
#include <flipper_format/flipper_format_i.h>
#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);

View File

@@ -1,295 +1,145 @@
#include <furi.h>
#include <furi_hal.h>
#include "hex_viewer.h"
#include "hex_viewer_icons.h"
#include <assets_icons.h>
#include <gui/gui.h>
#include <gui/elements.h>
#include <dialogs/dialogs.h>
#include <storage/storage.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include <toolbox/stream/file_stream.h>
#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;
}

View File

@@ -0,0 +1,93 @@
#pragma once
#include <furi.h>
#include <furi_hal.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdlib.h>
#include <hex_viewer_icons.h>
#include <dialogs/dialogs.h>
#include <notification/notification_messages.h>
#include <gui/view_dispatcher.h>
#include <gui/modules/submenu.h>
#include <gui/modules/text_input.h>
#include <gui/scene_manager.h>
#include <gui/modules/variable_item_list.h>
#include <gui/modules/button_menu.h>
#include <gui/modules/dialog_ex.h>
#include "scenes/hex_viewer_scene.h"
#include "views/hex_viewer_startscreen.h"
#include "helpers/hex_viewer_storage.h"
#include <storage/storage.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#include <toolbox/stream/file_stream.h>
#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;

View File

@@ -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,
};

View File

@@ -0,0 +1,29 @@
#pragma once
#include <gui/scene_manager.h>
// 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

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -0,0 +1,147 @@
#include "../hex_viewer.h"
#include <lib/toolbox/value_index.h>
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);
}

View File

@@ -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);
}

View File

@@ -0,0 +1,245 @@
#include "../hex_viewer.h"
#include <furi.h>
#include <furi_hal.h>
#include <input/input.h>
#include <gui/elements.h>
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;
}

View File

@@ -0,0 +1,19 @@
#pragma once
#include <gui/view.h>
#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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 959 B

View File

@@ -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);

View File

@@ -14,16 +14,19 @@
#include <storage/storage.h>
#include <gui/modules/submenu.h>
#include <gui/modules/dialog_ex.h>
#include <gui/modules/popup.h>
#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 <assets_icons.h>
#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);
void hid_hal_mouse_release_all(Hid* instance);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 969 B

After

Width:  |  Height:  |  Size: 174 B

View File

@@ -4,9 +4,10 @@ typedef enum {
HidViewKeyboard,
HidViewNumpad,
HidViewMedia,
HidViewMovie,
HidViewMouse,
HidViewMouseClicker,
HidViewMouseJiggler,
BtHidViewTikTok,
HidViewExitConfirm,
} HidView;
HidViewPtt,
} HidView;

View File

@@ -5,8 +5,6 @@
#include "../hid.h"
#include "hid_icons.h"
#include <assets_icons.h>
#define TAG "HidKeyboard"
struct HidKeyboard {

View File

@@ -4,8 +4,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#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");
}

View File

@@ -7,8 +7,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#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

View File

@@ -4,8 +4,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#define TAG "HidMouse"
struct HidMouse {

View File

@@ -4,8 +4,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#define TAG "HidMouseClicker"
#define DEFAULT_CLICK_RATE 1
#define MAXIMUM_CLICK_RATE 60

View File

@@ -4,8 +4,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#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);
}
},

View File

@@ -2,8 +2,7 @@
#include <gui/view.h>
#define MOUSE_MOVE_SHORT 5
#define MOUSE_MOVE_LONG 20
#define MOUSE_MOVE_TINY 1
typedef struct Hid Hid;
typedef struct HidMouseJiggler HidMouseJiggler;

View File

@@ -0,0 +1,229 @@
#include "hid_movie.h"
#include <furi.h>
#include <furi_hal_bt_hid.h>
#include <furi_hal_usb_hid.h>
#include <gui/elements.h>
#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);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
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);

View File

@@ -5,8 +5,6 @@
#include "../hid.h"
#include "hid_icons.h"
#include <assets_icons.h>
#define TAG "HidNumpad"
struct HidNumpad {

View File

@@ -0,0 +1,431 @@
#include "hid_ptt.h"
#include <gui/elements.h>
#include <notification/notification_messages.h>
#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);
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <gui/view.h>
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);

View File

@@ -4,8 +4,6 @@
#include "hid_icons.h"
#include <assets_icons.h>
#define TAG "HidTikTok"
struct HidTikTok {

View File

@@ -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);

View File

@@ -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",
)

View File

@@ -48,4 +48,11 @@ typedef enum {
SubRemCustomEventSceneEditPreviewSaved,
SubRemCustomEventSceneNewName,
#ifdef FW_ORIGIN_Official
SubRemCustomEventSceneFwWarningExit,
SubRemCustomEventSceneFwWarningNext,
SubRemCustomEventSceneFwWarningContinue,
#endif
} SubRemCustomEvent;

View File

@@ -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 {

View File

@@ -1,10 +1,12 @@
#include "subghz_txrx_i.h"
#include <lib/subghz/protocols/protocol_items.h>
#include <lib/subghz/subghz_protocol_registry.h>
#include <applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h>
#include <lib/subghz/devices/cc1101_int/cc1101_int_interconnect.h>
#ifndef FW_ORIGIN_Official
#include <lib/subghz/blocks/custom_btn.h>
#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);

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

View File

@@ -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)
ADD_SCENE(subrem, enter_new_name, EnterNewName)
#ifdef FW_ORIGIN_Official
ADD_SCENE(subrem, fw_warning, FwWarning)
#endif

View File

@@ -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) ==

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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(

View File

@@ -1,4 +1,5 @@
#include "subghz_remote_app_i.h"
#include <lib/toolbox/version.h>
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);

View File

@@ -3,12 +3,9 @@
#include <flipper_format/flipper_format_i.h>
#include "helpers/txrx/subghz_txrx.h"
// #include <lib/subghz/protocols/keeloq.h>
// #include <lib/subghz/protocols/star_line.h>
#include <lib/subghz/protocols/protocol_items.h>
#ifndef FW_ORIGIN_Official
#include <lib/subghz/blocks/custom_btn.h>
#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;
}

View File

@@ -6,6 +6,8 @@
#include "helpers/txrx/subghz_txrx.h"
#include "subghz_remote_icons.h"
#include <assets_icons.h>
#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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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",
)