receiver.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. #include "receiver.h"
  2. #include "../subghz_i.h"
  3. #include <math.h>
  4. #include <input/input.h>
  5. #include <gui/elements.h>
  6. #include <assets_icons.h>
  7. #include <m-string.h>
  8. #include <m-array.h>
  9. #define FRAME_HEIGHT 12
  10. #define MAX_LEN_PX 100
  11. #define MENU_ITEMS 4u
  12. #define UNLOCK_CNT 3
  13. typedef struct {
  14. string_t item_str;
  15. uint8_t type;
  16. } SubGhzReceiverMenuItem;
  17. ARRAY_DEF(SubGhzReceiverMenuItemArray, SubGhzReceiverMenuItem, M_POD_OPLIST)
  18. #define M_OPL_SubGhzReceiverMenuItemArray_t() \
  19. ARRAY_OPLIST(SubGhzReceiverMenuItemArray, M_POD_OPLIST)
  20. struct SubGhzReceiverHistory {
  21. SubGhzReceiverMenuItemArray_t data;
  22. };
  23. typedef struct SubGhzReceiverHistory SubGhzReceiverHistory;
  24. static const Icon* ReceiverItemIcons[] = {
  25. [SubGhzProtocolTypeUnknown] = &I_Quest_7x8,
  26. [SubGhzProtocolTypeStatic] = &I_Unlock_7x8,
  27. [SubGhzProtocolTypeDynamic] = &I_Lock_7x8,
  28. };
  29. typedef enum {
  30. SubGhzViewReceiverBarShowDefault,
  31. SubGhzViewReceiverBarShowLock,
  32. SubGhzViewReceiverBarShowToUnlockPress,
  33. SubGhzViewReceiverBarShowUnlock,
  34. } SubGhzViewReceiverBarShow;
  35. struct SubGhzViewReceiver {
  36. SubGhzLock lock;
  37. uint8_t lock_count;
  38. FuriTimer* timer;
  39. View* view;
  40. SubGhzViewReceiverCallback callback;
  41. void* context;
  42. };
  43. typedef struct {
  44. string_t frequency_str;
  45. string_t preset_str;
  46. string_t history_stat_str;
  47. SubGhzReceiverHistory* history;
  48. uint16_t idx;
  49. uint16_t list_offset;
  50. uint16_t history_item;
  51. SubGhzViewReceiverBarShow bar_show;
  52. } SubGhzViewReceiverModel;
  53. void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) {
  54. furi_assert(subghz_receiver);
  55. subghz_receiver->lock_count = 0;
  56. if(lock == SubGhzLockOn) {
  57. subghz_receiver->lock = lock;
  58. with_view_model(
  59. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  60. model->bar_show = SubGhzViewReceiverBarShowLock;
  61. return true;
  62. });
  63. furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000));
  64. } else {
  65. with_view_model(
  66. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  67. model->bar_show = SubGhzViewReceiverBarShowDefault;
  68. return true;
  69. });
  70. }
  71. }
  72. void subghz_view_receiver_set_callback(
  73. SubGhzViewReceiver* subghz_receiver,
  74. SubGhzViewReceiverCallback callback,
  75. void* context) {
  76. furi_assert(subghz_receiver);
  77. furi_assert(callback);
  78. subghz_receiver->callback = callback;
  79. subghz_receiver->context = context;
  80. }
  81. static void subghz_view_receiver_update_offset(SubGhzViewReceiver* subghz_receiver) {
  82. furi_assert(subghz_receiver);
  83. with_view_model(
  84. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  85. size_t history_item = model->history_item;
  86. uint16_t bounds = history_item > 3 ? 2 : history_item;
  87. if(history_item > 3 && model->idx >= (int16_t)(history_item - 1)) {
  88. model->list_offset = model->idx - 3;
  89. } else if(model->list_offset < model->idx - bounds) {
  90. model->list_offset =
  91. CLAMP(model->list_offset + 1, (int16_t)(history_item - bounds), 0);
  92. } else if(model->list_offset > model->idx - bounds) {
  93. model->list_offset = CLAMP(model->idx - 1, (int16_t)(history_item - bounds), 0);
  94. }
  95. return true;
  96. });
  97. }
  98. void subghz_view_receiver_add_item_to_menu(
  99. SubGhzViewReceiver* subghz_receiver,
  100. const char* name,
  101. uint8_t type) {
  102. furi_assert(subghz_receiver);
  103. with_view_model(
  104. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  105. SubGhzReceiverMenuItem* item_menu =
  106. SubGhzReceiverMenuItemArray_push_raw(model->history->data);
  107. string_init_set_str(item_menu->item_str, name);
  108. item_menu->type = type;
  109. if((model->idx == model->history_item - 1)) {
  110. model->history_item++;
  111. model->idx++;
  112. } else {
  113. model->history_item++;
  114. }
  115. return true;
  116. });
  117. subghz_view_receiver_update_offset(subghz_receiver);
  118. }
  119. void subghz_view_receiver_add_data_statusbar(
  120. SubGhzViewReceiver* subghz_receiver,
  121. const char* frequency_str,
  122. const char* preset_str,
  123. const char* history_stat_str) {
  124. furi_assert(subghz_receiver);
  125. with_view_model(
  126. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  127. string_set_str(model->frequency_str, frequency_str);
  128. string_set_str(model->preset_str, preset_str);
  129. string_set_str(model->history_stat_str, history_stat_str);
  130. return true;
  131. });
  132. }
  133. static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool scrollbar) {
  134. canvas_set_color(canvas, ColorBlack);
  135. canvas_draw_box(canvas, 0, 0 + idx * FRAME_HEIGHT, scrollbar ? 122 : 127, FRAME_HEIGHT);
  136. canvas_set_color(canvas, ColorWhite);
  137. canvas_draw_dot(canvas, 0, 0 + idx * FRAME_HEIGHT);
  138. canvas_draw_dot(canvas, 1, 0 + idx * FRAME_HEIGHT);
  139. canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 1);
  140. canvas_draw_dot(canvas, 0, (0 + idx * FRAME_HEIGHT) + 11);
  141. canvas_draw_dot(canvas, scrollbar ? 121 : 126, 0 + idx * FRAME_HEIGHT);
  142. canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11);
  143. }
  144. void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) {
  145. canvas_clear(canvas);
  146. canvas_set_color(canvas, ColorBlack);
  147. canvas_set_font(canvas, FontSecondary);
  148. elements_button_left(canvas, "Config");
  149. canvas_draw_line(canvas, 46, 51, 125, 51);
  150. bool scrollbar = model->history_item > 4;
  151. string_t str_buff;
  152. string_init(str_buff);
  153. SubGhzReceiverMenuItem* item_menu;
  154. for(size_t i = 0; i < MIN(model->history_item, MENU_ITEMS); ++i) {
  155. size_t idx = CLAMP((uint16_t)(i + model->list_offset), model->history_item, 0);
  156. item_menu = SubGhzReceiverMenuItemArray_get(model->history->data, idx);
  157. string_set(str_buff, item_menu->item_str);
  158. elements_string_fit_width(canvas, str_buff, scrollbar ? MAX_LEN_PX - 6 : MAX_LEN_PX);
  159. if(model->idx == idx) {
  160. subghz_view_receiver_draw_frame(canvas, i, scrollbar);
  161. } else {
  162. canvas_set_color(canvas, ColorBlack);
  163. }
  164. canvas_draw_icon(canvas, 1, 2 + i * FRAME_HEIGHT, ReceiverItemIcons[item_menu->type]);
  165. canvas_draw_str(canvas, 15, 9 + i * FRAME_HEIGHT, string_get_cstr(str_buff));
  166. string_reset(str_buff);
  167. }
  168. if(scrollbar) {
  169. elements_scrollbar_pos(canvas, 128, 0, 49, model->idx, model->history_item);
  170. }
  171. string_clear(str_buff);
  172. canvas_set_color(canvas, ColorBlack);
  173. if(model->history_item == 0) {
  174. canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52);
  175. canvas_set_font(canvas, FontPrimary);
  176. canvas_draw_str(canvas, 63, 46, "Scanning...");
  177. canvas_draw_line(canvas, 46, 51, 125, 51);
  178. canvas_set_font(canvas, FontSecondary);
  179. }
  180. switch(model->bar_show) {
  181. case SubGhzViewReceiverBarShowLock:
  182. canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8);
  183. canvas_draw_str(canvas, 74, 62, "Locked");
  184. break;
  185. case SubGhzViewReceiverBarShowToUnlockPress:
  186. canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
  187. canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
  188. canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
  189. canvas_set_font(canvas, FontSecondary);
  190. elements_bold_rounded_frame(canvas, 14, 8, 99, 48);
  191. elements_multiline_text(canvas, 65, 26, "To unlock\npress:");
  192. canvas_draw_icon(canvas, 65, 42, &I_Pin_back_arrow_10x8);
  193. canvas_draw_icon(canvas, 80, 42, &I_Pin_back_arrow_10x8);
  194. canvas_draw_icon(canvas, 95, 42, &I_Pin_back_arrow_10x8);
  195. canvas_draw_icon(canvas, 16, 13, &I_WarningDolphin_45x42);
  196. canvas_draw_dot(canvas, 17, 61);
  197. break;
  198. case SubGhzViewReceiverBarShowUnlock:
  199. canvas_draw_icon(canvas, 64, 55, &I_Unlock_7x8);
  200. canvas_draw_str(canvas, 74, 62, "Unlocked");
  201. break;
  202. default:
  203. canvas_draw_str(canvas, 44, 62, string_get_cstr(model->frequency_str));
  204. canvas_draw_str(canvas, 79, 62, string_get_cstr(model->preset_str));
  205. canvas_draw_str(canvas, 96, 62, string_get_cstr(model->history_stat_str));
  206. break;
  207. }
  208. }
  209. static void subghz_view_receiver_timer_callback(void* context) {
  210. furi_assert(context);
  211. SubGhzViewReceiver* subghz_receiver = context;
  212. with_view_model(
  213. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  214. model->bar_show = SubGhzViewReceiverBarShowDefault;
  215. return true;
  216. });
  217. if(subghz_receiver->lock_count < UNLOCK_CNT) {
  218. subghz_receiver->callback(
  219. SubGhzCustomEventViewReceiverOffDisplay, subghz_receiver->context);
  220. } else {
  221. subghz_receiver->lock = SubGhzLockOff;
  222. subghz_receiver->callback(SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
  223. }
  224. subghz_receiver->lock_count = 0;
  225. }
  226. bool subghz_view_receiver_input(InputEvent* event, void* context) {
  227. furi_assert(context);
  228. SubGhzViewReceiver* subghz_receiver = context;
  229. if(subghz_receiver->lock == SubGhzLockOn) {
  230. with_view_model(
  231. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  232. model->bar_show = SubGhzViewReceiverBarShowToUnlockPress;
  233. return true;
  234. });
  235. if(subghz_receiver->lock_count == 0) {
  236. furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(1000));
  237. }
  238. if(event->key == InputKeyBack && event->type == InputTypeShort) {
  239. subghz_receiver->lock_count++;
  240. }
  241. if(subghz_receiver->lock_count >= UNLOCK_CNT) {
  242. // subghz_receiver->callback(
  243. // SubGhzCustomEventViewReceiverUnlock, subghz_receiver->context);
  244. with_view_model(
  245. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  246. model->bar_show = SubGhzViewReceiverBarShowUnlock;
  247. return true;
  248. });
  249. //subghz_receiver->lock = SubGhzLockOff;
  250. furi_timer_start(subghz_receiver->timer, pdMS_TO_TICKS(650));
  251. }
  252. return true;
  253. }
  254. if(event->key == InputKeyBack && event->type == InputTypeShort) {
  255. subghz_receiver->callback(SubGhzCustomEventViewReceiverBack, subghz_receiver->context);
  256. } else if(
  257. event->key == InputKeyUp &&
  258. (event->type == InputTypeShort || event->type == InputTypeRepeat)) {
  259. with_view_model(
  260. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  261. if(model->idx != 0) model->idx--;
  262. return true;
  263. });
  264. } else if(
  265. event->key == InputKeyDown &&
  266. (event->type == InputTypeShort || event->type == InputTypeRepeat)) {
  267. with_view_model(
  268. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  269. if(model->idx != model->history_item - 1) model->idx++;
  270. return true;
  271. });
  272. } else if(event->key == InputKeyLeft && event->type == InputTypeShort) {
  273. subghz_receiver->callback(SubGhzCustomEventViewReceiverConfig, subghz_receiver->context);
  274. } else if(event->key == InputKeyOk && event->type == InputTypeShort) {
  275. with_view_model(
  276. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  277. if(model->history_item != 0) {
  278. subghz_receiver->callback(
  279. SubGhzCustomEventViewReceiverOK, subghz_receiver->context);
  280. }
  281. return false;
  282. });
  283. }
  284. subghz_view_receiver_update_offset(subghz_receiver);
  285. return true;
  286. }
  287. void subghz_view_receiver_enter(void* context) {
  288. furi_assert(context);
  289. }
  290. void subghz_view_receiver_exit(void* context) {
  291. furi_assert(context);
  292. SubGhzViewReceiver* subghz_receiver = context;
  293. with_view_model(
  294. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  295. string_reset(model->frequency_str);
  296. string_reset(model->preset_str);
  297. string_reset(model->history_stat_str);
  298. for
  299. M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
  300. string_clear(item_menu->item_str);
  301. item_menu->type = 0;
  302. }
  303. SubGhzReceiverMenuItemArray_reset(model->history->data);
  304. model->idx = 0;
  305. model->list_offset = 0;
  306. model->history_item = 0;
  307. return false;
  308. });
  309. furi_timer_stop(subghz_receiver->timer);
  310. }
  311. SubGhzViewReceiver* subghz_view_receiver_alloc() {
  312. SubGhzViewReceiver* subghz_receiver = malloc(sizeof(SubGhzViewReceiver));
  313. // View allocation and configuration
  314. subghz_receiver->view = view_alloc();
  315. subghz_receiver->lock = SubGhzLockOff;
  316. subghz_receiver->lock_count = 0;
  317. view_allocate_model(
  318. subghz_receiver->view, ViewModelTypeLocking, sizeof(SubGhzViewReceiverModel));
  319. view_set_context(subghz_receiver->view, subghz_receiver);
  320. view_set_draw_callback(subghz_receiver->view, (ViewDrawCallback)subghz_view_receiver_draw);
  321. view_set_input_callback(subghz_receiver->view, subghz_view_receiver_input);
  322. view_set_enter_callback(subghz_receiver->view, subghz_view_receiver_enter);
  323. view_set_exit_callback(subghz_receiver->view, subghz_view_receiver_exit);
  324. with_view_model(
  325. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  326. string_init(model->frequency_str);
  327. string_init(model->preset_str);
  328. string_init(model->history_stat_str);
  329. model->bar_show = SubGhzViewReceiverBarShowDefault;
  330. model->history = malloc(sizeof(SubGhzReceiverHistory));
  331. SubGhzReceiverMenuItemArray_init(model->history->data);
  332. return true;
  333. });
  334. subghz_receiver->timer =
  335. furi_timer_alloc(subghz_view_receiver_timer_callback, FuriTimerTypeOnce, subghz_receiver);
  336. return subghz_receiver;
  337. }
  338. void subghz_view_receiver_free(SubGhzViewReceiver* subghz_receiver) {
  339. furi_assert(subghz_receiver);
  340. with_view_model(
  341. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  342. string_clear(model->frequency_str);
  343. string_clear(model->preset_str);
  344. string_clear(model->history_stat_str);
  345. for
  346. M_EACH(item_menu, model->history->data, SubGhzReceiverMenuItemArray_t) {
  347. string_clear(item_menu->item_str);
  348. item_menu->type = 0;
  349. }
  350. SubGhzReceiverMenuItemArray_clear(model->history->data);
  351. free(model->history);
  352. return false;
  353. });
  354. furi_timer_free(subghz_receiver->timer);
  355. view_free(subghz_receiver->view);
  356. free(subghz_receiver);
  357. }
  358. View* subghz_view_receiver_get_view(SubGhzViewReceiver* subghz_receiver) {
  359. furi_assert(subghz_receiver);
  360. return subghz_receiver->view;
  361. }
  362. uint16_t subghz_view_receiver_get_idx_menu(SubGhzViewReceiver* subghz_receiver) {
  363. furi_assert(subghz_receiver);
  364. uint32_t idx = 0;
  365. with_view_model(
  366. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  367. idx = model->idx;
  368. return false;
  369. });
  370. return idx;
  371. }
  372. void subghz_view_receiver_set_idx_menu(SubGhzViewReceiver* subghz_receiver, uint16_t idx) {
  373. furi_assert(subghz_receiver);
  374. with_view_model(
  375. subghz_receiver->view, (SubGhzViewReceiverModel * model) {
  376. model->idx = idx;
  377. if(model->idx > 2) model->list_offset = idx - 2;
  378. return true;
  379. });
  380. subghz_view_receiver_update_offset(subghz_receiver);
  381. }