irda-app-remote-manager.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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. std::vector<std::string> IrdaAppRemoteManager::get_button_list(void) const {
  48. std::vector<std::string> name_vector;
  49. name_vector.reserve(remote->buttons.size());
  50. for(const auto& it : remote->buttons) {
  51. name_vector.emplace_back(it.name);
  52. }
  53. // copy elision
  54. return name_vector;
  55. }
  56. const IrdaAppSignal& IrdaAppRemoteManager::get_button_data(size_t index) const {
  57. furi_check(remote.get() != nullptr);
  58. auto& buttons = remote->buttons;
  59. furi_check(index < buttons.size());
  60. return buttons.at(index).signal;
  61. }
  62. std::string IrdaAppRemoteManager::make_full_name(const std::string& remote_name) const {
  63. return std::string("/") + irda_directory + "/" + remote_name + irda_extension;
  64. }
  65. std::string IrdaAppRemoteManager::make_remote_name(const std::string& full_name) const {
  66. std::string str(full_name, full_name.find_last_of('/') + 1, full_name.size());
  67. str.erase(str.find_last_of('.'));
  68. return str;
  69. }
  70. bool IrdaAppRemoteManager::delete_remote() {
  71. FS_Error fs_res;
  72. IrdaAppFileParser file_parser;
  73. fs_res = file_parser.get_fs_api().common.remove(make_full_name(remote->name).c_str());
  74. if(fs_res != FSE_OK) {
  75. file_parser.get_sd_api().show_error(
  76. file_parser.get_sd_api().context, "Error deleting file");
  77. return false;
  78. }
  79. remote.reset();
  80. return true;
  81. }
  82. bool IrdaAppRemoteManager::delete_button(uint32_t index) {
  83. furi_check(remote.get() != nullptr);
  84. auto& buttons = remote->buttons;
  85. furi_check(index < buttons.size());
  86. buttons.erase(buttons.begin() + index);
  87. return store();
  88. }
  89. std::string IrdaAppRemoteManager::get_button_name(uint32_t index) {
  90. furi_check(remote.get() != nullptr);
  91. auto& buttons = remote->buttons;
  92. furi_check(index < buttons.size());
  93. return buttons[index].name;
  94. }
  95. std::string IrdaAppRemoteManager::get_remote_name() {
  96. furi_check(remote.get() != nullptr);
  97. return remote->name;
  98. }
  99. int IrdaAppRemoteManager::find_remote_name(const std::vector<std::string>& strings) {
  100. int i = 0;
  101. for(const auto& str : strings) {
  102. if(!str.compare(remote->name)) {
  103. return i;
  104. }
  105. ++i;
  106. }
  107. return -1;
  108. }
  109. bool IrdaAppRemoteManager::rename_remote(const char* str) {
  110. furi_check(str != nullptr);
  111. furi_check(remote.get() != nullptr);
  112. if(!remote->name.compare(str)) return true;
  113. std::vector<std::string> remote_list;
  114. bool result = get_remote_list(remote_list);
  115. if(!result) return false;
  116. auto new_name = find_vacant_name(remote_list, str);
  117. IrdaAppFileParser file_parser;
  118. FS_Error fs_err = file_parser.get_fs_api().common.rename(
  119. make_full_name(remote->name).c_str(), make_full_name(new_name).c_str());
  120. remote->name = new_name;
  121. if(fs_err != FSE_OK) {
  122. file_parser.get_sd_api().show_error(
  123. file_parser.get_sd_api().context, "Error renaming\nremote file");
  124. }
  125. return fs_err == FSE_OK;
  126. }
  127. bool IrdaAppRemoteManager::rename_button(uint32_t index, const char* str) {
  128. furi_check(remote.get() != nullptr);
  129. auto& buttons = remote->buttons;
  130. furi_check(index < buttons.size());
  131. buttons[index].name = str;
  132. return store();
  133. }
  134. size_t IrdaAppRemoteManager::get_number_of_buttons() {
  135. furi_check(remote.get() != nullptr);
  136. return remote->buttons.size();
  137. }
  138. bool IrdaAppRemoteManager::store(void) {
  139. File file;
  140. std::string dirname(std::string("/") + irda_directory);
  141. IrdaAppFileParser file_parser;
  142. FS_Error fs_err = file_parser.get_fs_api().common.mkdir(dirname.c_str());
  143. if((fs_err != FSE_OK) && (fs_err != FSE_EXIST)) {
  144. file_parser.get_sd_api().show_error(
  145. file_parser.get_sd_api().context, "Can't create directory");
  146. return false;
  147. }
  148. bool res = file_parser.get_fs_api().file.open(
  149. &file, make_full_name(remote->name).c_str(), FSAM_WRITE, FSOM_CREATE_ALWAYS);
  150. if(!res) {
  151. file_parser.get_sd_api().show_error(
  152. file_parser.get_sd_api().context, "Cannot create\nnew remote file");
  153. return false;
  154. }
  155. for(const auto& button : remote->buttons) {
  156. bool result = file_parser.store_signal(&file, button.signal, button.name.c_str());
  157. if(!result) {
  158. file_parser.get_sd_api().show_error(
  159. file_parser.get_sd_api().context, "Cannot write\nto key file");
  160. file_parser.get_fs_api().file.close(&file);
  161. return false;
  162. }
  163. }
  164. file_parser.get_fs_api().file.close(&file);
  165. file_parser.get_sd_api().check_error(file_parser.get_sd_api().context);
  166. return true;
  167. }
  168. bool IrdaAppRemoteManager::get_remote_list(std::vector<std::string>& remote_names) const {
  169. bool fs_res = false;
  170. char name[128];
  171. File dir;
  172. std::string dirname(std::string("/") + irda_directory);
  173. remote_names.clear();
  174. IrdaAppFileParser file_parser;
  175. fs_res = file_parser.get_fs_api().dir.open(&dir, dirname.c_str());
  176. if(!fs_res) {
  177. if(!check_fs()) {
  178. file_parser.get_sd_api().show_error(
  179. file_parser.get_sd_api().context, "Cannot open\napplication directory");
  180. return false;
  181. } else {
  182. return true; // SD ok, but no files written yet
  183. }
  184. }
  185. while(file_parser.get_fs_api().dir.read(&dir, nullptr, name, sizeof(name)) && strlen(name)) {
  186. std::string filename(name);
  187. auto extension_index = filename.rfind(irda_extension);
  188. if((extension_index == std::string::npos) ||
  189. (extension_index + strlen(irda_extension) != filename.size())) {
  190. continue;
  191. }
  192. remote_names.push_back(filename.erase(extension_index));
  193. }
  194. file_parser.get_fs_api().dir.close(&dir);
  195. return true;
  196. }
  197. bool IrdaAppRemoteManager::load(const std::string& name_arg, bool fullpath) {
  198. bool fs_res = false;
  199. IrdaAppFileParser file_parser;
  200. File file;
  201. std::string full_filename;
  202. std::string remote_name;
  203. if(fullpath) {
  204. full_filename = name_arg;
  205. remote_name = make_remote_name(name_arg);
  206. } else {
  207. full_filename = make_full_name(name_arg);
  208. remote_name = name_arg;
  209. }
  210. fs_res = file_parser.get_fs_api().file.open(
  211. &file, full_filename.c_str(), FSAM_READ, FSOM_OPEN_EXISTING);
  212. if(!fs_res) {
  213. file_parser.get_sd_api().show_error(
  214. file_parser.get_sd_api().context, "Error opening file");
  215. return false;
  216. }
  217. remote = std::make_unique<IrdaAppRemote>(remote_name);
  218. while(1) {
  219. auto file_signal = file_parser.read_signal(&file);
  220. if(!file_signal.get()) break;
  221. remote->buttons.emplace_back(file_signal->name, file_signal->signal);
  222. }
  223. file_parser.get_fs_api().file.close(&file);
  224. return true;
  225. }
  226. bool IrdaAppRemoteManager::check_fs() const {
  227. // TODO: [FL-1431] Add return value to file_parser.get_sd_api().check_error() and replace get_fs_info().
  228. IrdaAppFileParser file_parser;
  229. auto fs_err = file_parser.get_fs_api().common.get_fs_info(nullptr, nullptr);
  230. if(fs_err != FSE_OK)
  231. file_parser.get_sd_api().show_error(file_parser.get_sd_api().context, "SD card not found");
  232. return fs_err == FSE_OK;
  233. }