[FL-1401] Add Universal TV remote (#539)

* Remove excess headers
* Add ButtonPanel
* Add Popup
* Move FileReader to standalone object
* Universal remote (part 1)
* Universal remote (part 2)
* Global rename tranciever/file_parser
* Compile assets
* syntax fix
* English: rename tranceiver to transceiver.

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Albert Kharisov 2021-06-25 16:52:27 +03:00 committed by GitHub
parent 5e08674663
commit c583cce5bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 1878 additions and 740 deletions

View File

@ -8,6 +8,42 @@
#include <string.h>
#include <stdint.h>
void elements_progress_bar(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t progress,
uint8_t total) {
furi_assert(canvas);
furi_assert(total > 0);
uint8_t height = 9;
uint8_t marker_width = 7;
furi_assert(width > marker_width);
uint8_t progress_length = ((float)progress / total) * (width - marker_width - 2);
// rframe doesnt work if (radius * 2) > any rect side, so write manually
uint8_t x_max = x + width - 1;
uint8_t y_max = y + height - 1;
canvas_draw_line(canvas, x + 3, y, x_max - 3, y);
canvas_draw_line(canvas, x_max - 3, y, x_max, y + 3);
canvas_draw_line(canvas, x_max, y + 3, x_max, y_max - 3);
canvas_draw_line(canvas, x_max, y_max - 3, x_max - 3, y_max);
canvas_draw_line(canvas, x_max - 3, y_max, x + 3, y_max);
canvas_draw_line(canvas, x + 3, y_max, x, y_max - 3);
canvas_draw_line(canvas, x, y_max - 3, x, y + 3);
canvas_draw_line(canvas, x, y + 3, x + 3, y);
canvas_draw_rbox(canvas, x + 1, y + 1, marker_width + progress_length, height - 2, 3);
canvas_invert_color(canvas);
canvas_draw_dot(canvas, x + progress_length + 3, y + 2);
canvas_draw_dot(canvas, x + progress_length + 4, y + 2);
canvas_draw_dot(canvas, x + progress_length + 5, y + 3);
canvas_draw_dot(canvas, x + progress_length + 6, y + 4);
canvas_invert_color(canvas);
}
void elements_scrollbar_pos(
Canvas* canvas,
uint8_t x,

View File

@ -8,6 +8,22 @@
extern "C" {
#endif
/*
* Draw progress bar.
* @param x - progress bar position on X axis
* @param y - progress bar position on Y axis
* @param width - progress bar width
* @param progress - progress in unnamed metric
* @param total - total amount in unnamed metric
*/
void elements_progress_bar(
Canvas* canvas,
uint8_t x,
uint8_t y,
uint8_t width,
uint8_t progress,
uint8_t total);
/*
* Draw scrollbar on canvas at specific position.
* @param x - scrollbar position on X axis
@ -16,7 +32,6 @@ extern "C" {
* @param pos - current element
* @param total - total elements
*/
void elements_scrollbar_pos(
Canvas* canvas,
uint8_t x,

View File

@ -0,0 +1,409 @@
#include "button_panel.h"
#include "api-hal-resources.h"
#include "gui/canvas.h"
#include <m-array.h>
#include <m-i-list.h>
#include <m-list.h>
#include <furi.h>
#include <gui/elements.h>
#include <stdint.h>
typedef struct {
// uint16_t to support multi-screen, wide button panel
uint16_t x;
uint16_t y;
Font font;
const char* str;
} LabelElement;
LIST_DEF(LabelList, LabelElement, M_POD_OPLIST)
#define M_OPL_LabelList_t() LIST_OPLIST(LabelList)
typedef struct {
uint16_t x;
uint16_t y;
IconName name;
IconName name_selected;
} IconElement;
typedef struct ButtonItem {
uint32_t index;
ButtonItemCallback callback;
IconElement icon;
void* callback_context;
} ButtonItem;
ARRAY_DEF(ButtonArray, ButtonItem*, M_PTR_OPLIST);
#define M_OPL_ButtonArray_t() ARRAY_OPLIST(ButtonArray, M_PTR_OPLIST)
ARRAY_DEF(ButtonMatrix, ButtonArray_t);
#define M_OPL_ButtonMatrix_t() ARRAY_OPLIST(ButtonMatrix, M_OPL_ButtonArray_t())
struct ButtonPanel {
View* view;
ButtonPanelInputCallback input_callback;
void* input_context;
};
typedef struct {
ButtonMatrix_t button_matrix;
LabelList_t labels;
uint16_t reserve_x;
uint16_t reserve_y;
uint16_t selected_item_x;
uint16_t selected_item_y;
ButtonPanelDrawCallback draw_callback;
void* draw_context;
} ButtonPanelModel;
static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y);
static void button_panel_process_up(ButtonPanel* button_panel);
static void button_panel_process_down(ButtonPanel* button_panel);
static void button_panel_process_left(ButtonPanel* button_panel);
static void button_panel_process_right(ButtonPanel* button_panel);
static void button_panel_process_ok(ButtonPanel* button_panel);
static void button_panel_view_draw_callback(Canvas* canvas, void* _model);
static bool button_panel_view_input_callback(InputEvent* event, void* context);
ButtonPanel* button_panel_alloc() {
ButtonPanel* button_panel = furi_alloc(sizeof(ButtonPanel));
button_panel->view = view_alloc();
view_set_orientation(button_panel->view, ViewOrientationVertical);
view_set_context(button_panel->view, button_panel);
view_allocate_model(button_panel->view, ViewModelTypeLocking, sizeof(ButtonPanelModel));
view_set_draw_callback(button_panel->view, button_panel_view_draw_callback);
view_set_input_callback(button_panel->view, button_panel_view_input_callback);
button_panel->input_callback = NULL;
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
model->reserve_x = 0;
model->reserve_y = 0;
model->selected_item_x = 0;
model->selected_item_y = 0;
model->draw_callback = NULL;
ButtonMatrix_init(model->button_matrix);
LabelList_init(model->labels);
return true;
});
return button_panel;
}
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y) {
furi_check(reserve_x > 0);
furi_check(reserve_y > 0);
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
model->reserve_x = reserve_x;
model->reserve_y = reserve_y;
ButtonMatrix_reserve(model->button_matrix, model->reserve_y);
for(size_t i = 0; i > model->reserve_y; ++i) {
ButtonArray_t* array = ButtonMatrix_get(model->button_matrix, i);
ButtonArray_init(*array);
ButtonArray_reserve(*array, reserve_x);
// TODO: do we need to clear allocated memory of ptr-s to ButtonItem ??
}
LabelList_init(model->labels);
return true;
});
}
void button_panel_free(ButtonPanel* button_panel) {
furi_assert(button_panel);
button_panel_clean(button_panel);
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
LabelList_clear(model->labels);
ButtonMatrix_clear(model->button_matrix);
return true;
});
view_free(button_panel->view);
free(button_panel);
}
void button_panel_clean(ButtonPanel* button_panel) {
furi_assert(button_panel);
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
for(size_t x = 0; x < model->reserve_x; ++x) {
for(size_t y = 0; y < model->reserve_y; ++y) {
ButtonItem** button_item = button_panel_get_item(model, x, y);
free(*button_item);
*button_item = NULL;
}
}
return true;
});
}
static ButtonItem** button_panel_get_item(ButtonPanelModel* model, size_t x, size_t y) {
furi_assert(model);
furi_check(x < model->reserve_x);
furi_check(y < model->reserve_y);
ButtonArray_t* button_array = ButtonMatrix_get_at(model->button_matrix, x);
ButtonItem** button_item = ButtonArray_get_at(*button_array, y);
return button_item;
}
void button_panel_add_item(
ButtonPanel* button_panel,
uint32_t index,
uint16_t matrix_place_x,
uint16_t matrix_place_y,
uint16_t x,
uint16_t y,
IconName icon_name,
IconName icon_name_selected,
ButtonItemCallback callback,
void* callback_context) {
furi_assert(button_panel);
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
ButtonItem** button_item_ptr =
button_panel_get_item(model, matrix_place_x, matrix_place_y);
furi_check(*button_item_ptr == NULL);
*button_item_ptr = furi_alloc(sizeof(ButtonItem));
ButtonItem* button_item = *button_item_ptr;
button_item->callback = callback;
button_item->callback_context = callback_context;
button_item->icon.x = x;
button_item->icon.y = y;
button_item->icon.name = icon_name;
button_item->icon.name_selected = icon_name_selected;
button_item->index = index;
return true;
});
}
View* button_panel_get_view(ButtonPanel* button_panel) {
furi_assert(button_panel);
return button_panel->view;
}
static void button_panel_view_draw_callback(Canvas* canvas, void* _model) {
furi_assert(canvas);
furi_assert(_model);
ButtonPanelModel* model = _model;
canvas_clear(canvas);
canvas_set_color(canvas, ColorBlack);
for(size_t x = 0; x < model->reserve_x; ++x) {
for(size_t y = 0; y < model->reserve_y; ++y) {
ButtonItem* button_item = *button_panel_get_item(model, x, y);
IconName icon_name = button_item->icon.name;
if((model->selected_item_x == x) && (model->selected_item_y == y)) {
icon_name = button_item->icon.name_selected;
}
canvas_draw_icon_name(canvas, button_item->icon.x, button_item->icon.y, icon_name);
}
}
for
M_EACH(label, model->labels, LabelList_t) {
canvas_set_font(canvas, label->font);
canvas_draw_str(canvas, label->x, label->y, label->str);
}
if(model->draw_callback) model->draw_callback(canvas, model->draw_context);
}
static void button_panel_process_down(ButtonPanel* button_panel) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
size_t new_selected_item_x = model->selected_item_x;
size_t new_selected_item_y = model->selected_item_y;
size_t i;
if(new_selected_item_y >= (model->reserve_y - 1)) return false;
++new_selected_item_y;
for(i = 0; i < model->reserve_x; ++i) {
new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
break;
}
}
if(i == model->reserve_x) return false;
model->selected_item_x = new_selected_item_x;
model->selected_item_y = new_selected_item_y;
return true;
});
}
static void button_panel_process_up(ButtonPanel* button_panel) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
size_t new_selected_item_x = model->selected_item_x;
size_t new_selected_item_y = model->selected_item_y;
size_t i;
if(new_selected_item_y <= 0) return false;
--new_selected_item_y;
for(i = 0; i < model->reserve_x; ++i) {
new_selected_item_x = (model->selected_item_x + i) % model->reserve_x;
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
break;
}
}
if(i == model->reserve_x) return false;
model->selected_item_x = new_selected_item_x;
model->selected_item_y = new_selected_item_y;
return true;
});
}
static void button_panel_process_left(ButtonPanel* button_panel) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
size_t new_selected_item_x = model->selected_item_x;
size_t new_selected_item_y = model->selected_item_y;
size_t i;
if(new_selected_item_x <= 0) return false;
--new_selected_item_x;
for(i = 0; i < model->reserve_y; ++i) {
new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
break;
}
}
if(i == model->reserve_y) return false;
model->selected_item_x = new_selected_item_x;
model->selected_item_y = new_selected_item_y;
return true;
});
}
static void button_panel_process_right(ButtonPanel* button_panel) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
size_t new_selected_item_x = model->selected_item_x;
size_t new_selected_item_y = model->selected_item_y;
size_t i;
if(new_selected_item_x >= (model->reserve_x - 1)) return false;
++new_selected_item_x;
for(i = 0; i < model->reserve_y; ++i) {
new_selected_item_y = (model->selected_item_y + i) % model->reserve_y;
if(*button_panel_get_item(model, new_selected_item_x, new_selected_item_y)) {
break;
}
}
if(i == model->reserve_y) return false;
model->selected_item_x = new_selected_item_x;
model->selected_item_y = new_selected_item_y;
return true;
});
}
void button_panel_process_ok(ButtonPanel* button_panel) {
ButtonItem* button_item = NULL;
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
button_item =
*button_panel_get_item(model, model->selected_item_x, model->selected_item_y);
return true;
});
if(button_item && button_item->callback) {
button_item->callback(button_item->callback_context, button_item->index);
}
}
static bool button_panel_view_input_callback(InputEvent* event, void* context) {
ButtonPanel* button_panel = context;
furi_assert(button_panel);
bool consumed = false;
if(button_panel->input_callback) {
consumed = button_panel->input_callback(event, button_panel->input_context);
} else if(event->type == InputTypeShort) {
switch(event->key) {
case InputKeyUp:
consumed = true;
button_panel_process_up(button_panel);
break;
case InputKeyDown:
consumed = true;
button_panel_process_down(button_panel);
break;
case InputKeyLeft:
consumed = true;
button_panel_process_left(button_panel);
break;
case InputKeyRight:
consumed = true;
button_panel_process_right(button_panel);
break;
case InputKeyOk:
consumed = true;
button_panel_process_ok(button_panel);
break;
default:
break;
}
}
return consumed;
}
void button_panel_add_label(
ButtonPanel* button_panel,
uint16_t x,
uint16_t y,
Font font,
const char* label_str) {
furi_assert(button_panel);
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
LabelElement* label = LabelList_push_raw(model->labels);
label->x = x;
label->y = y;
label->font = font;
label->str = label_str;
return true;
});
}
void button_panel_set_popup_draw_callback(
ButtonPanel* button_panel,
ButtonPanelDrawCallback callback,
void* context) {
with_view_model(
button_panel->view, (ButtonPanelModel * model) {
model->draw_callback = callback;
model->draw_context = context;
return true;
});
}
void button_panel_set_popup_input_callback(
ButtonPanel* button_panel,
ButtonPanelInputCallback callback,
void* context) {
button_panel->input_context = context;
button_panel->input_callback = callback;
}

View File

@ -0,0 +1,129 @@
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Button panel module descriptor */
typedef struct ButtonPanel ButtonPanel;
/** Callback type to call for handling selecting button_panel items */
typedef void (*ButtonItemCallback)(void* context, uint32_t index);
/** Callback type for additional drawings above main button_panel screen */
typedef void (*ButtonPanelDrawCallback)(Canvas* canvas, void* _model);
/** Callback type to intercept input events of button_panel */
typedef bool (*ButtonPanelInputCallback)(InputEvent* event, void* context);
/** Allocate new button_panel module.
*
* @return just-created module
*/
ButtonPanel* button_panel_alloc(void);
/** Free button_panel module.
*
* @param button_panel - module to free
*/
void button_panel_free(ButtonPanel* button_panel);
/** Free items from button_panel module. Preallocated matrix stays unchanged.
*
* @param button_panel - module to clean
*/
void button_panel_clean(ButtonPanel* button_panel);
/** Reserve space for adding items.
*
* One does not simply use button_panel_add_item() without this function.
* It should be allocated space for it first.
*
* @param button_panel - module to modify
* @param reserve_x - number of columns in button_panel
* @param reserve_y - number of rows in button_panel
*/
void button_panel_reserve(ButtonPanel* button_panel, size_t reserve_x, size_t reserve_y);
/** Add item to button_panel module.
*
* Have to set element in bounds of allocated size by X and by Y.
*
* @param button_panel - module
* @param index - value to pass to callback
* @param matrix_place_x - coordinates by x-axis on virtual grid, it
* is only used for naviagation
* @param matrix_place_y - coordinates by y-axis on virtual grid, it
* is only used for naviagation
* @param x - x-coordinate to draw icon on
* @param y - y-coordinate to draw icon on
* @param icon_name - name of the icon to draw
* @param icon_name_selected - name of the icon to draw when current
* element is selected
* @param callback - function to call when specific element is selected
* (pressed Ok on selected item)
* @param callback_context - context to pass to callback
*/
void button_panel_add_item(
ButtonPanel* button_panel,
uint32_t index,
uint16_t matrix_place_x,
uint16_t matrix_place_y,
uint16_t x,
uint16_t y,
IconName icon_name,
IconName icon_name_selected,
ButtonItemCallback callback,
void* callback_context);
/** Get button_panel view.
*
* @param button_panel - module to get view from
* @return acquired view
*/
View* button_panel_get_view(ButtonPanel* button_panel);
/** Add label to button_panel module.
*
* @param x - x-coordinate to place label
* @param y - y-coordinate to place label
* @param font - font to write label with
* @param label_str - string label to write
*/
void button_panel_add_label(
ButtonPanel* button_panel,
uint16_t x,
uint16_t y,
Font font,
const char* label_str);
// TODO: [FL-1445] Have to replace callbacks above with additional popup-layer
/** Set popup draw callback for button_panel module.
*
* Used to add popup drawings after main draw callback is done.
*
* @param button_panel - module to modify
* @param callback - callback function to set for draw event
* @param context - context to pass to callback
*/
void button_panel_set_popup_draw_callback(
ButtonPanel* button_panel,
ButtonPanelDrawCallback callback,
void* context);
/** Set popup input callback for button_panel module.
*
* Used to add popup input callback. It will intercept all input
* events for current view.
*
* @param button_panel - module to modify
* @param callback - function to overwrite main input callbacks
* @param context - context to pass to callback
*/
void button_panel_set_popup_input_callback(
ButtonPanel* button_panel,
ButtonPanelInputCallback callback,
void* context);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,77 @@
#include "irda-app-brute-force.hpp"
void IrdaAppBruteForce::add_record(int index, const char* name) {
records[name].index = index;
records[name].amount = 0;
}
bool IrdaAppBruteForce::calculate_messages() {
bool fs_res = false;
fs_res = file_parser.get_fs_api().file.open(
&file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING);
if(!fs_res) {
file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "Can't open file");
return false;
}
file_parser.reset();
while(1) {
auto message = file_parser.read_message(&file);
if(!message) break;
auto element = records.find(message->name);
if(element != records.cend()) {
++element->second.amount;
}
}
file_parser.get_fs_api().file.close(&file);
return true;
}
void IrdaAppBruteForce::stop_bruteforce() {
if(current_record.size()) {
file_parser.get_fs_api().file.close(&file);
current_record.clear();
}
}
// TODO: [FL-1418] replace with timer-chained consequence of messages.
bool IrdaAppBruteForce::send_next_bruteforce(const IrdaAppSignalTransceiver& transceiver) {
furi_assert(current_record.size());
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> message;
do {
message = file_parser.read_message(&file);
} while(message && current_record.compare(message->name));
if(message) {
transceiver.send_message(&message->message);
}
return !!message;
}
bool IrdaAppBruteForce::start_bruteforce(int index, int& record_amount) {
file_parser.reset();
for(const auto& it : records) {
if(it.second.index == index) {
record_amount = it.second.amount;
current_record = it.first;
break;
}
}
if(record_amount) {
bool fs_res = file_parser.get_fs_api().file.open(
&file, universal_db_filename, FSAM_READ, FSOM_OPEN_EXISTING);
if(fs_res) {
return true;
} else {
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Can't open file");
}
}
return false;
}

View File

@ -0,0 +1,38 @@
#pragma once
#include "furi/check.h"
#include <unordered_map>
#include "irda-app-file-parser.hpp"
#include "irda-app-transceiver.hpp"
class IrdaAppBruteForce {
const char* universal_db_filename;
IrdaAppFileParser file_parser;
File file;
std::string current_record;
typedef struct {
int index;
int amount;
} Record;
// 'key' is record name, because we have to search by both, index and name,
// but index search has place once per button press, and should not be
// noticed, but name search should occur during entering universal menu,
// and will go through container for every record in file, that's why
// more critical to have faster search by record name.
std::unordered_map<std::string, Record> records;
public:
bool calculate_messages();
void stop_bruteforce();
bool send_next_bruteforce(const IrdaAppSignalTransceiver& receiver);
bool start_bruteforce(int index, int& record_amount);
void add_record(int index, const char* name);
IrdaAppBruteForce(const char* filename) : universal_db_filename (filename) {}
~IrdaAppBruteForce() {
stop_bruteforce();
}
};

View File

@ -1,5 +1,4 @@
#pragma once
#include <stdint.h>
#include <irda.h>
#include <gui/modules/dialog_ex.h>
@ -14,6 +13,8 @@ public:
IrdaMessageReceived,
TextEditDone,
PopupTimer,
ButtonPanelPressed,
ButtonPanelPopupBackPressed,
};
union {

View File

@ -0,0 +1,58 @@
#include "irda-app-file-parser.hpp"
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> IrdaAppFileParser::read_message(File* file) {
while(1) {
auto str = getline(file);
if(str.empty()) return nullptr;
auto message = parse_message(str);
if(message) return message;
}
}
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage>
IrdaAppFileParser::parse_message(const std::string& str) const {
char protocol_name[32];
uint32_t address;
uint32_t command;
auto irda_file_message = std::make_unique<IrdaFileMessage>();
int parsed = std::sscanf(
str.c_str(),
"%31s %31s A:%lX C:%lX",
irda_file_message->name,
protocol_name,
&address,
&command);
if(parsed != 4) {
return nullptr;
}
IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
return nullptr;
}
int address_length = irda_get_protocol_address_length(protocol);
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
if(address != (address & address_mask)) {
return nullptr;
}
int command_length = irda_get_protocol_command_length(protocol);
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
if(command != (command & command_mask)) {
return nullptr;
}
irda_file_message->message = {
.protocol = protocol,
.address = address,
.command = command,
.repeat = false,
};
return irda_file_message;
}

View File

@ -0,0 +1,17 @@
#pragma once
#include "file_reader/file_reader.hpp"
#include "irda.h"
class IrdaAppFileParser : public FileReader {
public:
typedef struct {
char name[32];
IrdaMessage message;
} IrdaFileMessage;
std::unique_ptr<IrdaAppFileParser::IrdaFileMessage> read_message(File* file);
private:
std::unique_ptr<IrdaFileMessage> parse_message(const std::string& str) const;
};

View File

@ -4,10 +4,10 @@
#include "furi/check.h"
#include "gui/modules/button_menu.h"
#include "irda.h"
#include "sys/_stdint.h"
#include <cstdio>
#include <string>
#include <utility>
#include "irda-app-file-parser.hpp"
const char* IrdaAppRemoteManager::irda_directory = "irda";
const char* IrdaAppRemoteManager::irda_extension = ".ir";
@ -33,16 +33,6 @@ find_vacant_name(const std::vector<std::string>& strings, const std::string& nam
}
}
IrdaAppRemoteManager::IrdaAppRemoteManager() {
sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
}
IrdaAppRemoteManager::~IrdaAppRemoteManager() {
furi_record_close("sdcard");
furi_record_close("sdcard-ex");
}
bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) {
remote->buttons.emplace_back(button_name, message);
return store();
@ -94,10 +84,12 @@ std::string IrdaAppRemoteManager::make_filename(const std::string& name) const {
bool IrdaAppRemoteManager::delete_remote() {
FS_Error fs_res;
IrdaAppFileParser file_parser;
fs_res = fs_api->common.remove(make_filename(remote->name).c_str());
fs_res = file_parser.get_fs_api().common.remove(make_filename(remote->name).c_str());
if(fs_res != FSE_OK) {
show_file_error_message("Error deleting file");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Error deleting file");
return false;
}
remote.reset();
@ -147,11 +139,13 @@ bool IrdaAppRemoteManager::rename_remote(const char* str) {
if(!result) return false;
auto new_name = find_vacant_name(remote_list, str);
FS_Error fs_err = fs_api->common.rename(
IrdaAppFileParser file_parser;
FS_Error fs_err = file_parser.get_fs_api().common.rename(
make_filename(remote->name).c_str(), make_filename(new_name).c_str());
remote->name = new_name;
if(fs_err != FSE_OK) {
show_file_error_message("Error renaming\nremote file");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Error renaming\nremote file");
}
return fs_err == FSE_OK;
}
@ -170,26 +164,25 @@ size_t IrdaAppRemoteManager::get_number_of_buttons() {
return remote->buttons.size();
}
void IrdaAppRemoteManager::show_file_error_message(const char* error_text) const {
sd_ex_api->show_error(sd_ex_api->context, error_text);
}
bool IrdaAppRemoteManager::store(void) {
File file;
uint16_t write_count;
std::string dirname(std::string("/") + irda_directory);
FS_Error fs_err = fs_api->common.mkdir(dirname.c_str());
IrdaAppFileParser file_parser;
FS_Error fs_err = file_parser.get_fs_api().common.mkdir(dirname.c_str());
if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) {
show_file_error_message("Can't create directory");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Can't create directory");
return false;
}
std::string filename = dirname + "/" + remote->name + irda_extension;
bool res = fs_api->file.open(&file, filename.c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
bool res = file_parser.get_fs_api().file.open(
&file, make_filename(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
if(!res) {
show_file_error_message("Cannot create\nnew remote file");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Cannot create\nnew remote file");
return false;
}
@ -210,103 +203,21 @@ bool IrdaAppRemoteManager::store(void) {
button.message.command);
auto content_len = strlen(content);
write_count = fs_api->file.write(&file, content, content_len);
write_count = file_parser.get_fs_api().file.write(&file, content, content_len);
if(file.error_id != FSE_OK || write_count != content_len) {
show_file_error_message("Cannot write\nto key file");
fs_api->file.close(&file);
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Cannot write\nto key file");
file_parser.get_fs_api().file.close(&file);
return false;
}
}
fs_api->file.close(&file);
sd_ex_api->check_error(sd_ex_api->context);
file_parser.get_fs_api().file.close(&file);
file_parser.get_sd_api().check_error(file_parser.get_sd_api().context);
return true;
}
bool IrdaAppRemoteManager::parse_button(std::string& str) {
char button_name[32];
char protocol_name[32];
uint32_t address;
uint32_t command;
int parsed = std::sscanf(
str.c_str(), "%31s %31s A:%lX C:%lX", button_name, protocol_name, &address, &command);
if(parsed != 4) {
return false;
}
IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name);
if(!irda_is_protocol_valid((IrdaProtocol)protocol)) {
return false;
}
int address_length = irda_get_protocol_address_length(protocol);
uint32_t address_mask = (1LU << (4 * address_length)) - 1;
if(address != (address & address_mask)) {
return false;
}
int command_length = irda_get_protocol_command_length(protocol);
uint32_t command_mask = (1LU << (4 * command_length)) - 1;
if(command != (command & command_mask)) {
return false;
}
IrdaMessage irda_message = {
.protocol = protocol,
.address = address,
.command = command,
.repeat = false,
};
remote->buttons.emplace_back(button_name, &irda_message);
return true;
}
std::string getline(
const FS_Api* fs_api,
File& file,
char file_buf[],
size_t file_buf_size,
size_t& file_buf_cnt) {
std::string str;
size_t newline_index = 0;
bool found_eol = false;
while(1) {
if(file_buf_cnt > 0) {
size_t end_index = 0;
char* endline_ptr = (char*)memchr(file_buf, '\n', file_buf_cnt);
newline_index = endline_ptr - file_buf;
if(endline_ptr == 0) {
end_index = file_buf_cnt;
} else if(newline_index < file_buf_cnt) {
end_index = newline_index + 1;
found_eol = true;
} else {
furi_assert(0);
}
str.append(file_buf, end_index);
memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
file_buf_cnt = file_buf_cnt - end_index;
if(found_eol) break;
}
file_buf_cnt +=
fs_api->file.read(&file, &file_buf[file_buf_cnt], file_buf_size - file_buf_cnt);
if(file_buf_cnt == 0) {
break; // end of reading
}
}
return str;
}
bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const {
bool fs_res = false;
char name[128];
@ -314,17 +225,19 @@ bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_name
std::string dirname(std::string("/") + irda_directory);
remote_names.clear();
fs_res = fs_api->dir.open(&dir, dirname.c_str());
IrdaAppFileParser file_parser;
fs_res = file_parser.get_fs_api().dir.open(&dir, dirname.c_str());
if(!fs_res) {
if(!check_fs()) {
show_file_error_message("Cannot open\napplication directory");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Cannot open\napplication directory");
return false;
} else {
return true; // SD ok, but no files written yet
}
}
while(fs_api->dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
while(file_parser.get_fs_api().dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
std::string filename(name);
auto extension_index = filename.rfind(irda_extension);
if((extension_index == std::string::npos) ||
@ -333,36 +246,41 @@ bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_name
}
remote_names.push_back(filename.erase(extension_index));
}
fs_api->dir.close(&dir);
file_parser.get_fs_api().dir.close(&dir);
return true;
}
bool IrdaAppRemoteManager::load(const std::string& name) {
bool fs_res = false;
IrdaAppFileParser file_parser;
File file;
fs_res = fs_api->file.open(&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
fs_res = file_parser.get_fs_api().file.open(
&file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
if(!fs_res) {
show_file_error_message("Error opening file");
file_parser.get_sd_api().show_error(
file_parser.get_sd_api().context, "Error opening file");
return false;
}
remote = std::make_unique<IrdaAppRemote>(name);
while(1) {
auto str = getline(fs_api, file, file_buf, sizeof(file_buf), file_buf_cnt);
if(str.empty()) break;
parse_button(str);
auto message = file_parser.read_message(&file);
if(!message) break;
remote->buttons.emplace_back(message->name, &message->message);
}
fs_api->file.close(&file);
file_parser.get_fs_api().file.close(&file);
return true;
}
bool IrdaAppRemoteManager::check_fs() const {
// TODO: [FL-1431] Add return value to sd_ex_api->check_error() and replace get_fs_info().
auto fs_err = fs_api->common.get_fs_info(nullptr, nullptr);
if(fs_err != FSE_OK) show_file_error_message("SD card not found");
// TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info().
IrdaAppFileParser file_parser;
auto fs_err = file_parser.get_fs_api().common.get_fs_info(nullptr, nullptr);
if(fs_err != FSE_OK)
file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "SD card not found");
return fs_err == FSE_OK;
}

View File

@ -1,9 +1,6 @@
#pragma once
#include "sys/_stdint.h"
#include <algorithm>
#include <stdint.h>
#include <string>
#include <list>
#include <vector>
#include <memory>
#include <irda.h>
@ -38,14 +35,7 @@ class IrdaAppRemoteManager {
static const char* irda_directory;
static const char* irda_extension;
std::unique_ptr<IrdaAppRemote> remote;
// TODO: make FS_Api and SdCard_Api unique_ptr
SdCard_Api* sd_ex_api;
FS_Api* fs_api;
void show_file_error_message(const char* error_text) const;
bool parse_button(std::string& str);
std::string make_filename(const std::string& name) const;
char file_buf[48];
size_t file_buf_cnt = 0;
public:
bool add_remote_with_button(const char* button_name, const IrdaMessage* message);
@ -63,8 +53,6 @@ public:
const IrdaMessage* get_button_data(size_t button_index) const;
bool delete_button(uint32_t index);
bool delete_remote();
IrdaAppRemoteManager();
~IrdaAppRemoteManager();
bool store();
bool load(const std::string& name);

View File

@ -2,10 +2,10 @@
#include "irda.h"
#include <api-hal-irda.h>
void IrdaAppSignalReceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) {
void IrdaAppSignalTransceiver::irda_rx_callback(void* ctx, bool level, uint32_t duration) {
IrdaAppEvent event;
const IrdaMessage* irda_message;
IrdaAppSignalReceiver* this_ = static_cast<IrdaAppSignalReceiver*>(ctx);
IrdaAppSignalTransceiver* this_ = static_cast<IrdaAppSignalTransceiver*>(ctx);
irda_message = irda_decode(this_->decoder, level, duration);
if(irda_message) {
@ -17,30 +17,30 @@ void IrdaAppSignalReceiver::irda_rx_callback(void* ctx, bool level, uint32_t dur
}
}
IrdaAppSignalReceiver::IrdaAppSignalReceiver(void)
IrdaAppSignalTransceiver::IrdaAppSignalTransceiver(void)
: decoder(irda_alloc_decoder()) {
}
IrdaAppSignalReceiver::~IrdaAppSignalReceiver() {
IrdaAppSignalTransceiver::~IrdaAppSignalTransceiver() {
api_hal_irda_rx_irq_deinit();
irda_free_decoder(decoder);
}
void IrdaAppSignalReceiver::capture_once_start(osMessageQueueId_t queue) {
void IrdaAppSignalTransceiver::capture_once_start(osMessageQueueId_t queue) {
event_queue = queue;
irda_reset_decoder(decoder);
api_hal_irda_rx_irq_init();
api_hal_irda_rx_irq_set_callback(IrdaAppSignalReceiver::irda_rx_callback, this);
api_hal_irda_rx_irq_set_callback(IrdaAppSignalTransceiver::irda_rx_callback, this);
}
void IrdaAppSignalReceiver::capture_stop(void) {
void IrdaAppSignalTransceiver::capture_stop(void) {
api_hal_irda_rx_irq_deinit();
}
IrdaMessage* IrdaAppSignalReceiver::get_last_message(void) {
IrdaMessage* IrdaAppSignalTransceiver::get_last_message(void) {
return &message;
}
void IrdaAppSignalReceiver::send_message(const IrdaMessage* message) {
void IrdaAppSignalTransceiver::send_message(const IrdaMessage* message) const {
irda_send(message, 1);
}

View File

@ -1,14 +1,15 @@
#pragma once
#include <furi.h>
#include <irda.h>
class IrdaAppSignalReceiver {
class IrdaAppSignalTransceiver {
public:
IrdaAppSignalReceiver(void);
~IrdaAppSignalReceiver(void);
IrdaAppSignalTransceiver(void);
~IrdaAppSignalTransceiver(void);
void capture_once_start(osMessageQueueId_t event_queue);
void capture_stop(void);
IrdaMessage* get_last_message(void);
void send_message(const IrdaMessage* message);
void send_message(const IrdaMessage* message) const;
private:
osMessageQueueId_t event_queue;

View File

@ -1,7 +1,5 @@
#include "furi.h"
#include "gui/modules/button_menu.h"
#include "gui/modules/dialog_ex.h"
#include "gui/modules/text_input.h"
#include "gui/modules/button_panel.h"
#include "irda-app.hpp"
#include <callback-connector.h>
@ -19,13 +17,17 @@ IrdaAppViewManager::IrdaAppViewManager() {
popup = popup_alloc();
dialog_ex = dialog_ex_alloc();
text_input = text_input_alloc();
button_panel = button_panel_alloc();
popup_brut = popup_brut_alloc();
add_view(ViewType::ButtonPanel, button_panel_get_view(button_panel));
add_view(ViewType::ButtonMenu, button_menu_get_view(button_menu));
add_view(ViewType::Submenu, submenu_get_view(submenu));
add_view(ViewType::Popup, popup_get_view(popup));
add_view(ViewType::DialogEx, dialog_ex_get_view(dialog_ex));
add_view(ViewType::TextInput, text_input_get_view(text_input));
view_set_previous_callback(button_panel_get_view(button_panel), callback);
view_set_previous_callback(button_menu_get_view(button_menu), callback);
view_set_previous_callback(submenu_get_view(submenu), callback);
view_set_previous_callback(popup_get_view(popup), callback);
@ -34,6 +36,8 @@ IrdaAppViewManager::IrdaAppViewManager() {
}
IrdaAppViewManager::~IrdaAppViewManager() {
view_dispatcher_remove_view(
view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonPanel));
view_dispatcher_remove_view(
view_dispatcher, static_cast<uint32_t>(IrdaAppViewManager::ViewType::ButtonMenu));
view_dispatcher_remove_view(
@ -47,9 +51,11 @@ IrdaAppViewManager::~IrdaAppViewManager() {
submenu_free(submenu);
popup_free(popup);
button_panel_free(button_panel);
button_menu_free(button_menu);
dialog_ex_free(dialog_ex);
text_input_free(text_input);
popup_brut_free(popup_brut);
view_dispatcher_free(view_dispatcher);
furi_record_close("gui");
@ -80,6 +86,14 @@ ButtonMenu* IrdaAppViewManager::get_button_menu() {
return button_menu;
}
ButtonPanel* IrdaAppViewManager::get_button_panel() {
return button_panel;
}
IrdaAppPopupBrut* IrdaAppViewManager::get_popup_brut() {
return popup_brut;
}
osMessageQueueId_t IrdaAppViewManager::get_event_queue() {
return event_queue;
}

View File

@ -7,6 +7,8 @@
#include <gui/modules/submenu.h>
#include <gui/modules/popup.h>
#include "irda-app.hpp"
#include "view/irda-app-brut-view.h"
#include "gui/modules/button_panel.h"
class IrdaAppViewManager {
public:
@ -15,6 +17,7 @@ public:
TextInput,
Submenu,
ButtonMenu,
ButtonPanel,
Popup,
};
@ -31,6 +34,8 @@ public:
Popup* get_popup();
TextInput* get_text_input();
ButtonMenu* get_button_menu();
ButtonPanel* get_button_panel();
IrdaAppPopupBrut* get_popup_brut();
osMessageQueueId_t get_event_queue();
@ -44,6 +49,8 @@ private:
Submenu* submenu;
Popup* popup;
ButtonMenu* button_menu;
ButtonPanel* button_panel;
IrdaAppPopupBrut* popup_brut;
osMessageQueueId_t event_queue;

View File

@ -1,9 +1,7 @@
#include "irda-app.hpp"
#include "sys/_stdint.h"
#include <furi.h>
#include <gui/gui.h>
#include <input/input.h>
#include <stdarg.h>
#include <stdio.h>
#include <callback-connector.h>
@ -101,8 +99,8 @@ IrdaAppRemoteManager* IrdaApp::get_remote_manager() {
return &remote_manager;
}
IrdaAppSignalReceiver* IrdaApp::get_receiver() {
return &receiver;
IrdaAppSignalTransceiver* IrdaApp::get_transceiver() {
return &transceiver;
}
void IrdaApp::set_text_store(uint8_t index, const char* text...) {

View File

@ -1,13 +1,13 @@
#pragma once
#include "sys/_stdint.h"
#include <map>
#include <irda.h>
#include <furi.h>
#include "irda/scene/irda-app-scene.hpp"
#include "irda-app-event.hpp"
#include "scene/irda-app-scene.hpp"
#include "irda-app-view-manager.hpp"
#include "irda-app-remote-manager.hpp"
#include "irda-app-receiver.hpp"
#include "irda-app-transceiver.hpp"
#include <forward_list>
#include <stdint.h>
#include <notification/notification-messages.h>
@ -51,7 +51,7 @@ public:
bool switch_to_previous_scene(uint8_t count = 1);
Scene get_previous_scene();
IrdaAppViewManager* get_view_manager();
IrdaAppSignalReceiver* get_receiver();
IrdaAppSignalTransceiver* get_transceiver();
void set_text_store(uint8_t index, const char* text...);
char* get_text_store(uint8_t index);
uint8_t get_text_store_size();
@ -103,7 +103,7 @@ private:
uint32_t current_button;
NotificationApp* notification;
IrdaAppSignalReceiver receiver;
IrdaAppSignalTransceiver transceiver;
IrdaAppViewManager view_manager;
IrdaAppRemoteManager remote_manager;
@ -113,6 +113,8 @@ private:
std::map<Scene, IrdaAppScene*> scenes = {
{Scene::Start, new IrdaAppSceneStart()},
{Scene::Universal, new IrdaAppSceneUniversal()},
{Scene::UniversalTV, new IrdaAppSceneUniversalTV()},
// {Scene::UniversalAudio, new IrdaAppSceneUniversalAudio()},
{Scene::Learn, new IrdaAppSceneLearn()},
{Scene::LearnSuccess, new IrdaAppSceneLearnSuccess()},
{Scene::LearnEnterName, new IrdaAppSceneLearnEnterName()},

View File

@ -2,7 +2,6 @@
#include "irda.h"
#include "irda/scene/irda-app-scene.hpp"
#include <string>
#include <stdio.h>
static void dialog_result_callback(DialogExResult result, void* context) {
auto app = static_cast<IrdaApp*>(context);

View File

@ -1,5 +1,4 @@
#include "../irda-app.hpp"
#include <cstdio>
void IrdaAppSceneEditRename::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,6 +1,4 @@
#include "../irda-app.hpp"
#include <string>
#include <stdio.h>
#include <gui/modules/popup.h>
void IrdaAppSceneLearnDoneAfter::on_enter(IrdaApp* app) {

View File

@ -1,6 +1,4 @@
#include "../irda-app.hpp"
#include <string>
#include <stdio.h>
void IrdaAppSceneLearnDone::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager();

View File

@ -1,15 +1,12 @@
#include "../irda-app.hpp"
#include "gui/modules/text_input.h"
#include <callback-connector.h>
#include <string>
#include <stdio.h>
void IrdaAppSceneLearnEnterName::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager();
TextInput* text_input = view_manager->get_text_input();
auto receiver = app->get_receiver();
auto message = receiver->get_last_message();
auto transceiver = app->get_transceiver();
auto message = transceiver->get_last_message();
app->set_text_store(
0,
@ -34,14 +31,14 @@ bool IrdaAppSceneLearnEnterName::on_event(IrdaApp* app, IrdaAppEvent* event) {
if(event->type == IrdaAppEvent::Type::TextEditDone) {
auto remote_manager = app->get_remote_manager();
auto receiver = app->get_receiver();
auto transceiver = app->get_transceiver();
bool result = false;
if(app->get_learn_new_remote()) {
result = remote_manager->add_remote_with_button(
app->get_text_store(0), receiver->get_last_message());
app->get_text_store(0), transceiver->get_last_message());
} else {
result =
remote_manager->add_button(app->get_text_store(0), receiver->get_last_message());
result = remote_manager->add_button(
app->get_text_store(0), transceiver->get_last_message());
}
if(!result) {

View File

@ -1,7 +1,5 @@
#include "../irda-app.hpp"
#include "irda.h"
#include <string>
#include <stdio.h>
static void dialog_result_callback(DialogExResult result, void* context) {
auto app = static_cast<IrdaApp*>(context);
@ -19,8 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) {
app->notify_green_on();
auto receiver = app->get_receiver();
auto message = receiver->get_last_message();
auto transceiver = app->get_transceiver();
auto message = transceiver->get_last_message();
app->set_text_store(0, "%s", irda_get_protocol_name(message->protocol));
app->set_text_store(
@ -52,8 +50,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) {
break;
case DialogExResultCenter: {
app->notify_space_blink();
auto receiver = app->get_receiver();
auto message = receiver->get_last_message();
auto transceiver = app->get_transceiver();
auto message = transceiver->get_last_message();
irda_send(message, 1);
break;
}

View File

@ -2,10 +2,10 @@
void IrdaAppSceneLearn::on_enter(IrdaApp* app) {
auto view_manager = app->get_view_manager();
auto receiver = app->get_receiver();
auto transceiver = app->get_transceiver();
auto event_queue = view_manager->get_event_queue();
receiver->capture_once_start(event_queue);
transceiver->capture_once_start(event_queue);
auto popup = view_manager->get_popup();

View File

@ -65,7 +65,7 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) {
app->notify_click_and_blink();
auto remote_manager = app->get_remote_manager();
auto message = remote_manager->get_button_data(event->payload.menu_index);
app->get_receiver()->send_message(message);
app->get_transceiver()->send_message(message);
break;
}
} else if(event->type == IrdaAppEvent::Type::Back) {

View File

@ -0,0 +1,98 @@
#include "../irda-app.hpp"
#include "assets_icons.h"
#include "gui/modules/button_menu.h"
#include "gui/modules/button_panel.h"
#include "../view/irda-app-brut-view.h"
#include "gui/view.h"
#include "irda/irda-app-view-manager.hpp"
#include "irda/scene/irda-app-scene.hpp"
void IrdaAppSceneUniversalCommon::irda_app_item_callback(void* context, uint32_t index) {
IrdaApp* app = static_cast<IrdaApp*>(context);
IrdaAppEvent event;
event.type = IrdaAppEvent::Type::ButtonPanelPressed;
event.payload.menu_index = index;
app->get_view_manager()->send_event(&event);
}
static bool irda_popup_brut_input_callback(InputEvent* event, void* context) {
furi_assert(context);
furi_assert(event);
auto app = static_cast<IrdaApp*>(context);
bool consumed = false;
if((event->type == InputTypeShort) && (event->key == InputKeyBack)) {
consumed = true;
IrdaAppEvent irda_event;
irda_event.type = IrdaAppEvent::Type::ButtonPanelPopupBackPressed;
app->get_view_manager()->send_event(&irda_event);
}
return consumed;
}
void IrdaAppSceneUniversalCommon::remove_popup(IrdaApp* app) {
auto button_panel = app->get_view_manager()->get_button_panel();
button_panel_set_popup_draw_callback(button_panel, NULL, NULL);
button_panel_set_popup_input_callback(button_panel, NULL, NULL);
}
void IrdaAppSceneUniversalCommon::show_popup(IrdaApp* app, int record_amount) {
auto button_panel = app->get_view_manager()->get_button_panel();
auto popup_brut = app->get_view_manager()->get_popup_brut();
popup_brut_set_progress_max(popup_brut, record_amount);
button_panel_set_popup_draw_callback(button_panel, popup_brut_draw_callback, popup_brut);
button_panel_set_popup_input_callback(button_panel, irda_popup_brut_input_callback, app);
}
void IrdaAppSceneUniversalCommon::progress_popup(IrdaApp* app) {
popup_brut_increase_progress(app->get_view_manager()->get_popup_brut());
auto button_panel = app->get_view_manager()->get_button_panel();
with_view_model_cpp(button_panel_get_view(button_panel), void*, model, { return true; });
}
bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) {
bool consumed = false;
if(event->type == IrdaAppEvent::Type::Tick) {
if(brute_force_started) {
if(brute_force.send_next_bruteforce(*app->get_transceiver())) {
progress_popup(app);
} else {
brute_force.stop_bruteforce();
brute_force_started = false;
remove_popup(app);
}
}
consumed = true;
}
if(event->type == IrdaAppEvent::Type::ButtonPanelPopupBackPressed) {
consumed = true;
brute_force_started = false;
brute_force.stop_bruteforce();
remove_popup(app);
} else if(event->type == IrdaAppEvent::Type::ButtonPanelPressed) {
int record_amount = 0;
if(brute_force.start_bruteforce(event->payload.menu_index, record_amount)) {
if(record_amount > 0) {
brute_force_started = true;
show_popup(app, record_amount);
}
} else {
app->switch_to_previous_scene();
}
consumed = true;
}
return consumed;
}
void IrdaAppSceneUniversalCommon::on_exit(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager();
ButtonPanel* button_panel = view_manager->get_button_panel();
button_panel_clean(button_panel);
}

View File

@ -0,0 +1,61 @@
#include "irda/scene/irda-app-scene.hpp"
#include "irda/irda-app.hpp"
void IrdaAppSceneUniversalTV::on_enter(IrdaApp* app) {
IrdaAppViewManager* view_manager = app->get_view_manager();
ButtonPanel* button_panel = view_manager->get_button_panel();
button_panel_reserve(button_panel, 2, 3);
int i = 0;
button_panel_add_item(
button_panel, i, 0, 0, 3, 19, I_Power_25x27, I_Power_hvr_25x27, irda_app_item_callback, app);
brute_force.add_record(i, "POWER");
++i;
button_panel_add_item(
button_panel, i, 1, 0, 36, 19, I_Mute_25x27, I_Mute_hvr_25x27, irda_app_item_callback, app);
brute_force.add_record(i, "MUTE");
++i;
button_panel_add_item(
button_panel,
i,
0,
1,
3,
66,
I_Vol_up_25x27,
I_Vol_up_hvr_25x27,
irda_app_item_callback,
app);
brute_force.add_record(i, "VOL+");
++i;
button_panel_add_item(
button_panel, i, 1, 1, 36, 66, I_Up_25x27, I_Up_hvr_25x27, irda_app_item_callback, app);
brute_force.add_record(i, "CH+");
++i;
button_panel_add_item(
button_panel,
i,
0,
2,
3,
98,
I_Vol_down_25x27,
I_Vol_down_hvr_25x27,
irda_app_item_callback,
app);
brute_force.add_record(i, "VOL-");
++i;
button_panel_add_item(
button_panel, i, 1, 2, 36, 98, I_Down_25x27, I_Down_hvr_25x27, irda_app_item_callback, app);
brute_force.add_record(i, "CH-");
button_panel_add_label(button_panel, 6, 11, FontPrimary, "TV remote");
button_panel_add_label(button_panel, 9, 64, FontSecondary, "Vol");
button_panel_add_label(button_panel, 43, 64, FontSecondary, "Ch");
view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonPanel);
if(!brute_force.calculate_messages()) {
app->switch_to_previous_scene();
}
}

View File

@ -37,7 +37,7 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) {
submenu_item_selected = event->payload.menu_index;
switch(event->payload.menu_index) {
case SubmenuIndexUniversalTV:
// app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
app->switch_to_next_scene(IrdaApp::Scene::UniversalTV);
break;
case SubmenuIndexUniversalAudio:
// app->switch_to_next_scene(IrdaApp::Scene::UniversalAudio);

View File

@ -1,10 +1,11 @@
#pragma once
#include "../irda-app.hpp"
#include "../irda-app-event.hpp"
#include <api-hal-irda.h>
#include "irda.h"
#include <gui/elements.h>
#include <vector>
#include <string>
#include "../irda-app-brute-force.hpp"
class IrdaApp;
@ -137,3 +138,31 @@ public:
void on_exit(IrdaApp* app) final;
};
class IrdaAppSceneUniversalCommon : public IrdaAppScene {
bool brute_force_started = false;
protected:
bool on_event(IrdaApp* app, IrdaAppEvent* event) final;
void on_exit(IrdaApp* app) final;
IrdaAppBruteForce brute_force;
void remove_popup(IrdaApp* app);
void show_popup(IrdaApp* app, int record_amount);
void progress_popup(IrdaApp* app);
static void irda_app_item_callback(void* context, uint32_t index);
IrdaAppSceneUniversalCommon(const char* filename) : brute_force(filename) {}
~IrdaAppSceneUniversalCommon() {}
};
class IrdaAppSceneUniversalTV : public IrdaAppSceneUniversalCommon {
public:
void on_enter(IrdaApp* app) final;
IrdaAppSceneUniversalTV() : IrdaAppSceneUniversalCommon("/irda/universal/tv.ir") {}
~IrdaAppSceneUniversalTV() {}
};
class IrdaAppSceneUniversalAudio : public IrdaAppSceneUniversalCommon {
public:
void on_enter(IrdaApp* app) final;
IrdaAppSceneUniversalAudio() : IrdaAppSceneUniversalCommon("/irda/universal/audio.ir") {}
~IrdaAppSceneUniversalAudio() {}
};

View File

@ -0,0 +1,82 @@
#include "api-hal-resources.h"
#include "assets_icons.h"
#include "gui/canvas.h"
#include "gui/view.h"
#include "input/input.h"
#include <gui/elements.h>
#include <furi.h>
#include "irda-app-brut-view.h"
#include "gui/modules/button_panel.h"
#include <stdint.h>
struct IrdaAppPopupBrut {
uint16_t progress;
uint16_t progress_max;
char percents_string_storage[8];
};
void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut) {
furi_assert(popup_brut);
if(popup_brut->progress < popup_brut->progress_max)
++popup_brut->progress;
else
furi_assert(0);
}
void popup_brut_draw_callback(Canvas* canvas, void* context) {
furi_assert(canvas);
furi_assert(context);
IrdaAppPopupBrut* popup_brut = (IrdaAppPopupBrut*)context;
uint8_t x = 0;
uint8_t width = 64;
uint8_t x_max = x + width - 1;
uint8_t y = 36;
uint8_t height = 59;
uint8_t y_max = y + height - 1;
canvas_invert_color(canvas);
canvas_draw_rbox(canvas, x + 1, y + 1, width - 2, height - 2, 3);
canvas_invert_color(canvas);
canvas_draw_rframe(canvas, x, y, width, height, 3);
canvas_draw_rframe(canvas, x + 1, y + 1, width - 2, height - 2, 3);
canvas_draw_line(canvas, x + 2, y + 1, x + 2, y + 3);
canvas_draw_line(canvas, x + 1, y + 2, x + 3, y + 2);
canvas_draw_line(canvas, x_max - 2, y + 1, x_max - 2, y + 3);
canvas_draw_line(canvas, x_max - 1, y + 2, x_max - 3, y + 2);
canvas_draw_line(canvas, x + 2, y_max - 1, x + 2, y_max - 3);
canvas_draw_line(canvas, x + 1, y_max - 2, x + 3, y_max - 2);
canvas_draw_line(canvas, x_max - 2, y_max - 1, x_max - 2, y_max - 3);
canvas_draw_line(canvas, x_max - 1, y_max - 2, x_max - 3, y_max - 2);
elements_progress_bar(
canvas, x + 4, y + 19, x_max - 8, popup_brut->progress, popup_brut->progress_max);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, x + 15, y + 12, "Sending ...");
canvas_draw_icon_name(canvas, x + 11, y_max - 14, I_Back_15x10);
uint8_t percent_value = 100 * popup_brut->progress / popup_brut->progress_max;
snprintf(
popup_brut->percents_string_storage,
sizeof(popup_brut->percents_string_storage),
"%d%%",
percent_value);
elements_multiline_text_aligned(
canvas, x + 32, y + 40, AlignCenter, AlignBottom, popup_brut->percents_string_storage);
canvas_draw_str(canvas, x + 30, y_max - 5, "= stop");
}
void popup_brut_set_progress_max(IrdaAppPopupBrut* popup_brut, uint16_t progress_max) {
furi_assert(popup_brut);
popup_brut->progress = 0;
popup_brut->progress_max = progress_max;
}
IrdaAppPopupBrut* popup_brut_alloc(void) {
return (IrdaAppPopupBrut*)furi_alloc(sizeof(IrdaAppPopupBrut));
}
void popup_brut_free(IrdaAppPopupBrut* popup_brut) {
free(popup_brut);
}

View File

@ -0,0 +1,18 @@
#pragma once
#include <gui/view.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct IrdaAppPopupBrut IrdaAppPopupBrut;
void popup_brut_increase_progress(IrdaAppPopupBrut* popup_brut);
IrdaAppPopupBrut* popup_brut_alloc();
void popup_brut_free(IrdaAppPopupBrut* popup_brut);
void popup_brut_draw_callback(Canvas* canvas, void* model);
void popup_brut_set_progress_max(IrdaAppPopupBrut* popup_brut, uint16_t progress_max);
#ifdef __cplusplus
}
#endif

File diff suppressed because one or more lines are too long

View File

@ -3,129 +3,143 @@
#include <gui/icon.h>
typedef enum {
I_SDQuestion_35x43,
I_SDError_43x35,
I_Health_16x16,
I_FaceCharging_29x14,
I_BatteryBody_52x28,
I_Voltage_16x16,
I_Temperature_16x16,
I_FaceNopower_29x14,
I_FaceNormal_29x14,
I_Battery_16x16,
I_FaceConfused_29x14,
I_PassportBottom_128x17,
I_DoorLeft_8x56,
I_DoorLocked_10x56,
I_DoorRight_8x56,
I_DoorLeft_70x55,
I_PassportLeft_6x47,
I_DoorRight_70x55,
I_LockPopup_100x49,
I_WalkR2_32x32,
I_WalkL2_32x32,
I_WalkRB1_32x32,
I_Home_painting_17x20,
I_WalkLB2_32x32,
I_Sofa_40x13,
I_WalkLB1_32x32,
I_PC_22x29,
I_WalkL1_32x32,
I_TV_20x20,
I_WalkR1_32x32,
I_WalkRB2_32x32,
I_TV_20x24,
I_dir_10px,
I_Nfc_10px,
I_sub1_10px,
I_ir_10px,
I_ibutt_10px,
I_unknown_10px,
I_ble_10px,
I_125_10px,
I_FX_SittingB_40x27,
I_BigGames_24x24,
I_BigProfile_24x24,
I_DolphinOkay_41x43,
I_DolphinFirstStart5_45x53,
I_DolphinFirstStart4_67x53,
I_DolphinFirstStart2_59x51,
I_DolphinFirstStart0_70x53,
I_DolphinFirstStart6_58x54,
I_DolphinFirstStart1_59x53,
I_DolphinFirstStart8_56x51,
I_DolphinFirstStart7_61x51,
I_Flipper_young_80x60,
I_BigBurger_24x24,
I_FX_Bang_32x6,
I_DolphinFirstStart3_57x48,
I_BadUsb_9x8,
I_PlaceholderR_30x13,
I_Background_128x8,
I_Lock_8x8,
I_Battery_26x8,
I_PlaceholderL_11x13,
I_Battery_19x8,
I_SDcardMounted_11x8,
I_SDcardFail_11x8,
I_USBConnected_15x8,
I_Bluetooth_5x8,
I_Background_128x11,
I_IrdaArrowUp_4x8,
I_Down_hvr_25x27,
I_Vol_down_hvr_25x27,
I_Down_25x27,
I_Fill_marker_7x7,
I_Vol_down_25x27,
I_Vol_up_25x27,
I_Up_hvr_25x27,
I_Vol_up_hvr_25x27,
I_IrdaLearnShort_128x31,
I_IrdaSend_128x64,
I_Mute_hvr_25x27,
I_Back_15x10,
I_Up_25x27,
I_IrdaArrowUp_4x8,
I_Mute_25x27,
I_Power_25x27,
I_IrdaSendShort_128x34,
I_IrdaArrowDown_4x8,
I_IrdaLearn_128x64,
I_IrdaSend_128x64,
I_IrdaSendShort_128x34,
I_passport_happy1_43x45,
I_passport_bad3_43x45,
I_passport_okay2_43x45,
I_passport_bad2_43x45,
I_passport_okay3_43x45,
I_passport_bad1_43x45,
I_passport_happy3_43x45,
I_passport_happy2_43x45,
I_passport_okay1_43x45,
I_ButtonRightSmall_3x5,
I_ButtonLeft_4x7,
I_ButtonLeftSmall_3x5,
I_ButtonRight_4x7,
I_ButtonCenter_7x7,
I_Power_hvr_25x27,
A_Games_14,
A_Plugins_14,
A_Power_14,
A_GPIO_14,
A_Bluetooth_14,
A_Passport_14,
A_Sub1ghz_14,
A_NFC_14,
A_Tamagotchi_14,
A_FileManager_14,
A_125khz_14,
A_U2F_14,
A_Infrared_14,
A_Power_14,
A_Settings_14,
A_125khz_14,
A_iButton_14,
A_Bluetooth_14,
A_GPIO_14,
I_DolphinMafia_115x62,
I_DolphinExcited_64x63,
I_iButtonDolphinSuccess_109x60,
I_iButtonDolphinVerySuccess_108x52,
A_FileManager_14,
A_Tamagotchi_14,
A_NFC_14,
A_Plugins_14,
I_SDQuestion_35x43,
I_SDError_43x35,
I_BatteryBody_52x28,
I_FaceCharging_29x14,
I_Health_16x16,
I_Temperature_16x16,
I_Battery_16x16,
I_FaceConfused_29x14,
I_FaceNormal_29x14,
I_Voltage_16x16,
I_FaceNopower_29x14,
I_iButtonKey_49x44,
I_DolphinNice_96x59,
I_DolphinExcited_64x63,
I_DolphinWait_61x59,
I_iButtonDolphinVerySuccess_108x52,
I_DolphinMafia_115x62,
I_DolphinNice_96x59,
I_iButtonDolphinSuccess_109x60,
I_Background_128x11,
I_Lock_8x8,
I_Battery_26x8,
I_Battery_19x8,
I_USBConnected_15x8,
I_Background_128x8,
I_BadUsb_9x8,
I_PlaceholderL_11x13,
I_SDcardFail_11x8,
I_Bluetooth_5x8,
I_PlaceholderR_30x13,
I_SDcardMounted_11x8,
I_WalkR2_32x32,
I_WalkRB2_32x32,
I_WalkR1_32x32,
I_PC_22x29,
I_WalkRB1_32x32,
I_WalkL2_32x32,
I_WalkLB1_32x32,
I_WalkLB2_32x32,
I_TV_20x20,
I_TV_20x24,
I_Home_painting_17x20,
I_Sofa_40x13,
I_WalkL1_32x32,
I_passport_bad1_43x45,
I_passport_bad3_43x45,
I_passport_happy3_43x45,
I_passport_happy2_43x45,
I_passport_okay3_43x45,
I_passport_okay2_43x45,
I_passport_happy1_43x45,
I_passport_bad2_43x45,
I_passport_okay1_43x45,
A_Wink_128x64,
A_MDWL_32x32,
A_MDWR_32x32,
A_WatchingTV_128x64,
A_MDI_32x32,
A_MDWRB_32x32,
A_MDIB_32x32,
A_FX_Sitting_40x27,
A_MDWR_32x32,
A_MDWL_32x32,
A_MDWRB_32x32,
A_MDWLB_32x32,
I_KeySave_24x11,
I_KeyBackspaceSelected_16x9,
A_MDIB_32x32,
A_WatchingTV_128x64,
I_PassportBottom_128x17,
I_DoorLeft_70x55,
I_DoorLeft_8x56,
I_DoorRight_70x55,
I_DoorRight_8x56,
I_DoorLocked_10x56,
I_PassportLeft_6x47,
I_LockPopup_100x49,
I_sub1_10px,
I_ir_10px,
I_unknown_10px,
I_ibutt_10px,
I_Nfc_10px,
I_ble_10px,
I_125_10px,
I_dir_10px,
I_ButtonCenter_7x7,
I_ButtonLeft_4x7,
I_ButtonLeftSmall_3x5,
I_ButtonRightSmall_3x5,
I_ButtonRight_4x7,
I_DolphinFirstStart2_59x51,
I_BigBurger_24x24,
I_DolphinFirstStart6_58x54,
I_Flipper_young_80x60,
I_FX_Bang_32x6,
I_DolphinFirstStart8_56x51,
I_DolphinFirstStart1_59x53,
I_DolphinOkay_41x43,
I_DolphinFirstStart3_57x48,
I_DolphinFirstStart5_45x53,
I_DolphinFirstStart7_61x51,
I_FX_SittingB_40x27,
I_BigProfile_24x24,
I_DolphinFirstStart0_70x53,
I_BigGames_24x24,
I_DolphinFirstStart4_67x53,
I_KeySaveSelected_24x11,
I_KeyBackspace_16x9,
I_KeyBackspaceSelected_16x9,
I_KeySave_24x11,
} IconName;
Icon * assets_icons_get(IconName name);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -0,0 +1,39 @@
#include "file_reader/file_reader.hpp"
std::string FileReader::getline(File* file) {
std::string str;
size_t newline_index = 0;
bool found_eol = false;
while(1) {
if(file_buf_cnt > 0) {
size_t end_index = 0;
char* endline_ptr = (char*)memchr(file_buf, '\n', file_buf_cnt);
newline_index = endline_ptr - file_buf;
if(endline_ptr == 0) {
end_index = file_buf_cnt;
} else if(newline_index < file_buf_cnt) {
end_index = newline_index + 1;
found_eol = true;
} else {
furi_assert(0);
}
str.append(file_buf, end_index);
memmove(file_buf, &file_buf[end_index], file_buf_cnt - end_index);
file_buf_cnt = file_buf_cnt - end_index;
if(found_eol) break;
}
file_buf_cnt +=
fs_api->file.read(file, &file_buf[file_buf_cnt], sizeof(file_buf) - file_buf_cnt);
if(file_buf_cnt == 0) {
break; // end of reading
}
}
return str;
}

View File

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <memory>
#include "sd-card-api.h"
#include "filesystem-api.h"
class FileReader {
private:
char file_buf[48];
size_t file_buf_cnt = 0;
SdCard_Api* sd_ex_api;
FS_Api* fs_api;
public:
FileReader() {
sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex"));
fs_api = static_cast<FS_Api*>(furi_record_open("sdcard"));
reset();
}
~FileReader() {
furi_record_close("sdcard");
furi_record_close("sdcard-ex");
}
std::string getline(File* file);
void reset(void) {
file_buf_cnt = 0;
}
SdCard_Api& get_sd_api() {
return *sd_ex_api;
}
FS_Api& get_fs_api() {
return *fs_api;
}
};

View File

@ -30,6 +30,7 @@ void irda_encoder_samsung32_encode(uint32_t addr, uint32_t cmd, bool repeat) {
uint8_t command = cmd & 0xFF;
uint8_t command_inverse = (uint8_t) ~command;
irda_encode_space(&encoder_timings, 100);
if (!repeat) {
irda_encode_samsung32_preamble();
irda_encode_byte(&encoder_timings, address);

View File

@ -87,6 +87,10 @@ C_SOURCES += $(wildcard $(LIB_DIR)/drivers/*.c)
CFLAGS += -I$(LIB_DIR)/version
C_SOURCES += $(LIB_DIR)/version/version.c
#file reader
CFLAGS += -I$(LIB_DIR)/file_reader
CPP_SOURCES += $(wildcard $(LIB_DIR)/file_reader/*.cpp)
#irda lib
CFLAGS += -I$(LIB_DIR)/irda
C_SOURCES += $(wildcard $(LIB_DIR)/irda/*.c)