irda-app-remote-manager.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. #include "irda-app-remote-manager.hpp"
  2. #include "filesystem-api.h"
  3. #include "furi.h"
  4. #include "furi/check.h"
  5. #include "gui/modules/button_menu.h"
  6. #include "irda.h"
  7. #include <cstdio>
  8. #include <string>
  9. #include <utility>
  10. #include "irda-app-file-parser.hpp"
  11. const char* IrdaAppRemoteManager::irda_directory = "irda";
  12. const char* IrdaAppRemoteManager::irda_extension = ".ir";
  13. static const std::string default_remote_name = "remote";
  14. static bool find_string(const std::vector<std::string>& strings, const std::string& match_string) {
  15. for(const auto& string : strings) {
  16. if(!string.compare(match_string)) return true;
  17. }
  18. return false;
  19. }
  20. static std::string
  21. find_vacant_name(const std::vector<std::string>& strings, const std::string& name) {
  22. // if suggested name is occupied, try another one (name2, name3, etc)
  23. if(find_string(strings, name)) {
  24. int i = 1;
  25. while(find_string(strings, name + std::to_string(++i)))
  26. ;
  27. return name + std::to_string(i);
  28. } else {
  29. return name;
  30. }
  31. }
  32. bool IrdaAppRemoteManager::add_button(const char* button_name, const IrdaAppSignal& signal) {
  33. remote->buttons.emplace_back(button_name, signal);
  34. return store();
  35. }
  36. bool IrdaAppRemoteManager::add_remote_with_button(
  37. const char* button_name,
  38. const IrdaAppSignal& signal) {
  39. furi_check(button_name != nullptr);
  40. std::vector<std::string> remote_list;
  41. bool result = get_remote_list(remote_list);
  42. if(!result) return false;
  43. auto new_name = find_vacant_name(remote_list, default_remote_name);
  44. remote = std::make_unique<IrdaAppRemote>(new_name);
  45. return add_button(button_name, signal);
  46. }
  47. IrdaAppRemote::IrdaAppRemote(const std::string& name)
  48. : name(name) {
  49. }
  50. std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const {
  51. std::vector<std::string> name_vector;
  52. name_vector.reserve(remote->buttons.size());
  53. for(const auto& it : remote->buttons) {
  54. name_vector.emplace_back(it.name);
  55. }
  56. // copy elision
  57. return name_vector;
  58. }
  59. const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const {
  60. furi_check(remote.get() != nullptr);
  61. auto& buttons = remote->buttons;
  62. furi_check(index < buttons.size());
  63. return buttons.at(index).signal;
  64. }
  65. std::string IrdaAppRemoteManager::make_filename(const std::string& name) const {
  66. return std::string("/") + irda_directory + "/" + name + irda_extension;
  67. }
  68. bool IrdaAppRemoteManager::delete_remote() {
  69. FS_Error fs_res;
  70. IrdaAppFileParser file_parser;
  71. fs_res = file_parser.get_fs_api().common.remove(make_filename(remote->name).c_str());
  72. if(fs_res != FSE_OK) {
  73. file_parser.get_sd_api().show_error(
  74. file_parser.get_sd_api().context, "Error deleting file");
  75. return false;
  76. }
  77. remote.reset();
  78. return true;
  79. }
  80. bool IrdaAppRemoteManager::delete_button(uint32_t index) {
  81. furi_check(remote.get() != nullptr);
  82. auto& buttons = remote->buttons;
  83. furi_check(index < buttons.size());
  84. buttons.erase(buttons.begin() + index);
  85. return store();
  86. }
  87. std::string IrdaAppRemoteManager::get_button_name(uint32_t index) {
  88. furi_check(remote.get() != nullptr);
  89. auto& buttons = remote->buttons;
  90. furi_check(index < buttons.size());
  91. return buttons[index].name;
  92. }
  93. std::string IrdaAppRemoteManager::get_remote_name() {
  94. furi_check(remote.get() != nullptr);
  95. return remote->name;
  96. }
  97. int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) {
  98. int i = 0;
  99. for(const auto& str : strings) {
  100. if(!str.compare(remote->name)) {
  101. return i;
  102. }
  103. ++i;
  104. }
  105. return -1;
  106. }
  107. bool IrdaAppRemoteManager::rename_remote(const char* str) {
  108. furi_check(str != nullptr);
  109. furi_check(remote.get() != nullptr);
  110. if(!remote->name.compare(str)) return true;
  111. std::vector<std::string> remote_list;
  112. bool result = get_remote_list(remote_list);
  113. if(!result) return false;
  114. auto new_name = find_vacant_name(remote_list, str);
  115. IrdaAppFileParser file_parser;
  116. FS_Error fs_err = file_parser.get_fs_api().common.rename(
  117. make_filename(remote->name).c_str(), make_filename(new_name).c_str());
  118. remote->name = new_name;
  119. if(fs_err != FSE_OK) {
  120. file_parser.get_sd_api().show_error(
  121. file_parser.get_sd_api().context, "Error renaming\nremote file");
  122. }
  123. return fs_err == FSE_OK;
  124. }
  125. bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) {
  126. furi_check(remote.get() != nullptr);
  127. auto& buttons = remote->buttons;
  128. furi_check(index < buttons.size());
  129. buttons[index].name = str;
  130. return store();
  131. }
  132. size_t IrdaAppRemoteManager::get_number_of_buttons() {
  133. furi_check(remote.get() != nullptr);
  134. return remote->buttons.size();
  135. }
  136. bool IrdaAppRemoteManager::store(void) {
  137. File file;
  138. std::string dirname(std::string("/") + irda_directory);
  139. IrdaAppFileParser file_parser;
  140. FS_Error fs_err = file_parser.get_fs_api().common.mkdir(dirname.c_str());
  141. if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) {
  142. file_parser.get_sd_api().show_error(
  143. file_parser.get_sd_api().context, "Can't create directory");
  144. return false;
  145. }
  146. bool res = file_parser.get_fs_api().file.open(
  147. &file, make_filename(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
  148. if(!res) {
  149. file_parser.get_sd_api().show_error(
  150. file_parser.get_sd_api().context, "Cannot create\nnew remote file");
  151. return false;
  152. }
  153. for(const auto& button : remote->buttons) {
  154. bool result = file_parser.store_signal(&file, button.signal, button.name.c_str());
  155. if(!result) {
  156. file_parser.get_sd_api().show_error(
  157. file_parser.get_sd_api().context, "Cannot write\nto key file");
  158. file_parser.get_fs_api().file.close(&file);
  159. return false;
  160. }
  161. }
  162. file_parser.get_fs_api().file.close(&file);
  163. file_parser.get_sd_api().check_error(file_parser.get_sd_api().context);
  164. return true;
  165. }
  166. bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const {
  167. bool fs_res = false;
  168. char name[128];
  169. File dir;
  170. std::string dirname(std::string("/") + irda_directory);
  171. remote_names.clear();
  172. IrdaAppFileParser file_parser;
  173. fs_res = file_parser.get_fs_api().dir.open(&dir, dirname.c_str());
  174. if(!fs_res) {
  175. if(!check_fs()) {
  176. file_parser.get_sd_api().show_error(
  177. file_parser.get_sd_api().context, "Cannot open\napplication directory");
  178. return false;
  179. } else {
  180. return true; // SD ok, but no files written yet
  181. }
  182. }
  183. while(file_parser.get_fs_api().dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
  184. std::string filename(name);
  185. auto extension_index = filename.rfind(irda_extension);
  186. if((extension_index == std::string::npos) ||
  187. (extension_index + strlen(irda_extension) != filename.size())) {
  188. continue;
  189. }
  190. remote_names.push_back(filename.erase(extension_index));
  191. }
  192. file_parser.get_fs_api().dir.close(&dir);
  193. return true;
  194. }
  195. bool IrdaAppRemoteManager::load(const std::string& name) {
  196. bool fs_res = false;
  197. IrdaAppFileParser file_parser;
  198. File file;
  199. fs_res = file_parser.get_fs_api().file.open(
  200. &file, make_filename(name).c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
  201. if(!fs_res) {
  202. file_parser.get_sd_api().show_error(
  203. file_parser.get_sd_api().context, "Error opening file");
  204. return false;
  205. }
  206. remote = std::make_unique<IrdaAppRemote>(name);
  207. while(1) {
  208. auto file_signal = file_parser.read_signal(&file);
  209. if(!file_signal.get()) break;
  210. remote->buttons.emplace_back(file_signal->name, file_signal->signal);
  211. }
  212. file_parser.get_fs_api().file.close(&file);
  213. return true;
  214. }
  215. bool IrdaAppRemoteManager::check_fs() const {
  216. // TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info().
  217. IrdaAppFileParser file_parser;
  218. auto fs_err = file_parser.get_fs_api().common.get_fs_info(nullptr, nullptr);
  219. if(fs_err != FSE_OK)
  220. file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "SD card not found");
  221. return fs_err == FSE_OK;
  222. }