totp_app_settings.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. #include "totp_app_settings.h"
  2. #include <math.h>
  3. #include <totp_icons.h>
  4. #include "../../../services/fonts/font_provider.h"
  5. #include "../../canvas_extensions.h"
  6. #include "../../ui_controls.h"
  7. #include "../../common_dialogs.h"
  8. #include "../../scene_director.h"
  9. #include "../../constants.h"
  10. #include "../../../services/config/config.h"
  11. #include "../../../services/convert/convert.h"
  12. #include <roll_value.h>
  13. #include "../../../config/app/config.h"
  14. #ifdef TOTP_BADBT_AUTOMATION_ENABLED
  15. #include "../../../workers/bt_type_code/bt_type_code.h"
  16. #endif
  17. #define FONT_TEST_STR_LENGTH (7)
  18. static const char* YES_NO_LIST[] = {"NO", "YES"};
  19. static const char* AUTOMATION_LIST[] = {
  20. "None",
  21. "USB"
  22. #ifdef TOTP_BADBT_AUTOMATION_ENABLED
  23. ,
  24. "Bluetooth",
  25. "BT and USB"
  26. #endif
  27. };
  28. static const char* BAD_KB_LAYOUT_LIST[] = {"QWERTY", "AZERTY", "QWERTZ"};
  29. static const char* FONT_TEST_STR = "0123BCD";
  30. typedef enum {
  31. HoursInput,
  32. MinutesInput,
  33. FontSelect,
  34. SoundSwitch,
  35. VibroSwitch,
  36. AutomationSwitch,
  37. BadKeyboardLayoutSelect,
  38. AutomationDelaySelect,
  39. ConfirmButton
  40. } Control;
  41. typedef struct {
  42. int8_t tz_offset_hours;
  43. uint8_t tz_offset_minutes;
  44. bool notification_sound;
  45. bool notification_vibro;
  46. AutomationMethod automation_method;
  47. uint16_t y_offset;
  48. AutomationKeyboardLayout automation_kb_layout;
  49. Control selected_control;
  50. uint8_t active_font_index;
  51. FontInfo* active_font;
  52. uint8_t total_fonts_count;
  53. uint16_t automation_initial_delay;
  54. char automation_initial_delay_formatted[10];
  55. } SceneState;
  56. static void two_digit_to_str(int8_t num, char* str) {
  57. char* s = str;
  58. if(num < 0) {
  59. *(s++) = '-';
  60. num = -num;
  61. }
  62. uint8_t d1 = (num / 10) % 10;
  63. uint8_t d2 = num % 10;
  64. *(s++) = CONVERT_DIGIT_TO_CHAR(d1);
  65. *(s++) = CONVERT_DIGIT_TO_CHAR(d2);
  66. *(s++) = '\0';
  67. }
  68. static void update_formatted_automation_initial_delay(SceneState* scene_state) {
  69. snprintf(
  70. &scene_state->automation_initial_delay_formatted[0],
  71. sizeof(scene_state->automation_initial_delay_formatted),
  72. "%.1f sec.",
  73. (double)(scene_state->automation_initial_delay / 1000.0f));
  74. }
  75. void totp_scene_app_settings_activate(PluginState* plugin_state) {
  76. SceneState* scene_state = malloc(sizeof(SceneState));
  77. furi_check(scene_state != NULL);
  78. plugin_state->current_scene_state = scene_state;
  79. float off_int;
  80. float off_dec = modff(plugin_state->timezone_offset, &off_int);
  81. scene_state->tz_offset_hours = off_int;
  82. scene_state->tz_offset_minutes = 60.0f * off_dec;
  83. scene_state->notification_sound = plugin_state->notification_method & NotificationMethodSound;
  84. scene_state->notification_vibro = plugin_state->notification_method & NotificationMethodVibro;
  85. scene_state->automation_method =
  86. MIN(plugin_state->automation_method, COUNT_OF(AUTOMATION_LIST) - 1);
  87. scene_state->automation_kb_layout =
  88. MIN(plugin_state->automation_kb_layout, COUNT_OF(BAD_KB_LAYOUT_LIST) - 1);
  89. scene_state->automation_initial_delay = plugin_state->automation_initial_delay;
  90. scene_state->total_fonts_count = totp_font_provider_get_fonts_count();
  91. scene_state->active_font_index = plugin_state->active_font_index;
  92. scene_state->active_font = totp_font_info_alloc();
  93. if(!totp_font_provider_get_font(scene_state->active_font_index, scene_state->active_font)) {
  94. scene_state->active_font_index = 0;
  95. totp_font_provider_get_font(scene_state->active_font_index, scene_state->active_font);
  96. }
  97. update_formatted_automation_initial_delay(scene_state);
  98. }
  99. void totp_scene_app_settings_render(Canvas* const canvas, const PluginState* plugin_state) {
  100. const SceneState* scene_state = plugin_state->current_scene_state;
  101. if(scene_state->selected_control < FontSelect) {
  102. canvas_set_font(canvas, FontPrimary);
  103. canvas_draw_str_aligned(
  104. canvas, 0, 0 - scene_state->y_offset, AlignLeft, AlignTop, "Timezone offset");
  105. canvas_set_font(canvas, FontSecondary);
  106. char tmp_str[4];
  107. two_digit_to_str(scene_state->tz_offset_hours, &tmp_str[0]);
  108. canvas_draw_str_aligned(
  109. canvas, 0, 17 - scene_state->y_offset, AlignLeft, AlignTop, "Hours:");
  110. ui_control_select_render(
  111. canvas,
  112. 36,
  113. 10 - scene_state->y_offset,
  114. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  115. &tmp_str[0],
  116. scene_state->selected_control == HoursInput);
  117. two_digit_to_str(scene_state->tz_offset_minutes, &tmp_str[0]);
  118. canvas_draw_str_aligned(
  119. canvas, 0, 35 - scene_state->y_offset, AlignLeft, AlignTop, "Minutes:");
  120. ui_control_select_render(
  121. canvas,
  122. 36,
  123. 28 - scene_state->y_offset,
  124. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  125. &tmp_str[0],
  126. scene_state->selected_control == MinutesInput);
  127. } else if(scene_state->selected_control < SoundSwitch) {
  128. canvas_set_font(canvas, FontPrimary);
  129. canvas_draw_str_aligned(
  130. canvas, 0, 64 - scene_state->y_offset, AlignLeft, AlignTop, "Font");
  131. canvas_set_font(canvas, FontSecondary);
  132. const FontInfo* const font = scene_state->active_font;
  133. ui_control_select_render(
  134. canvas,
  135. 0,
  136. 74 - scene_state->y_offset,
  137. SCREEN_WIDTH - UI_CONTROL_VSCROLL_WIDTH,
  138. font->name,
  139. scene_state->selected_control == FontSelect);
  140. uint8_t font_x_offset =
  141. SCREEN_WIDTH_CENTER -
  142. (((font->char_info[0].width + font->space_width) * FONT_TEST_STR_LENGTH) >> 1);
  143. uint8_t font_y_offset = 108 - scene_state->y_offset - (font->height >> 1);
  144. canvas_draw_str_ex(
  145. canvas, font_x_offset, font_y_offset, FONT_TEST_STR, FONT_TEST_STR_LENGTH, font);
  146. } else if(scene_state->selected_control < AutomationSwitch) {
  147. canvas_set_font(canvas, FontPrimary);
  148. canvas_draw_str_aligned(
  149. canvas, 0, 128 - scene_state->y_offset, AlignLeft, AlignTop, "Notifications");
  150. canvas_set_font(canvas, FontSecondary);
  151. canvas_draw_str_aligned(
  152. canvas, 0, 145 - scene_state->y_offset, AlignLeft, AlignTop, "Sound:");
  153. ui_control_select_render(
  154. canvas,
  155. 36,
  156. 138 - scene_state->y_offset,
  157. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  158. YES_NO_LIST[scene_state->notification_sound],
  159. scene_state->selected_control == SoundSwitch);
  160. canvas_draw_str_aligned(
  161. canvas, 0, 163 - scene_state->y_offset, AlignLeft, AlignTop, "Vibro:");
  162. ui_control_select_render(
  163. canvas,
  164. 36,
  165. 156 - scene_state->y_offset,
  166. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  167. YES_NO_LIST[scene_state->notification_vibro],
  168. scene_state->selected_control == VibroSwitch);
  169. } else {
  170. canvas_set_font(canvas, FontPrimary);
  171. canvas_draw_str_aligned(
  172. canvas, 0, 192 - scene_state->y_offset, AlignLeft, AlignTop, "Automation");
  173. canvas_set_font(canvas, FontSecondary);
  174. int group_offset = 0;
  175. if(scene_state->selected_control <= AutomationSwitch) {
  176. canvas_draw_str_aligned(
  177. canvas,
  178. 0,
  179. 209 - scene_state->y_offset - group_offset,
  180. AlignLeft,
  181. AlignTop,
  182. "Method:");
  183. ui_control_select_render(
  184. canvas,
  185. 36,
  186. 202 - scene_state->y_offset - group_offset,
  187. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  188. AUTOMATION_LIST[scene_state->automation_method],
  189. scene_state->selected_control == AutomationSwitch);
  190. } else {
  191. group_offset += 18;
  192. }
  193. canvas_draw_str_aligned(
  194. canvas, 0, 227 - scene_state->y_offset - group_offset, AlignLeft, AlignTop, "Layout:");
  195. ui_control_select_render(
  196. canvas,
  197. 36,
  198. 220 - scene_state->y_offset - group_offset,
  199. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  200. BAD_KB_LAYOUT_LIST[scene_state->automation_kb_layout],
  201. scene_state->selected_control == BadKeyboardLayoutSelect);
  202. canvas_draw_str_aligned(
  203. canvas, 0, 245 - scene_state->y_offset - group_offset, AlignLeft, AlignTop, "Delay:");
  204. ui_control_select_render(
  205. canvas,
  206. 36,
  207. 238 - scene_state->y_offset - group_offset,
  208. SCREEN_WIDTH - 36 - UI_CONTROL_VSCROLL_WIDTH,
  209. &scene_state->automation_initial_delay_formatted[0],
  210. scene_state->selected_control == AutomationDelaySelect);
  211. ui_control_button_render(
  212. canvas,
  213. SCREEN_WIDTH_CENTER - 24,
  214. 260 - scene_state->y_offset - group_offset,
  215. 48,
  216. 13,
  217. "Confirm",
  218. scene_state->selected_control == ConfirmButton);
  219. }
  220. ui_control_vscroll_render(
  221. canvas, SCREEN_WIDTH - 3, 0, SCREEN_HEIGHT, scene_state->selected_control, ConfirmButton);
  222. }
  223. bool totp_scene_app_settings_handle_event(
  224. const PluginEvent* const event,
  225. PluginState* plugin_state) {
  226. if(event->type != EventTypeKey) {
  227. return true;
  228. }
  229. SceneState* scene_state = (SceneState*)plugin_state->current_scene_state;
  230. if(event->input.type == InputTypePress || event->input.type == InputTypeRepeat) {
  231. switch(event->input.key) {
  232. case InputKeyUp:
  233. totp_roll_value_uint8_t(
  234. &scene_state->selected_control,
  235. -1,
  236. HoursInput,
  237. ConfirmButton,
  238. RollOverflowBehaviorStop);
  239. if(scene_state->selected_control > VibroSwitch) {
  240. scene_state->y_offset = SCREEN_HEIGHT * 3;
  241. } else if(scene_state->selected_control > FontSelect) {
  242. scene_state->y_offset = SCREEN_HEIGHT * 2;
  243. } else if(scene_state->selected_control > MinutesInput) {
  244. scene_state->y_offset = SCREEN_HEIGHT;
  245. } else {
  246. scene_state->y_offset = 0;
  247. }
  248. break;
  249. case InputKeyDown:
  250. totp_roll_value_uint8_t(
  251. &scene_state->selected_control,
  252. 1,
  253. HoursInput,
  254. ConfirmButton,
  255. RollOverflowBehaviorStop);
  256. if(scene_state->selected_control > VibroSwitch) {
  257. scene_state->y_offset = SCREEN_HEIGHT * 3;
  258. } else if(scene_state->selected_control > FontSelect) {
  259. scene_state->y_offset = SCREEN_HEIGHT * 2;
  260. } else if(scene_state->selected_control > MinutesInput) {
  261. scene_state->y_offset = SCREEN_HEIGHT;
  262. } else {
  263. scene_state->y_offset = 0;
  264. }
  265. break;
  266. case InputKeyRight:
  267. if(scene_state->selected_control == HoursInput) {
  268. totp_roll_value_int8_t(
  269. &scene_state->tz_offset_hours, 1, -12, 12, RollOverflowBehaviorStop);
  270. } else if(scene_state->selected_control == MinutesInput) {
  271. totp_roll_value_uint8_t(
  272. &scene_state->tz_offset_minutes, 15, 0, 45, RollOverflowBehaviorRoll);
  273. } else if(scene_state->selected_control == FontSelect) {
  274. totp_roll_value_uint8_t(
  275. &scene_state->active_font_index,
  276. 1,
  277. 0,
  278. scene_state->total_fonts_count - 1,
  279. RollOverflowBehaviorRoll);
  280. totp_font_provider_get_font(
  281. scene_state->active_font_index, scene_state->active_font);
  282. } else if(scene_state->selected_control == SoundSwitch) {
  283. scene_state->notification_sound = !scene_state->notification_sound;
  284. } else if(scene_state->selected_control == VibroSwitch) {
  285. scene_state->notification_vibro = !scene_state->notification_vibro;
  286. } else if(scene_state->selected_control == AutomationSwitch) {
  287. totp_roll_value_uint8_t(
  288. &scene_state->automation_method,
  289. 1,
  290. 0,
  291. COUNT_OF(AUTOMATION_LIST) - 1,
  292. RollOverflowBehaviorRoll);
  293. } else if(scene_state->selected_control == BadKeyboardLayoutSelect) {
  294. totp_roll_value_uint8_t(
  295. &scene_state->automation_kb_layout,
  296. 1,
  297. 0,
  298. COUNT_OF(BAD_KB_LAYOUT_LIST) - 1,
  299. RollOverflowBehaviorRoll);
  300. } else if(scene_state->selected_control == AutomationDelaySelect) {
  301. totp_roll_value_uint16_t(
  302. &scene_state->automation_initial_delay,
  303. 500,
  304. 0,
  305. 60000,
  306. RollOverflowBehaviorStop);
  307. update_formatted_automation_initial_delay(scene_state);
  308. }
  309. break;
  310. case InputKeyLeft:
  311. if(scene_state->selected_control == HoursInput) {
  312. totp_roll_value_int8_t(
  313. &scene_state->tz_offset_hours, -1, -12, 12, RollOverflowBehaviorStop);
  314. } else if(scene_state->selected_control == MinutesInput) {
  315. totp_roll_value_uint8_t(
  316. &scene_state->tz_offset_minutes, -15, 0, 45, RollOverflowBehaviorRoll);
  317. } else if(scene_state->selected_control == FontSelect) {
  318. totp_roll_value_uint8_t(
  319. &scene_state->active_font_index,
  320. -1,
  321. 0,
  322. scene_state->total_fonts_count - 1,
  323. RollOverflowBehaviorRoll);
  324. totp_font_provider_get_font(
  325. scene_state->active_font_index, scene_state->active_font);
  326. } else if(scene_state->selected_control == SoundSwitch) {
  327. scene_state->notification_sound = !scene_state->notification_sound;
  328. } else if(scene_state->selected_control == VibroSwitch) {
  329. scene_state->notification_vibro = !scene_state->notification_vibro;
  330. } else if(scene_state->selected_control == AutomationSwitch) {
  331. totp_roll_value_uint8_t(
  332. &scene_state->automation_method,
  333. -1,
  334. 0,
  335. COUNT_OF(AUTOMATION_LIST) - 1,
  336. RollOverflowBehaviorRoll);
  337. } else if(scene_state->selected_control == BadKeyboardLayoutSelect) {
  338. totp_roll_value_uint8_t(
  339. &scene_state->automation_kb_layout,
  340. -1,
  341. 0,
  342. COUNT_OF(BAD_KB_LAYOUT_LIST) - 1,
  343. RollOverflowBehaviorRoll);
  344. } else if(scene_state->selected_control == AutomationDelaySelect) {
  345. totp_roll_value_uint16_t(
  346. &scene_state->automation_initial_delay,
  347. -500,
  348. 0,
  349. 60000,
  350. RollOverflowBehaviorStop);
  351. update_formatted_automation_initial_delay(scene_state);
  352. }
  353. break;
  354. case InputKeyOk:
  355. if(scene_state->selected_control == ConfirmButton) {
  356. plugin_state->timezone_offset = (float)scene_state->tz_offset_hours +
  357. (float)scene_state->tz_offset_minutes / 60.0f;
  358. plugin_state->notification_method =
  359. (scene_state->notification_sound ? NotificationMethodSound :
  360. NotificationMethodNone) |
  361. (scene_state->notification_vibro ? NotificationMethodVibro :
  362. NotificationMethodNone);
  363. plugin_state->automation_method = scene_state->automation_method;
  364. plugin_state->active_font_index = scene_state->active_font_index;
  365. plugin_state->automation_kb_layout = scene_state->automation_kb_layout;
  366. plugin_state->automation_initial_delay = scene_state->automation_initial_delay;
  367. if(!totp_config_file_update_user_settings(plugin_state)) {
  368. totp_dialogs_config_updating_error(plugin_state);
  369. return false;
  370. }
  371. #ifdef TOTP_BADBT_AUTOMATION_ENABLED
  372. if((scene_state->automation_method & AutomationMethodBadBt) == 0 &&
  373. plugin_state->bt_type_code_worker_context != NULL) {
  374. totp_bt_type_code_worker_free(plugin_state->bt_type_code_worker_context);
  375. plugin_state->bt_type_code_worker_context = NULL;
  376. }
  377. #endif
  378. totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
  379. }
  380. break;
  381. case InputKeyBack: {
  382. totp_scene_director_activate_scene(plugin_state, TotpSceneTokenMenu);
  383. break;
  384. }
  385. default:
  386. break;
  387. }
  388. }
  389. return true;
  390. }
  391. void totp_scene_app_settings_deactivate(PluginState* plugin_state) {
  392. if(plugin_state->current_scene_state == NULL) return;
  393. SceneState* scene_state = plugin_state->current_scene_state;
  394. totp_font_info_free(scene_state->active_font);
  395. free(scene_state);
  396. plugin_state->current_scene_state = NULL;
  397. }