diff options
author | Albert Kharisov <ah@bright-box.com> | 2021-06-09 16:04:49 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-09 16:04:49 +0300 |
commit | 6c74ea65c27d86a98db55882ff86563bb161423e (patch) | |
tree | 792f0fbee2b9c35d7b7b3bc48f73f0eb7cd23ddb /applications/irda | |
parent | 498ffe8d2c7bcc2a107db3d5624fb4f612aac34c (diff) |
[FL-1369, FL-1397, FL-1420] IRDA + SDcard (#513)
* Add saving to SD-Card (not ready yet)
* Add saving to SD-card (done)
* Select previous menu item
* Fix central button
* Fix current_button
* Refactoring
* Add notifications
* [FL-1417] Add IRDA CLI
CLI commands:
1) ir_rx
Receives all IR-trafic, decodes and prints result to stdout
2) ir_tx <protocol> <address> <command>
Transmits IR-signal. Address and command are hex-formatted
* Fix BUG with random memory corruption at random time in random place in random universe in random unknown space and time forever amen
* Fix submenu set_selected_item
* Bring protocol order back
* Add TODO sdcard check
Diffstat (limited to 'applications/irda')
17 files changed, 675 insertions, 121 deletions
diff --git a/applications/irda/cli/irda-cli.cpp b/applications/irda/cli/irda-cli.cpp new file mode 100644 index 00000000..549e2e23 --- /dev/null +++ b/applications/irda/cli/irda-cli.cpp @@ -0,0 +1,104 @@ +#include "app-template.h" +#include "cli/cli.h" +#include "cmsis_os2.h" +#include <furi.h> +#include <api-hal-irda.h> +#include "irda.h" +#include <sstream> +#include <string> +#include <m-string.h> + +typedef struct IrdaCli { + IrdaHandler* handler; + osMessageQueueId_t message_queue; +} IrdaCli; + +static void irda_rx_callback(void* ctx, bool level, uint32_t duration) { + IrdaCli* irda_cli = (IrdaCli*)ctx; + const IrdaMessage* message; + message = irda_decode(irda_cli->handler, level, duration); + if(message) { + osMessageQueuePut(irda_cli->message_queue, message, 0, 0); + } +} + +static void irda_cli_start_ir_rx(Cli* cli, string_t args, void* context) { + if(api_hal_irda_rx_irq_is_busy()) { + printf("IRDA is busy. Exit."); + return; + } + IrdaCli irda_cli; + irda_cli.handler = irda_alloc_decoder(); + irda_cli.message_queue = osMessageQueueNew(2, sizeof(IrdaMessage), NULL); + api_hal_irda_rx_irq_init(); + api_hal_irda_rx_irq_set_callback(irda_rx_callback, &irda_cli); + + printf("Receiving IRDA...\r\nPress Ctrl+C to abort\r\n"); + while(!cli_cmd_interrupt_received(cli)) { + IrdaMessage message; + if(osOK == osMessageQueueGet(irda_cli.message_queue, &message, NULL, 50)) { + printf( + "%s, A:0x%0*lX, C:0x%0*lX%s\r\n", + irda_get_protocol_name(message.protocol), + irda_get_protocol_address_length(message.protocol), + message.address, + irda_get_protocol_command_length(message.protocol), + message.command, + message.repeat ? " R" : ""); + } + } + + api_hal_irda_rx_irq_deinit(); + irda_free_decoder(irda_cli.handler); + osMessageQueueDelete(irda_cli.message_queue); +} + +static void irda_cli_print_usage(void) { + printf("Usage:\r\n\tir_tx <protocol> <command> <address>\r\n"); + printf("\t<command> and <address> are hex-formatted\r\n"); + printf("\tAvailable protocols:"); + for(int i = 0; irda_is_protocol_valid((IrdaProtocol)i); ++i) { + printf(" %s", irda_get_protocol_name((IrdaProtocol)i)); + } + printf("\r\n"); +} + +static void irda_cli_start_ir_tx(Cli* cli, string_t args, void* context) { + if(api_hal_irda_rx_irq_is_busy()) { + printf("IRDA is busy. Exit."); + return; + } + auto ss = std::istringstream(string_get_cstr(args)); + uint32_t command = 0; + uint32_t address = 0; + std::string protocol_name; + + if(!(ss >> protocol_name) || !(ss >> std::hex >> address) || !(ss >> std::hex >> command)) { + printf("Wrong arguments.\r\n"); + irda_cli_print_usage(); + return; + } + + IrdaProtocol protocol = irda_get_protocol_by_name(protocol_name.c_str()); + + if(!irda_is_protocol_valid(protocol)) { + printf("Unknown protocol.\r\n"); + irda_cli_print_usage(); + return; + } + + IrdaMessage message = { + .protocol = protocol, + .address = address, + .command = command, + .repeat = false, + }; + irda_send(&message, 1); +} + +extern "C" void irda_cli_init() { + Cli* cli = (Cli*)furi_record_open("cli"); + cli_add_command(cli, "ir_rx", irda_cli_start_ir_rx, NULL); + cli_add_command(cli, "ir_tx", irda_cli_start_ir_tx, NULL); + furi_record_close("cli"); +} diff --git a/applications/irda/irda-app-remote-manager.cpp b/applications/irda/irda-app-remote-manager.cpp index 7e68b4c9..33d763bc 100644 --- a/applications/irda/irda-app-remote-manager.cpp +++ b/applications/irda/irda-app-remote-manager.cpp @@ -1,51 +1,78 @@ #include "irda-app-remote-manager.hpp" +#include "filesystem-api.h" #include "furi.h" +#include "furi/check.h" +#include "gui/modules/button_menu.h" +#include "irda.h" +#include "sys/_stdint.h" +#include <cstdio> #include <string> #include <utility> +const char* IrdaAppRemoteManager::irda_directory = "irda"; +const char* IrdaAppRemoteManager::irda_extension = ".ir"; +static const std::string default_remote_name = "remote"; + +static bool find_string(const std::vector<std::string>& strings, const std::string& match_string) { + for(const auto& string : strings) { + if(!string.compare(match_string)) return true; + } + return false; +} + +static std::string +find_vacant_name(const std::vector<std::string>& strings, const std::string& name) { + // if suggested name is occupied, try another one (name2, name3, etc) + if(find_string(strings, name)) { + int i = 1; + while(find_string(strings, name + std::to_string(++i))) + ; + return name + std::to_string(i); + } else { + return name; + } +} + IrdaAppRemoteManager::IrdaAppRemoteManager() { - // Read from api-hal-storage, and fill remotes + sd_ex_api = static_cast<SdCard_Api*>(furi_record_open("sdcard-ex")); + fs_api = static_cast<FS_Api*>(furi_record_open("sdcard")); } -static const std::string default_remote_name = "remote"; +IrdaAppRemoteManager::~IrdaAppRemoteManager() { + furi_record_close("sdcard"); + furi_record_close("sdcard-ex"); +} -void IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { - remotes[current_remote_index].buttons.emplace_back(button_name, message); +bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaMessage* message) { + remote->buttons.emplace_back(button_name, message); + return store(); } -void IrdaAppRemoteManager::add_remote_with_button( +bool IrdaAppRemoteManager::add_remote_with_button( const char* button_name, const IrdaMessage* message) { - bool found = true; - int i = 0; + furi_check(button_name != nullptr); + furi_check(message != nullptr); - // find first free common name for remote - do { - found = false; - ++i; - for(const auto& it : remotes) { - if(it.name == (default_remote_name + std::to_string(i))) { - found = true; - break; - } - } - } while(found); + std::vector<std::string> remote_list; + bool result = get_remote_list(remote_list); + if(!result) return false; - remotes.emplace_back(default_remote_name + std::to_string(i)); - current_remote_index = remotes.size() - 1; - add_button(button_name, message); + auto new_name = find_vacant_name(remote_list, default_remote_name); + + remote = std::make_unique<IrdaAppRemote>(new_name); + return add_button(button_name, message); } -IrdaAppRemote::IrdaAppRemote(std::string name) +IrdaAppRemote::IrdaAppRemote(const std::string& name) : name(name) { } std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { std::vector<std::string> name_vector; - auto remote = remotes[current_remote_index]; - name_vector.reserve(remote.buttons.size()); + name_vector.reserve(remote->buttons.size()); - for(const auto& it : remote.buttons) { + for(const auto& it : remote->buttons) { name_vector.emplace_back(it.name); } @@ -53,78 +80,289 @@ std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const { return name_vector; } -std::vector<std::string> IrdaAppRemoteManager::get_remote_list() const { - std::vector<std::string> name_vector; - name_vector.reserve(remotes.size()); +const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t index) const { + furi_check(remote.get() != nullptr); + auto& buttons = remote->buttons; + furi_check(index < buttons.size()); + + return &buttons.at(index).message; +} + +std::string IrdaAppRemoteManager::make_filename(const std::string& name) const { + return std::string("/") + irda_directory + "/" + name + irda_extension; +} + +bool IrdaAppRemoteManager::delete_remote() { + FS_Error fs_res; - for(const auto& it : remotes) { - name_vector.push_back(it.name); + fs_res = fs_api->common.remove(make_filename(remote->name).c_str()); + if(fs_res != FSE_OK) { + show_file_error_message("Error deleting file"); + return false; } + remote.reset(); + return true; +} - // copy elision - return name_vector; +bool IrdaAppRemoteManager::delete_button(uint32_t index) { + furi_check(remote.get() != nullptr); + auto& buttons = remote->buttons; + furi_check(index < buttons.size()); + + buttons.erase(buttons.begin() + index); + return store(); } -size_t IrdaAppRemoteManager::get_current_remote(void) const { - return current_remote_index; +std::string IrdaAppRemoteManager::get_button_name(uint32_t index) { + furi_check(remote.get() != nullptr); + auto& buttons = remote->buttons; + furi_check(index < buttons.size()); + return buttons[index].name; } -size_t IrdaAppRemoteManager::get_current_button(void) const { - return current_button_index; +std::string IrdaAppRemoteManager::get_remote_name() { + furi_check(remote.get() != nullptr); + return remote->name; } -void IrdaAppRemote::add_button( - size_t remote_index, - const char* button_name, - const IrdaMessage* message) { - buttons.emplace_back(button_name, message); +int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) { + int i = 0; + for(const auto& str : strings) { + if(!str.compare(remote->name)) { + return i; + } + ++i; + } + return -1; } -const IrdaMessage* IrdaAppRemoteManager::get_button_data(size_t button_index) const { - furi_check(remotes[current_remote_index].buttons.size() > button_index); - auto& b = remotes[current_remote_index].buttons.at(button_index); - return &b.message; +bool IrdaAppRemoteManager::rename_remote(const char* str) { + furi_check(str != nullptr); + furi_check(remote.get() != nullptr); + + if(!remote->name.compare(str)) return true; + + std::vector<std::string> remote_list; + bool result = get_remote_list(remote_list); + if(!result) return false; + + auto new_name = find_vacant_name(remote_list, str); + FS_Error fs_err = 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"); + } + return fs_err == FSE_OK; } -void IrdaAppRemoteManager::set_current_remote(size_t index) { - furi_check(index < remotes.size()); - current_remote_index = index; +bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) { + furi_check(remote.get() != nullptr); + auto& buttons = remote->buttons; + furi_check(index < buttons.size()); + + buttons[index].name = str; + return store(); } -void IrdaAppRemoteManager::set_current_button(size_t index) { - furi_check(current_remote_index < remotes.size()); - furi_check(index < remotes[current_remote_index].buttons.size()); - current_button_index = index; +size_t IrdaAppRemoteManager::get_number_of_buttons() { + furi_check(remote.get() != nullptr); + return remote->buttons.size(); } -void IrdaAppRemoteManager::delete_current_remote() { - remotes.erase(remotes.begin() + current_remote_index); - current_remote_index = 0; +void IrdaAppRemoteManager::show_file_error_message(const char* error_text) const { + sd_ex_api->show_error(sd_ex_api->context, error_text); } -void IrdaAppRemoteManager::delete_current_button() { - auto& buttons = remotes[current_remote_index].buttons; - buttons.erase(buttons.begin() + current_button_index); - current_button_index = 0; +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()); + if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) { + show_file_error_message("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); + + if(!res) { + show_file_error_message("Cannot create\nnew remote file"); + return false; + } + + char content[128]; + + for(const auto& button : remote->buttons) { + auto protocol = button.message.protocol; + + sniprintf( + content, + sizeof(content), + "%.31s %.31s A:%0*lX C:%0*lX\n", + button.name.c_str(), + irda_get_protocol_name(protocol), + irda_get_protocol_address_length(protocol), + button.message.address, + irda_get_protocol_command_length(protocol), + button.message.command); + + auto content_len = strlen(content); + write_count = 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); + return false; + } + } + + fs_api->file.close(&file); + sd_ex_api->check_error(sd_ex_api->context); + + return true; } -std::string IrdaAppRemoteManager::get_current_button_name() { - auto buttons = remotes[current_remote_index].buttons; - return buttons[current_button_index].name; +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 IrdaAppRemoteManager::get_current_remote_name() { - return remotes[current_remote_index].name; +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; } -void IrdaAppRemoteManager::rename_remote(const char* str) { - remotes[current_remote_index].name = str; +bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const { + bool fs_res = false; + char name[128]; + File dir; + std::string dirname(std::string("/") + irda_directory); + remote_names.clear(); + + fs_res = fs_api->dir.open(&dir, dirname.c_str()); + if(!fs_res) { + if(!check_fs()) { + show_file_error_message("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)) { + std::string filename(name); + auto extension_index = filename.rfind(irda_extension); + if((extension_index == std::string::npos) || + (extension_index + strlen(irda_extension) != filename.size())) { + continue; + } + remote_names.push_back(filename.erase(extension_index)); + } + fs_api->dir.close(&dir); + + return true; } -void IrdaAppRemoteManager::rename_button(const char* str) { - remotes[current_remote_index].buttons[current_button_index].name = str; +bool IrdaAppRemoteManager::load(const std::string& name) { + bool fs_res = false; + File file; + + fs_res = 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"); + 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); + } + fs_api->file.close(&file); + + return true; } -size_t IrdaAppRemoteManager::get_current_remote_buttons_number() { - return remotes[current_remote_index].buttons.size(); +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"); + return fs_err == FSE_OK; } diff --git a/applications/irda/irda-app-remote-manager.hpp b/applications/irda/irda-app-remote-manager.hpp index 1930ba5b..dd0ae9bc 100644 --- a/applications/irda/irda-app-remote-manager.hpp +++ b/applications/irda/irda-app-remote-manager.hpp @@ -1,9 +1,14 @@ #pragma once +#include "sys/_stdint.h" +#include <algorithm> #include <stdint.h> #include <string> #include <list> #include <vector> +#include <memory> #include <irda.h> +#include <sd-card-api.h> +#include <filesystem-api.h> class IrdaAppRemoteButton { friend class IrdaAppRemoteManager; @@ -19,35 +24,50 @@ class IrdaAppRemote { friend class IrdaAppRemoteManager; std::vector<IrdaAppRemoteButton> buttons; std::string name; - bool add(const IrdaMessage*); - void add_button(size_t remote_index, const char* button_name, const IrdaMessage* message); public: - IrdaAppRemote(std::string name); + IrdaAppRemote(const std::string& name); + IrdaAppRemote& operator=(std::string& new_name) noexcept + { + name = new_name; + buttons.clear(); + return *this; + } }; class IrdaAppRemoteManager { - size_t current_remote_index; - size_t current_button_index; - std::vector<IrdaAppRemote> remotes; + 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: - std::vector<std::string> get_remote_list() const; - std::vector<std::string> get_button_list() const; - void add_remote_with_button(const char* button_name, const IrdaMessage* message); - void add_button(const char* button_name, const IrdaMessage* message); + bool add_remote_with_button(const char* button_name, const IrdaMessage* message); + bool add_button(const char* button_name, const IrdaMessage* message); + + int find_remote_name(const std::vector<std::string>& strings); + bool rename_button(uint32_t index, const char* str); + bool rename_remote(const char* str); - size_t get_current_remote(void) const; - size_t get_current_button(void) const; + bool get_remote_list(std::vector<std::string>& remote_names) const; + std::vector<std::string> get_button_list() const; + std::string get_button_name(uint32_t index); + std::string get_remote_name(); + size_t get_number_of_buttons(); const IrdaMessage* get_button_data(size_t button_index) const; - void set_current_remote(size_t index); - void set_current_button(size_t index); - void rename_button(const char* str); - void rename_remote(const char* str); - std::string get_current_button_name(); - std::string get_current_remote_name(); - size_t get_current_remote_buttons_number(); - void delete_current_button(); - void delete_current_remote(); + bool delete_button(uint32_t index); + bool delete_remote(); IrdaAppRemoteManager(); - ~IrdaAppRemoteManager() {}; + ~IrdaAppRemoteManager(); + + bool store(); + bool load(const std::string& name); + bool check_fs() const; }; diff --git a/applications/irda/irda-app.cpp b/applications/irda/irda-app.cpp index a4d69403..95b9a137 100644 --- a/applications/irda/irda-app.cpp +++ b/applications/irda/irda-app.cpp @@ -1,4 +1,5 @@ #include "irda-app.hpp" +#include "sys/_stdint.h" #include <furi.h> #include <gui/gui.h> #include <input/input.h> @@ -154,3 +155,70 @@ void IrdaApp::set_edit_action(IrdaApp::EditAction value) { IrdaApp::EditAction IrdaApp::get_edit_action(void) { return action; } + +void IrdaApp::set_current_button(int value) { + current_button = value; +} + +int IrdaApp::get_current_button() { + return current_button; +} + +void IrdaApp::notify_success() { + notification_message(notification, &sequence_success); +} + +void IrdaApp::notify_red_blink() { + notification_message(notification, &sequence_blink_red_10); +} + +void IrdaApp::notify_space_blink() { + static const NotificationSequence sequence = { + &message_green_0, + &message_delay_50, + &message_green_255, + &message_do_not_reset, + NULL, + }; + + notification_message_block(notification, &sequence); +} + +void IrdaApp::notify_click() { + static const NotificationSequence sequence = { + &message_click, + &message_delay_1, + &message_sound_off, + NULL, + }; + + notification_message_block(notification, &sequence); +} + +void IrdaApp::notify_click_and_blink() { + static const NotificationSequence sequence = { + &message_click, + &message_delay_1, + &message_sound_off, + &message_red_0, + &message_green_255, + &message_blue_0, + &message_delay_10, + &message_green_0, + NULL, + }; + + notification_message_block(notification, &sequence); +} + +void IrdaApp::notify_double_vibro() { + notification_message(notification, &sequence_double_vibro); +} + +void IrdaApp::notify_green_on() { + notification_message(notification, &sequence_set_only_green_255); +} + +void IrdaApp::notify_green_off() { + notification_message(notification, &sequence_reset_green); +} diff --git a/applications/irda/irda-app.hpp b/applications/irda/irda-app.hpp index c1e8ce62..16ba3f49 100644 --- a/applications/irda/irda-app.hpp +++ b/applications/irda/irda-app.hpp @@ -1,4 +1,5 @@ #pragma once +#include "sys/_stdint.h" #include <map> #include <irda.h> #include <furi.h> @@ -9,6 +10,7 @@ #include "irda-app-receiver.hpp" #include <forward_list> #include <stdint.h> +#include <notification/notification-messages.h> class IrdaApp { @@ -65,11 +67,29 @@ public: bool get_learn_new_remote(); void set_learn_new_remote(bool value); + enum : int { + ButtonNA = -1, + }; + int get_current_button(); + void set_current_button(int value); + + void notify_success(); + void notify_red_blink(); + void notify_space_blink(); + void notify_double_vibro(); + void notify_green_on(); + void notify_green_off(); + void notify_click(); + void notify_click_and_blink(); + static void text_input_callback(void* context, char* text); static void popup_callback(void* context); - IrdaApp() {} + IrdaApp() { + notification = static_cast<NotificationApp*>(furi_record_open("notification")); + } ~IrdaApp() { + furi_record_close("notification"); for (auto &it : scenes) delete it.second; } @@ -80,7 +100,9 @@ private: bool learn_new_remote; EditElement element; EditAction action; + uint32_t current_button; + NotificationApp* notification; IrdaAppSignalReceiver receiver; IrdaAppViewManager view_manager; IrdaAppRemoteManager remote_manager; diff --git a/applications/irda/scene/irda-app-scene-edit-delete.cpp b/applications/irda/scene/irda-app-scene-edit-delete.cpp index 929dd2d4..a2d8934f 100644 --- a/applications/irda/scene/irda-app-scene-edit-delete.cpp +++ b/applications/irda/scene/irda-app-scene-edit-delete.cpp @@ -1,5 +1,6 @@ #include "../irda-app.hpp" #include "irda.h" +#include "irda/scene/irda-app-scene.hpp" #include <string> #include <stdio.h> @@ -20,12 +21,12 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { auto remote_manager = app->get_remote_manager(); if(app->get_edit_element() == IrdaApp::EditElement::Button) { - auto message = remote_manager->get_button_data(remote_manager->get_current_button()); + auto message = remote_manager->get_button_data(app->get_current_button()); dialog_ex_set_header(dialog_ex, "Delete button?", 64, 6, AlignCenter, AlignCenter); app->set_text_store( 0, "%s\n%s\nA=0x%0*lX C=0x%0*lX", - remote_manager->get_current_button_name().c_str(), + remote_manager->get_button_name(app->get_current_button()).c_str(), irda_get_protocol_name(message->protocol), irda_get_protocol_address_length(message->protocol), message->address, @@ -36,8 +37,8 @@ void IrdaAppSceneEditDelete::on_enter(IrdaApp* app) { app->set_text_store( 0, "%s\n with %lu buttons", - remote_manager->get_current_remote_name().c_str(), - remote_manager->get_current_remote_buttons_number()); + remote_manager->get_remote_name().c_str(), + remote_manager->get_number_of_buttons()); } dialog_ex_set_text(dialog_ex, app->get_text_store(0), 64, 32, AlignCenter, AlignCenter); @@ -63,13 +64,20 @@ bool IrdaAppSceneEditDelete::on_event(IrdaApp* app, IrdaAppEvent* event) { break; case DialogExResultRight: auto remote_manager = app->get_remote_manager(); + bool result = false; if(app->get_edit_element() == IrdaApp::EditElement::Remote) { - remote_manager->delete_current_remote(); + result = remote_manager->delete_remote(); } else { - remote_manager->delete_current_button(); + result = remote_manager->delete_button(app->get_current_button()); + app->set_current_button(IrdaApp::ButtonNA); } - app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); + if(!result) { + app->search_and_switch_to_previous_scene( + {IrdaApp::Scene::RemoteList, IrdaApp::Scene::Start}); + } else { + app->switch_to_next_scene(IrdaApp::Scene::EditDeleteDone); + } break; } } diff --git a/applications/irda/scene/irda-app-scene-edit-key-select.cpp b/applications/irda/scene/irda-app-scene-edit-key-select.cpp index e0ead609..41956b1d 100644 --- a/applications/irda/scene/irda-app-scene-edit-key-select.cpp +++ b/applications/irda/scene/irda-app-scene-edit-key-select.cpp @@ -14,7 +14,7 @@ static void submenu_callback(void* context, uint32_t index) { void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); Submenu* submenu = view_manager->get_submenu(); - int i = 0; + int item_number = 0; const char* header = app->get_edit_action() == IrdaApp::EditAction::Rename ? "Rename key:" : "Delete key:"; @@ -23,7 +23,11 @@ void IrdaAppSceneEditKeySelect::on_enter(IrdaApp* app) { auto remote_manager = app->get_remote_manager(); buttons_names = remote_manager->get_button_list(); for(const auto& it : buttons_names) { - submenu_add_item(submenu, it.c_str(), i++, submenu_callback, app); + submenu_add_item(submenu, it.c_str(), item_number++, submenu_callback, app); + } + if((item_number > 0) && (app->get_current_button() != IrdaApp::ButtonNA)) { + submenu_set_selected_item(submenu, app->get_current_button()); + app->set_current_button(IrdaApp::ButtonNA); } view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); @@ -33,8 +37,7 @@ bool IrdaAppSceneEditKeySelect::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; if(event->type == IrdaAppEvent::Type::MenuSelected) { - auto remote_manager = app->get_remote_manager(); - remote_manager->set_current_button(event->payload.menu_index); + app->set_current_button(event->payload.menu_index); consumed = true; if(app->get_edit_action() == IrdaApp::EditAction::Rename) { app->switch_to_next_scene(IrdaApp::Scene::EditRename); diff --git a/applications/irda/scene/irda-app-scene-edit-rename.cpp b/applications/irda/scene/irda-app-scene-edit-rename.cpp index 0ade41ca..0ed3700e 100644 --- a/applications/irda/scene/irda-app-scene-edit-rename.cpp +++ b/applications/irda/scene/irda-app-scene-edit-rename.cpp @@ -7,10 +7,11 @@ void IrdaAppSceneEditRename::on_enter(IrdaApp* app) { auto remote_manager = app->get_remote_manager(); if(app->get_edit_element() == IrdaApp::EditElement::Button) { - auto button_name = remote_manager->get_current_button_name(); + furi_assert(app->get_current_button() != IrdaApp::ButtonNA); + auto button_name = remote_manager->get_button_name(app->get_current_button()); strncpy(app->get_text_store(0), button_name.c_str(), app->get_text_store_size()); } else { - auto remote_name = remote_manager->get_current_remote_name(); + auto remote_name = remote_manager->get_remote_name(); strncpy(app->get_text_store(0), remote_name.c_str(), app->get_text_store_size()); } @@ -30,12 +31,20 @@ bool IrdaAppSceneEditRename::on_event(IrdaApp* app, IrdaAppEvent* event) { if(event->type == IrdaAppEvent::Type::TextEditDone) { auto remote_manager = app->get_remote_manager(); + bool result = false; if(app->get_edit_element() == IrdaApp::EditElement::Button) { - remote_manager->rename_button(app->get_text_store(0)); + result = + remote_manager->rename_button(app->get_current_button(), app->get_text_store(0)); + app->set_current_button(IrdaApp::ButtonNA); } else { - remote_manager->rename_remote(app->get_text_store(0)); + result = remote_manager->rename_remote(app->get_text_store(0)); + } + if(!result) { + app->search_and_switch_to_previous_scene( + {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); + } else { + app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); } - app->switch_to_next_scene_without_saving(IrdaApp::Scene::EditRenameDone); consumed = true; } diff --git a/applications/irda/scene/irda-app-scene-edit.cpp b/applications/irda/scene/irda-app-scene-edit.cpp index cd8a84fc..0ecb8d9e 100644 --- a/applications/irda/scene/irda-app-scene-edit.cpp +++ b/applications/irda/scene/irda-app-scene-edit.cpp @@ -1,4 +1,5 @@ #include "../irda-app.hpp" +#include "gui/modules/submenu.h" typedef enum { SubmenuIndexAddKey, @@ -27,6 +28,8 @@ void IrdaAppSceneEdit::on_enter(IrdaApp* app) { submenu_add_item(submenu, "Delete key", SubmenuIndexDeleteKey, submenu_callback, app); submenu_add_item(submenu, "Rename remote", SubmenuIndexRenameRemote, submenu_callback, app); submenu_add_item(submenu, "Delete remote", SubmenuIndexDeleteRemote, submenu_callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); + submenu_item_selected = 0; view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); } @@ -35,8 +38,10 @@ bool IrdaAppSceneEdit::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; if(event->type == IrdaAppEvent::Type::MenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexAddKey: + app->set_learn_new_remote(false); app->switch_to_next_scene(IrdaApp::Scene::Learn); break; case SubmenuIndexRenameKey: diff --git a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp index 49b581d1..590902ff 100644 --- a/applications/irda/scene/irda-app-scene-learn-enter-name.cpp +++ b/applications/irda/scene/irda-app-scene-learn-enter-name.cpp @@ -35,14 +35,21 @@ 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(); + bool result = false; if(app->get_learn_new_remote()) { - remote_manager->add_remote_with_button( + result = remote_manager->add_remote_with_button( app->get_text_store(0), receiver->get_last_message()); } else { - remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); + result = + remote_manager->add_button(app->get_text_store(0), receiver->get_last_message()); } - app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); + if(!result) { + app->search_and_switch_to_previous_scene( + {IrdaApp::Scene::Start, IrdaApp::Scene::RemoteList}); + } else { + app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnDone); + } } return consumed; } diff --git a/applications/irda/scene/irda-app-scene-learn-success.cpp b/applications/irda/scene/irda-app-scene-learn-success.cpp index 12a7f9ad..b4037d78 100644 --- a/applications/irda/scene/irda-app-scene-learn-success.cpp +++ b/applications/irda/scene/irda-app-scene-learn-success.cpp @@ -17,6 +17,8 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { IrdaAppViewManager* view_manager = app->get_view_manager(); DialogEx* dialog_ex = view_manager->get_dialog_ex(); + app->notify_green_on(); + auto receiver = app->get_receiver(); auto message = receiver->get_last_message(); @@ -32,6 +34,7 @@ void IrdaAppSceneLearnSuccess::on_enter(IrdaApp* app) { dialog_ex_set_text(dialog_ex, app->get_text_store(1), 75, 23, AlignLeft, AlignTop); dialog_ex_set_left_button_text(dialog_ex, "Retry"); dialog_ex_set_right_button_text(dialog_ex, "Save"); + dialog_ex_set_center_button_text(dialog_ex, "Send"); dialog_ex_set_icon(dialog_ex, 0, 1, I_DolphinExcited_64x63); dialog_ex_set_result_callback(dialog_ex, dialog_result_callback); dialog_ex_set_context(dialog_ex, app); @@ -47,11 +50,20 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { case DialogExResultLeft: app->switch_to_next_scene_without_saving(IrdaApp::Scene::Learn); break; - case DialogExResultCenter: - furi_assert(0); + case DialogExResultCenter: { + app->notify_space_blink(); + auto receiver = app->get_receiver(); + auto message = receiver->get_last_message(); + irda_send(message, 1); break; + } case DialogExResultRight: - app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); + auto remote_manager = app->get_remote_manager(); + if(remote_manager->check_fs()) { + app->switch_to_next_scene(IrdaApp::Scene::LearnEnterName); + } else { + app->switch_to_previous_scene(); + } break; } } @@ -60,4 +72,8 @@ bool IrdaAppSceneLearnSuccess::on_event(IrdaApp* app, IrdaAppEvent* event) { } void IrdaAppSceneLearnSuccess::on_exit(IrdaApp* app) { + IrdaAppViewManager* view_manager = app->get_view_manager(); + DialogEx* dialog_ex = view_manager->get_dialog_ex(); + dialog_ex_set_center_button_text(dialog_ex, nullptr); + app->notify_green_off(); } diff --git a/applications/irda/scene/irda-app-scene-learn.cpp b/applications/irda/scene/irda-app-scene-learn.cpp index 993575bb..c551706a 100644 --- a/applications/irda/scene/irda-app-scene-learn.cpp +++ b/applications/irda/scene/irda-app-scene-learn.cpp @@ -14,13 +14,22 @@ void IrdaAppSceneLearn::on_enter(IrdaApp* app) { popup, "Point the remote at IR port\nand push the button", 5, 10, AlignLeft, AlignCenter); popup_set_callback(popup, NULL); + if(app->get_learn_new_remote()) { + app->notify_double_vibro(); + } + view_manager->switch_to(IrdaAppViewManager::ViewType::Popup); } bool IrdaAppSceneLearn::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; + if(event->type == IrdaAppEvent::Type::Tick) { + consumed = true; + app->notify_red_blink(); + } if(event->type == IrdaAppEvent::Type::IrdaMessageReceived) { + app->notify_success(); app->switch_to_next_scene_without_saving(IrdaApp::Scene::LearnSuccess); } diff --git a/applications/irda/scene/irda-app-scene-remote-list.cpp b/applications/irda/scene/irda-app-scene-remote-list.cpp index 51afcb48..aacc537f 100644 --- a/applications/irda/scene/irda-app-scene-remote-list.cpp +++ b/applications/irda/scene/irda-app-scene-remote-list.cpp @@ -20,13 +20,26 @@ void IrdaAppSceneRemoteList::on_enter(IrdaApp* app) { auto remote_manager = app->get_remote_manager(); int i = 0; - remote_names = remote_manager->get_remote_list(); - for(auto& a : remote_names) { - submenu_add_item(submenu, a.c_str(), i++, submenu_callback, app); + bool result = remote_manager->get_remote_list(remote_names); + if(!result) { + app->switch_to_previous_scene(); + return; + } + + for(auto& name : remote_names) { + submenu_add_item(submenu, name.c_str(), i++, submenu_callback, app); } submenu_add_item( submenu, " +", SubmenuIndexPlus, submenu_callback, app); + if((SubmenuIndex)submenu_item_selected == SubmenuIndexPlus) { + submenu_set_selected_item(submenu, submenu_item_selected); + } else { + int remote_index = remote_manager->find_remote_name(remote_names); + submenu_set_selected_item(submenu, (remote_index >= 0) ? remote_index : 0); + } + + submenu_item_selected = 0; view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); } @@ -38,11 +51,14 @@ bool IrdaAppSceneRemoteList::on_event(IrdaApp* app, IrdaAppEvent* event) { case SubmenuIndexPlus: app->set_learn_new_remote(true); app->switch_to_next_scene(IrdaApp::Scene::Learn); + submenu_item_selected = event->payload.menu_index; break; default: auto remote_manager = app->get_remote_manager(); - remote_manager->set_current_remote(event->payload.menu_index); - app->switch_to_next_scene(IrdaApp::Scene::Remote); + bool result = remote_manager->load(remote_names.at(event->payload.menu_index)); + if(result) { + app->switch_to_next_scene(IrdaApp::Scene::Remote); + } consumed = true; break; } diff --git a/applications/irda/scene/irda-app-scene-remote.cpp b/applications/irda/scene/irda-app-scene-remote.cpp index ba65ead0..ac33fcad 100644 --- a/applications/irda/scene/irda-app-scene-remote.cpp +++ b/applications/irda/scene/irda-app-scene-remote.cpp @@ -4,6 +4,7 @@ typedef enum { ButtonIndexPlus = -2, ButtonIndexEdit = -1, + ButtonIndexNA = 0, } ButtonIndex; static void button_menu_callback(void* context, int32_t index) { @@ -35,8 +36,12 @@ void IrdaAppSceneRemote::on_enter(IrdaApp* app) { button_menu_add_item( button_menu, "Edit", ButtonIndexEdit, button_menu_callback, ButtonMenuItemTypeControl, app); - app->set_text_store(0, "%s", remote_manager->get_current_remote_name().c_str()); + app->set_text_store(0, "%s", remote_manager->get_remote_name().c_str()); button_menu_set_header(button_menu, app->get_text_store(0)); + if(buttonmenu_item_selected != ButtonIndexNA) { + button_menu_set_selected_item(button_menu, buttonmenu_item_selected); + buttonmenu_item_selected = ButtonIndexNA; + } view_manager->switch_to(IrdaAppViewManager::ViewType::ButtonMenu); } @@ -46,12 +51,18 @@ bool IrdaAppSceneRemote::on_event(IrdaApp* app, IrdaAppEvent* event) { if(event->type == IrdaAppEvent::Type::MenuSelected) { switch(event->payload.menu_index) { case ButtonIndexPlus: + app->notify_click(); + buttonmenu_item_selected = event->payload.menu_index; + app->set_learn_new_remote(false); app->switch_to_next_scene(IrdaApp::Scene::Learn); break; case ButtonIndexEdit: + app->notify_click(); + buttonmenu_item_selected = event->payload.menu_index; app->switch_to_next_scene(IrdaApp::Scene::Edit); break; default: + 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); diff --git a/applications/irda/scene/irda-app-scene-start.cpp b/applications/irda/scene/irda-app-scene-start.cpp index 7efb6ccb..27f40ad3 100644 --- a/applications/irda/scene/irda-app-scene-start.cpp +++ b/applications/irda/scene/irda-app-scene-start.cpp @@ -25,6 +25,8 @@ void IrdaAppSceneStart::on_enter(IrdaApp* app) { submenu_add_item( submenu, "Learn new remote", SubmenuIndexLearnNewRemote, submenu_callback, app); submenu_add_item(submenu, "Saved remotes", SubmenuIndexSavedRemotes, submenu_callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); + submenu_item_selected = 0; view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); } @@ -33,6 +35,7 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; if(event->type == IrdaAppEvent::Type::MenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexUniversalLibrary: app->switch_to_next_scene(IrdaApp::Scene::Universal); @@ -44,6 +47,9 @@ bool IrdaAppSceneStart::on_event(IrdaApp* app, IrdaAppEvent* event) { case SubmenuIndexSavedRemotes: app->switch_to_next_scene(IrdaApp::Scene::RemoteList); break; + default: + furi_assert(0); + break; } consumed = true; } diff --git a/applications/irda/scene/irda-app-scene-universal.cpp b/applications/irda/scene/irda-app-scene-universal.cpp index 2ab4ae2e..949f995b 100644 --- a/applications/irda/scene/irda-app-scene-universal.cpp +++ b/applications/irda/scene/irda-app-scene-universal.cpp @@ -24,6 +24,8 @@ void IrdaAppSceneUniversal::on_enter(IrdaApp* app) { submenu_add_item(submenu, "Audio Players", SubmenuIndexUniversalAudio, submenu_callback, app); submenu_add_item( submenu, "Air Conditioners", SubmenuIndexUniversalAirConditioner, submenu_callback, app); + submenu_set_selected_item(submenu, submenu_item_selected); + submenu_item_selected = 0; view_manager->switch_to(IrdaAppViewManager::ViewType::Submenu); } @@ -32,6 +34,7 @@ bool IrdaAppSceneUniversal::on_event(IrdaApp* app, IrdaAppEvent* event) { bool consumed = false; if(event->type == IrdaAppEvent::Type::MenuSelected) { + submenu_item_selected = event->payload.menu_index; switch(event->payload.menu_index) { case SubmenuIndexUniversalTV: // app->switch_to_next_scene(IrdaApp::Scene::UniversalTV); diff --git a/applications/irda/scene/irda-app-scene.hpp b/applications/irda/scene/irda-app-scene.hpp index 832e4bc0..b21d435a 100644 --- a/applications/irda/scene/irda-app-scene.hpp +++ b/applications/irda/scene/irda-app-scene.hpp @@ -23,6 +23,8 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; +private: + uint32_t submenu_item_selected = 0; }; class IrdaAppSceneUniversal : public IrdaAppScene { @@ -30,6 +32,8 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; +private: + uint32_t submenu_item_selected = 0; }; class IrdaAppSceneLearn : public IrdaAppScene { @@ -74,6 +78,7 @@ public: void on_exit(IrdaApp* app) final; private: std::vector<std::string> buttons_names; + uint32_t buttonmenu_item_selected = 0; }; class IrdaAppSceneRemoteList : public IrdaAppScene { @@ -81,6 +86,8 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; +private: + uint32_t submenu_item_selected = 0; std::vector<std::string> remote_names; }; @@ -89,6 +96,8 @@ public: void on_enter(IrdaApp* app) final; bool on_event(IrdaApp* app, IrdaAppEvent* event) final; void on_exit(IrdaApp* app) final; +private: + uint32_t submenu_item_selected = 0; }; class IrdaAppSceneEditKeySelect : public IrdaAppScene { |