Update system apps
@@ -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.",
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
36
applications/system/hex_viewer/helpers/hex_viewer_haptic.c
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
39
applications/system/hex_viewer/helpers/hex_viewer_led.c
Normal 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 = {
|
||||
¬ification_led_message_1,
|
||||
¬ification_led_message_2,
|
||||
¬ification_led_message_3,
|
||||
&message_do_not_reset,
|
||||
NULL,
|
||||
};
|
||||
notification_message(app->notification, ¬ification_sequence);
|
||||
furi_thread_flags_wait(0, FuriFlagWaitAny, 10); //Delay, prevent removal from RAM before LED value set
|
||||
}
|
||||
|
||||
void hex_viewer_led_reset(void* context) {
|
||||
HexViewer* app = context;
|
||||
notification_message(app->notification, &sequence_reset_red);
|
||||
notification_message(app->notification, &sequence_reset_green);
|
||||
notification_message(app->notification, &sequence_reset_blue);
|
||||
|
||||
furi_thread_flags_wait(0, FuriFlagWaitAny, 300); //Delay, prevent removal from RAM before LED value set
|
||||
}
|
||||
6
applications/system/hex_viewer/helpers/hex_viewer_led.h
Normal 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);
|
||||
|
||||
27
applications/system/hex_viewer/helpers/hex_viewer_speaker.c
Normal 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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
171
applications/system/hex_viewer/helpers/hex_viewer_storage.c
Normal 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;
|
||||
}
|
||||
23
applications/system/hex_viewer/helpers/hex_viewer_storage.h
Normal 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);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
93
applications/system/hex_viewer/hex_viewer.h
Normal 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;
|
||||
30
applications/system/hex_viewer/scenes/hex_viewer_scene.c
Normal 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,
|
||||
};
|
||||
29
applications/system/hex_viewer/scenes/hex_viewer_scene.h
Normal 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
|
||||
@@ -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)
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
245
applications/system/hex_viewer/views/hex_viewer_startscreen.c
Normal 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;
|
||||
}
|
||||
@@ -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);
|
||||
BIN
applications/system/hid_app/assets/BtnFrameLeft_3x18.png
Normal file
|
After Width: | Height: | Size: 362 B |
BIN
applications/system/hid_app/assets/BtnFrameRight_2x18.png
Normal file
|
After Width: | Height: | Size: 362 B |
BIN
applications/system/hid_app/assets/Mic_btn_8x10.png
Normal file
|
After Width: | Height: | Size: 141 B |
|
After Width: | Height: | Size: 959 B |
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 174 B |
@@ -4,9 +4,10 @@ typedef enum {
|
||||
HidViewKeyboard,
|
||||
HidViewNumpad,
|
||||
HidViewMedia,
|
||||
HidViewMovie,
|
||||
HidViewMouse,
|
||||
HidViewMouseClicker,
|
||||
HidViewMouseJiggler,
|
||||
BtHidViewTikTok,
|
||||
HidViewExitConfirm,
|
||||
} HidView;
|
||||
HidViewPtt,
|
||||
} HidView;
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
#include "../hid.h"
|
||||
#include "hid_icons.h"
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "HidKeyboard"
|
||||
|
||||
struct HidKeyboard {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "HidMouse"
|
||||
|
||||
struct HidMouse {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
|
||||
229
applications/system/hid_app/views/hid_movie.c
Normal 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);
|
||||
}
|
||||
14
applications/system/hid_app/views/hid_movie.h
Normal 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);
|
||||
@@ -5,8 +5,6 @@
|
||||
#include "../hid.h"
|
||||
#include "hid_icons.h"
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "HidNumpad"
|
||||
|
||||
struct HidNumpad {
|
||||
|
||||
431
applications/system/hid_app/views/hid_ptt.c
Normal 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);
|
||||
}
|
||||
14
applications/system/hid_app/views/hid_ptt.h
Normal 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);
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "hid_icons.h"
|
||||
|
||||
#include <assets_icons.h>
|
||||
|
||||
#define TAG "HidTikTok"
|
||||
|
||||
struct HidTikTok {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -48,4 +48,11 @@ typedef enum {
|
||||
SubRemCustomEventSceneEditPreviewSaved,
|
||||
|
||||
SubRemCustomEventSceneNewName,
|
||||
|
||||
#ifdef FW_ORIGIN_Official
|
||||
SubRemCustomEventSceneFwWarningExit,
|
||||
SubRemCustomEventSceneFwWarningNext,
|
||||
SubRemCustomEventSceneFwWarningContinue,
|
||||
#endif
|
||||
|
||||
} SubRemCustomEvent;
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
After Width: | Height: | Size: 191 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 176 B |
BIN
applications/system/subghz_remote/icons/remote_scene/Dpad/ok.png
Normal file
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 206 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 178 B |
BIN
applications/system/subghz_remote/icons/remote_scene/Dpad/up.png
Normal file
|
After Width: | Height: | Size: 186 B |
|
After Width: | Height: | Size: 170 B |
|
After Width: | Height: | Size: 990 B |
|
After Width: | Height: | Size: 994 B |
|
After Width: | Height: | Size: 125 B |
|
After Width: | Height: | Size: 214 B |
@@ -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
|
||||
@@ -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) ==
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||