xremote_app.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  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 "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 file: \'%s\'", XREMOTE_APP_SETTINGS);
  28. bool success = false;
  29. do {
  30. if (!flipper_format_file_open_always(ff, XREMOTE_APP_SETTINGS)) break;
  31. if (!flipper_format_write_header_cstr(ff, "XRemote settings file", 1)) break;
  32. if (settings->orientation == ViewOrientationHorizontal &&
  33. !flipper_format_write_string_cstr(ff, "orientation", "horizontal")) break;
  34. else if (settings->orientation == ViewOrientationVertical &&
  35. !flipper_format_write_string_cstr(ff, "orientation", "vertical")) break;
  36. if (!flipper_format_write_uint32(ff, "repeat", &settings->repeat_count, 1)) break;
  37. success = true;
  38. } while(false);
  39. furi_record_close(RECORD_STORAGE);
  40. flipper_format_free(ff);
  41. return success;
  42. }
  43. bool xremote_app_settings_load(XRemoteAppSettings* settings)
  44. {
  45. Storage* storage = furi_record_open(RECORD_STORAGE);
  46. FlipperFormat* ff = flipper_format_buffered_file_alloc(storage);
  47. FuriString* header = furi_string_alloc();
  48. FuriString* orient = furi_string_alloc();
  49. FURI_LOG_I(TAG, "load file: \'%s\'", XREMOTE_APP_SETTINGS);
  50. uint32_t version = 0;
  51. uint32_t repeat = 0;
  52. bool success = false;
  53. do {
  54. if (!flipper_format_buffered_file_open_existing(ff, XREMOTE_APP_SETTINGS)) break;
  55. if (!flipper_format_read_header(ff, header, &version)) break;
  56. if (!furi_string_equal(header, "XRemote settings file") || (version != 1)) break;
  57. if (!flipper_format_read_string(ff, "orientation", orient)) break;
  58. if (!flipper_format_read_uint32(ff, "repeat", &repeat, 1)) break;
  59. if (!furi_string_equal(orient, "vertical"))
  60. settings->orientation = ViewOrientationVertical;
  61. else if (!furi_string_equal(orient, "horizontal"))
  62. settings->orientation = ViewOrientationHorizontal;
  63. settings->repeat_count = repeat;
  64. success = true;
  65. } while(false);
  66. furi_record_close(RECORD_STORAGE);
  67. furi_string_free(orient);
  68. furi_string_free(header);
  69. flipper_format_free(ff);
  70. return success;
  71. }
  72. XRemoteAppContext* xremote_app_context_alloc(void *arg)
  73. {
  74. XRemoteAppContext* ctx = malloc(sizeof(XRemoteAppContext));
  75. ctx->gui = furi_record_open(RECORD_GUI);
  76. ctx->notifications = furi_record_open(RECORD_NOTIFICATION);
  77. ctx->view_dispatcher = view_dispatcher_alloc();
  78. ctx->app_settings = xremote_app_settings_alloc();
  79. ctx->arg = arg;
  80. view_dispatcher_enable_queue(ctx->view_dispatcher);
  81. view_dispatcher_attach_to_gui(ctx->view_dispatcher, ctx->gui, ViewDispatcherTypeFullscreen);
  82. return ctx;
  83. }
  84. void xremote_app_context_free(XRemoteAppContext* ctx)
  85. {
  86. xremote_app_assert_void(ctx);
  87. notification_internal_message(ctx->notifications, &sequence_reset_blue);
  88. xremote_app_settings_free(ctx->app_settings);
  89. view_dispatcher_free(ctx->view_dispatcher);
  90. furi_record_close(RECORD_NOTIFICATION);
  91. furi_record_close(RECORD_GUI);
  92. free(ctx);
  93. }
  94. void xremote_app_view_alloc(XRemoteApp *app, uint32_t view_id, XRemoteViewAllocator allocator)
  95. {
  96. furi_assert(app);
  97. xremote_app_assert_void(app->app_ctx);
  98. if (app->view_id == view_id &&
  99. app->view_ctx != NULL) return;
  100. xremote_app_view_free(app);
  101. app->view_id = view_id;
  102. app->view_ctx = allocator(app->app_ctx->notifications);
  103. View* app_view = xremote_view_get_view(app->view_ctx);
  104. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  105. view_dispatcher_add_view(view_disp, app->view_id, app_view);
  106. }
  107. void xremote_app_view_free(XRemoteApp* app)
  108. {
  109. xremote_app_assert_void(app);
  110. if (app->app_ctx != NULL && app->view_id != XRemoteViewNone)
  111. {
  112. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  113. view_dispatcher_remove_view(view_disp, app->view_id);
  114. app->view_id = XRemoteViewNone;
  115. }
  116. if (app->view_ctx != NULL)
  117. {
  118. xremote_view_free(app->view_ctx);
  119. app->view_ctx = NULL;
  120. }
  121. }
  122. bool xremote_app_has_view(XRemoteApp *app, uint32_t view_id)
  123. {
  124. xremote_app_assert(app, false);
  125. return (app->view_id == view_id ||
  126. app->submenu_id == view_id);
  127. }
  128. void xremote_app_switch_to_view(XRemoteApp *app, uint32_t view_id)
  129. {
  130. furi_assert(app);
  131. xremote_app_assert_void(app->app_ctx);
  132. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  133. view_dispatcher_switch_to_view(view_disp, view_id);
  134. }
  135. void xremote_app_switch_to_submenu(XRemoteApp *app)
  136. {
  137. furi_assert(app);
  138. xremote_app_assert_void(app->app_ctx);
  139. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  140. view_dispatcher_switch_to_view(view_disp, app->submenu_id);
  141. }
  142. void xremote_app_submenu_add(XRemoteApp* app, const char *name, uint32_t index, SubmenuItemCallback callback)
  143. {
  144. furi_assert(app);
  145. xremote_app_assert_void(app->submenu);
  146. submenu_add_item(app->submenu, name, index, callback, app);
  147. }
  148. void xremote_app_submenu_alloc(XRemoteApp* app, uint32_t index, ViewNavigationCallback prev_cb)
  149. {
  150. furi_assert(app);
  151. app->submenu = submenu_alloc();
  152. app->submenu_id = index;
  153. XRemoteAppSettings *settings = app->app_ctx->app_settings;
  154. submenu_set_orientation(app->submenu, settings->orientation);
  155. view_set_previous_callback(submenu_get_view(app->submenu), prev_cb);
  156. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  157. view_dispatcher_add_view(view_disp, app->submenu_id, submenu_get_view(app->submenu));
  158. }
  159. void xremote_app_submenu_free(XRemoteApp *app)
  160. {
  161. xremote_app_assert_void(app);
  162. /* Remove submenu view from dispatcher */
  163. if (app->submenu_id != XRemoteViewNone && app->app_ctx != NULL)
  164. {
  165. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  166. view_dispatcher_remove_view(view_disp, app->submenu_id);
  167. app->submenu_id = XRemoteViewNone;
  168. }
  169. /* Free submenu */
  170. if (app->submenu != NULL)
  171. {
  172. submenu_free(app->submenu);
  173. app->submenu = NULL;
  174. }
  175. }
  176. void xremote_app_view_set_previous_callback(XRemoteApp* app, ViewNavigationCallback callback)
  177. {
  178. furi_assert(app);
  179. xremote_app_assert_void(app->view_ctx);
  180. View* view = xremote_view_get_view(app->view_ctx);
  181. view_set_previous_callback(view, callback);
  182. }
  183. void xremote_app_set_view_context(XRemoteApp* app, void *context, XRemoteViewClearCallback on_clear)
  184. {
  185. furi_assert(app);
  186. xremote_app_assert_void(app->view_ctx);
  187. xremote_view_set_context(app->view_ctx, context, on_clear);
  188. }
  189. void xremote_app_set_user_context(XRemoteApp* app, void *context, XRemoteAppClearCallback on_clear)
  190. {
  191. furi_assert(app);
  192. app->on_clear = on_clear;
  193. app->context = context;
  194. }
  195. void xremote_app_user_context_free(XRemoteApp* app)
  196. {
  197. furi_assert(app);
  198. xremote_app_assert_void(app->context);
  199. xremote_app_assert_void(app->on_clear);
  200. app->on_clear(app->context);
  201. app->on_clear = NULL;
  202. app->context = NULL;
  203. }
  204. XRemoteApp* xremote_app_alloc(XRemoteAppContext *ctx)
  205. {
  206. furi_assert(ctx);
  207. XRemoteApp* app = malloc(sizeof(XRemoteApp));
  208. app->submenu_id = XRemoteViewNone;
  209. app->view_id = XRemoteViewNone;
  210. app->app_ctx = ctx;
  211. app->submenu = NULL;
  212. app->view_ctx = NULL;
  213. app->on_clear = NULL;
  214. app->context = NULL;
  215. return app;
  216. }
  217. void xremote_app_free(XRemoteApp* app)
  218. {
  219. xremote_app_assert_void(app);
  220. xremote_app_submenu_free(app);
  221. xremote_app_view_free(app);
  222. if (app->on_clear != NULL)
  223. app->on_clear(app->context);
  224. free(app);
  225. }