xremote_app.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*!
  2. * @file flipper-xremote/xremote_app.c
  3. @license This project is released under the GNU GPLv3 License
  4. * @copyright (c) 2023 Sandro Kalatozishvili (s.kalatoz@gmail.com)
  5. *
  6. * @brief Shared functionality and data types between the apps.
  7. */
  8. #include "xremote_app.h"
  9. #define XREMOTE_APP_SETTINGS ANY_PATH("infrared/assets/xremote.cfg")
  10. #define TAG "XRemoteApp"
  11. XRemoteAppSettings* xremote_app_settings_alloc()
  12. {
  13. XRemoteAppSettings* settings = malloc(sizeof(XRemoteAppSettings));
  14. settings->orientation = ViewOrientationVertical;
  15. settings->repeat_count = 1;
  16. return settings;
  17. }
  18. void xremote_app_settings_free(XRemoteAppSettings* settings)
  19. {
  20. xremote_app_assert_void(settings);
  21. free(settings);
  22. }
  23. bool xremote_app_settings_store(XRemoteAppSettings* settings)
  24. {
  25. Storage* storage = furi_record_open(RECORD_STORAGE);
  26. FlipperFormat* ff = flipper_format_file_alloc(storage);
  27. FURI_LOG_I(TAG, "store config file: \'%s\'", XREMOTE_APP_SETTINGS);
  28. bool vertical = settings->orientation == ViewOrientationVertical;
  29. bool success = false;
  30. do {
  31. /* Write header in config file */
  32. if (!flipper_format_file_open_always(ff, XREMOTE_APP_SETTINGS)) break;
  33. if (!flipper_format_write_header_cstr(ff, "XRemote settings file", 1)) break;
  34. if (!flipper_format_write_comment_cstr(ff, "")) break;
  35. /* Write actual configuration to the settings file */
  36. const char *orientation = vertical ? "vertical" : "horizontal";
  37. if (!flipper_format_write_string_cstr(ff, "orientation", orientation)) break;
  38. if (!flipper_format_write_uint32(ff, "cmd_repeat", &settings->repeat_count, 1)) break;
  39. success = true;
  40. } while(false);
  41. furi_record_close(RECORD_STORAGE);
  42. flipper_format_free(ff);
  43. return success;
  44. }
  45. bool xremote_app_settings_load(XRemoteAppSettings* settings)
  46. {
  47. Storage* storage = furi_record_open(RECORD_STORAGE);
  48. FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
  49. FuriString* header = furi_string_alloc();
  50. FuriString* orient = furi_string_alloc();
  51. FURI_LOG_I(TAG, "load config file: \'%s\'", XREMOTE_APP_SETTINGS);
  52. uint32_t version = 0;
  53. uint32_t repeat = 0;
  54. bool success = false;
  55. do {
  56. /* Open file and read the header */
  57. if (!flipper_format_buffered_file_open_existing(ff, XREMOTE_APP_SETTINGS)) break;
  58. if (!flipper_format_read_header(ff, header, &version)) break;
  59. if (!furi_string_equal(header, "XRemote settings file") || (version != 1)) break;
  60. /* Read config data from the file */
  61. if (!flipper_format_read_string(ff, "orientation", orient)) break;
  62. if (!flipper_format_read_uint32(ff, "cmd_repeat", &repeat, 1)) break;
  63. /* Parse config data from the buffer */
  64. if (furi_string_equal(orient, "vertical"))
  65. settings->orientation = ViewOrientationVertical;
  66. else if (furi_string_equal(orient, "horizontal"))
  67. settings->orientation = ViewOrientationHorizontal;
  68. settings->repeat_count = repeat;
  69. success = true;
  70. } while(false);
  71. furi_record_close(RECORD_STORAGE);
  72. furi_string_free(orient);
  73. furi_string_free(header);
  74. flipper_format_free(ff);
  75. return success;
  76. }
  77. XRemoteAppContext* xremote_app_context_alloc(void *arg)
  78. {
  79. XRemoteAppContext* ctx = malloc(sizeof(XRemoteAppContext));
  80. ctx->app_argument = arg;
  81. /* Open GUI and norification records */
  82. ctx->gui = furi_record_open(RECORD_GUI);
  83. ctx->notifications = furi_record_open(RECORD_NOTIFICATION);
  84. /* Allocate and load global app settings */
  85. ctx->app_settings = xremote_app_settings_alloc();
  86. xremote_app_settings_load(ctx->app_settings);
  87. /* Allocate and setup view dispatcher */
  88. ctx->view_dispatcher = view_dispatcher_alloc();
  89. view_dispatcher_enable_queue(ctx->view_dispatcher);
  90. view_dispatcher_attach_to_gui(ctx->view_dispatcher, ctx->gui, ViewDispatcherTypeFullscreen);
  91. return ctx;
  92. }
  93. void xremote_app_context_free(XRemoteAppContext* ctx)
  94. {
  95. xremote_app_assert_void(ctx);
  96. notification_internal_message(ctx->notifications, &sequence_reset_blue);
  97. xremote_app_settings_free(ctx->app_settings);
  98. view_dispatcher_free(ctx->view_dispatcher);
  99. furi_record_close(RECORD_NOTIFICATION);
  100. furi_record_close(RECORD_GUI);
  101. free(ctx);
  102. }
  103. void xremote_app_view_alloc(XRemoteApp *app, uint32_t view_id, XRemoteViewAllocator allocator)
  104. {
  105. furi_assert(app);
  106. xremote_app_assert_void(app->app_ctx);
  107. if (app->view_id == view_id &&
  108. app->view_ctx != NULL) return;
  109. xremote_app_view_free(app);
  110. app->view_id = view_id;
  111. app->view_ctx = allocator(app->app_ctx->notifications);
  112. View* app_view = xremote_view_get_view(app->view_ctx);
  113. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  114. view_dispatcher_add_view(view_disp, app->view_id, app_view);
  115. }
  116. void xremote_app_view_free(XRemoteApp* app)
  117. {
  118. xremote_app_assert_void(app);
  119. if (app->app_ctx != NULL && app->view_id != XRemoteViewNone)
  120. {
  121. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  122. view_dispatcher_remove_view(view_disp, app->view_id);
  123. app->view_id = XRemoteViewNone;
  124. }
  125. if (app->view_ctx != NULL)
  126. {
  127. xremote_view_free(app->view_ctx);
  128. app->view_ctx = NULL;
  129. }
  130. }
  131. bool xremote_app_has_view(XRemoteApp *app, uint32_t view_id)
  132. {
  133. xremote_app_assert(app, false);
  134. return (app->view_id == view_id ||
  135. app->submenu_id == view_id);
  136. }
  137. void xremote_app_switch_to_view(XRemoteApp *app, uint32_t view_id)
  138. {
  139. furi_assert(app);
  140. xremote_app_assert_void(app->app_ctx);
  141. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  142. view_dispatcher_switch_to_view(view_disp, view_id);
  143. }
  144. void xremote_app_switch_to_submenu(XRemoteApp *app)
  145. {
  146. furi_assert(app);
  147. xremote_app_assert_void(app->app_ctx);
  148. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  149. view_dispatcher_switch_to_view(view_disp, app->submenu_id);
  150. }
  151. void xremote_app_submenu_add(XRemoteApp* app, const char *name, uint32_t index, SubmenuItemCallback callback)
  152. {
  153. furi_assert(app);
  154. xremote_app_assert_void(app->submenu);
  155. submenu_add_item(app->submenu, name, index, callback, app);
  156. }
  157. void xremote_app_submenu_alloc(XRemoteApp* app, uint32_t index, ViewNavigationCallback prev_cb)
  158. {
  159. furi_assert(app);
  160. app->submenu = submenu_alloc();
  161. app->submenu_id = index;
  162. XRemoteAppSettings *settings = app->app_ctx->app_settings;
  163. submenu_set_orientation(app->submenu, settings->orientation);
  164. view_set_previous_callback(submenu_get_view(app->submenu), prev_cb);
  165. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  166. view_dispatcher_add_view(view_disp, app->submenu_id, submenu_get_view(app->submenu));
  167. }
  168. void xremote_app_submenu_free(XRemoteApp *app)
  169. {
  170. xremote_app_assert_void(app);
  171. /* Remove submenu view from dispatcher */
  172. if (app->submenu_id != XRemoteViewNone && app->app_ctx != NULL)
  173. {
  174. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  175. view_dispatcher_remove_view(view_disp, app->submenu_id);
  176. app->submenu_id = XRemoteViewNone;
  177. }
  178. /* Free submenu */
  179. if (app->submenu != NULL)
  180. {
  181. submenu_free(app->submenu);
  182. app->submenu = NULL;
  183. }
  184. }
  185. void xremote_app_view_set_previous_callback(XRemoteApp* app, ViewNavigationCallback callback)
  186. {
  187. furi_assert(app);
  188. xremote_app_assert_void(app->view_ctx);
  189. View* view = xremote_view_get_view(app->view_ctx);
  190. view_set_previous_callback(view, callback);
  191. }
  192. void xremote_app_set_view_context(XRemoteApp* app, void *context, XRemoteViewClearCallback on_clear)
  193. {
  194. furi_assert(app);
  195. xremote_app_assert_void(app->view_ctx);
  196. xremote_view_set_context(app->view_ctx, context, on_clear);
  197. }
  198. void xremote_app_set_user_context(XRemoteApp* app, void *context, XRemoteAppClearCallback on_clear)
  199. {
  200. furi_assert(app);
  201. app->on_clear = on_clear;
  202. app->context = context;
  203. }
  204. void xremote_app_user_context_free(XRemoteApp* app)
  205. {
  206. furi_assert(app);
  207. xremote_app_assert_void(app->context);
  208. xremote_app_assert_void(app->on_clear);
  209. app->on_clear(app->context);
  210. app->on_clear = NULL;
  211. app->context = NULL;
  212. }
  213. XRemoteApp* xremote_app_alloc(XRemoteAppContext *ctx)
  214. {
  215. furi_assert(ctx);
  216. XRemoteApp* app = malloc(sizeof(XRemoteApp));
  217. app->submenu_id = XRemoteViewNone;
  218. app->view_id = XRemoteViewNone;
  219. app->app_ctx = ctx;
  220. app->submenu = NULL;
  221. app->view_ctx = NULL;
  222. app->on_clear = NULL;
  223. app->context = NULL;
  224. return app;
  225. }
  226. void xremote_app_free(XRemoteApp* app)
  227. {
  228. xremote_app_assert_void(app);
  229. xremote_app_submenu_free(app);
  230. xremote_app_view_free(app);
  231. if (app->on_clear != NULL)
  232. app->on_clear(app->context);
  233. free(app);
  234. }