xremote_app.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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 = ViewOrientationHorizontal;
  15. settings->exit_behavior = XRemoteAppExitPress;
  16. settings->repeat_count = 2;
  17. return settings;
  18. }
  19. void xremote_app_settings_free(XRemoteAppSettings* settings)
  20. {
  21. xremote_app_assert_void(settings);
  22. free(settings);
  23. }
  24. bool xremote_app_settings_store(XRemoteAppSettings* settings)
  25. {
  26. Storage* storage = furi_record_open(RECORD_STORAGE);
  27. FlipperFormat* ff = flipper_format_file_alloc(storage);
  28. FURI_LOG_I(TAG, "store config file: \'%s\'", XREMOTE_APP_SETTINGS);
  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. if (!flipper_format_write_uint32(ff, "orientation", (uint32_t*)&settings->orientation, 1)) break;
  37. if (!flipper_format_write_uint32(ff, "repeat", (uint32_t*)&settings->repeat_count, 1)) break;
  38. if (!flipper_format_write_uint32(ff, "exit", (uint32_t*)&settings->exit_behavior, 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. FURI_LOG_I(TAG, "load config file: \'%s\'", XREMOTE_APP_SETTINGS);
  51. uint32_t version = 0;
  52. uint32_t value = 0;
  53. bool success = false;
  54. do {
  55. /* Open file and read the header */
  56. if (!flipper_format_buffered_file_open_existing(ff, XREMOTE_APP_SETTINGS)) break;
  57. if (!flipper_format_read_header(ff, header, &version)) break;
  58. if (!furi_string_equal(header, "XRemote settings file") || (version != 1)) break;
  59. /* Parse config data from the buffer */
  60. if (!flipper_format_read_uint32(ff, "orientation", &value, 1)) break;
  61. settings->orientation = value;
  62. if (!flipper_format_read_uint32(ff, "repeat", &value, 1)) break;
  63. settings->repeat_count = value;
  64. if (!flipper_format_read_uint32(ff, "exit", &value, 1)) break;
  65. settings->exit_behavior = value;
  66. success = true;
  67. } while(false);
  68. furi_record_close(RECORD_STORAGE);
  69. furi_string_free(header);
  70. flipper_format_free(ff);
  71. return success;
  72. }
  73. XRemoteAppContext* xremote_app_context_alloc(void *arg)
  74. {
  75. XRemoteAppContext* ctx = malloc(sizeof(XRemoteAppContext));
  76. ctx->app_argument = arg;
  77. /* Open GUI and norification records */
  78. ctx->gui = furi_record_open(RECORD_GUI);
  79. ctx->notifications = furi_record_open(RECORD_NOTIFICATION);
  80. /* Allocate and load global app settings */
  81. ctx->app_settings = xremote_app_settings_alloc();
  82. xremote_app_settings_load(ctx->app_settings);
  83. /* Last opened file or directory path */
  84. ctx->file_path = furi_string_alloc();
  85. furi_string_set(ctx->file_path, XREMOTE_APP_FOLDER);
  86. /* Allocate and setup view dispatcher */
  87. ctx->view_dispatcher = view_dispatcher_alloc();
  88. view_dispatcher_enable_queue(ctx->view_dispatcher);
  89. view_dispatcher_attach_to_gui(ctx->view_dispatcher, ctx->gui, ViewDispatcherTypeFullscreen);
  90. return ctx;
  91. }
  92. void xremote_app_context_free(XRemoteAppContext* ctx)
  93. {
  94. xremote_app_assert_void(ctx);
  95. notification_internal_message(ctx->notifications, &sequence_reset_blue);
  96. xremote_app_settings_free(ctx->app_settings);
  97. view_dispatcher_free(ctx->view_dispatcher);
  98. furi_record_close(RECORD_NOTIFICATION);
  99. furi_record_close(RECORD_GUI);
  100. furi_string_free(ctx->file_path);
  101. free(ctx);
  102. }
  103. const char* xremote_app_context_get_exit_str(XRemoteAppContext* ctx)
  104. {
  105. XRemoteAppExit exit_behavior = ctx->app_settings->exit_behavior;
  106. return exit_behavior == XRemoteAppExitHold ? "Hold to exit" : "Press to exit";
  107. }
  108. void xremote_app_view_alloc(XRemoteApp *app, uint32_t view_id, XRemoteViewAllocator allocator)
  109. {
  110. furi_assert(app);
  111. xremote_app_assert_void(app->app_ctx);
  112. if (app->view_id == view_id &&
  113. app->view_ctx != NULL) return;
  114. xremote_app_view_free(app);
  115. app->view_id = view_id;
  116. app->view_ctx = allocator(app->app_ctx);
  117. View* app_view = xremote_view_get_view(app->view_ctx);
  118. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  119. view_dispatcher_add_view(view_disp, app->view_id, app_view);
  120. }
  121. void xremote_app_view_free(XRemoteApp* app)
  122. {
  123. xremote_app_assert_void(app);
  124. if (app->app_ctx != NULL && app->view_id != XRemoteViewNone)
  125. {
  126. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  127. view_dispatcher_remove_view(view_disp, app->view_id);
  128. app->view_id = XRemoteViewNone;
  129. }
  130. if (app->view_ctx != NULL)
  131. {
  132. xremote_view_free(app->view_ctx);
  133. app->view_ctx = NULL;
  134. }
  135. }
  136. bool xremote_app_has_view(XRemoteApp *app, uint32_t view_id)
  137. {
  138. xremote_app_assert(app, false);
  139. return (app->view_id == view_id ||
  140. app->submenu_id == view_id);
  141. }
  142. void xremote_app_switch_to_view(XRemoteApp *app, uint32_t view_id)
  143. {
  144. furi_assert(app);
  145. xremote_app_assert_void(app->app_ctx);
  146. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  147. view_dispatcher_switch_to_view(view_disp, view_id);
  148. }
  149. void xremote_app_switch_to_submenu(XRemoteApp *app)
  150. {
  151. furi_assert(app);
  152. xremote_app_assert_void(app->app_ctx);
  153. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  154. view_dispatcher_switch_to_view(view_disp, app->submenu_id);
  155. }
  156. void xremote_app_submenu_add(XRemoteApp* app, const char *name, uint32_t index, SubmenuItemCallback callback)
  157. {
  158. furi_assert(app);
  159. xremote_app_assert_void(app->submenu);
  160. submenu_add_item(app->submenu, name, index, callback, app);
  161. }
  162. void xremote_app_submenu_alloc(XRemoteApp* app, uint32_t index, ViewNavigationCallback prev_cb)
  163. {
  164. furi_assert(app);
  165. app->submenu = submenu_alloc();
  166. app->submenu_id = index;
  167. XRemoteAppSettings *settings = app->app_ctx->app_settings;
  168. submenu_set_orientation(app->submenu, settings->orientation);
  169. view_set_previous_callback(submenu_get_view(app->submenu), prev_cb);
  170. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  171. view_dispatcher_add_view(view_disp, app->submenu_id, submenu_get_view(app->submenu));
  172. }
  173. void xremote_app_submenu_free(XRemoteApp *app)
  174. {
  175. xremote_app_assert_void(app);
  176. /* Remove submenu view from dispatcher */
  177. if (app->submenu_id != XRemoteViewNone && app->app_ctx != NULL)
  178. {
  179. ViewDispatcher* view_disp = app->app_ctx->view_dispatcher;
  180. view_dispatcher_remove_view(view_disp, app->submenu_id);
  181. app->submenu_id = XRemoteViewNone;
  182. }
  183. /* Free submenu */
  184. if (app->submenu != NULL)
  185. {
  186. submenu_free(app->submenu);
  187. app->submenu = NULL;
  188. }
  189. }
  190. void xremote_app_view_set_previous_callback(XRemoteApp* app, ViewNavigationCallback callback)
  191. {
  192. furi_assert(app);
  193. xremote_app_assert_void(app->view_ctx);
  194. View* view = xremote_view_get_view(app->view_ctx);
  195. view_set_previous_callback(view, callback);
  196. }
  197. void xremote_app_set_view_context(XRemoteApp* app, void *context, XRemoteViewClearCallback on_clear)
  198. {
  199. furi_assert(app);
  200. xremote_app_assert_void(app->view_ctx);
  201. xremote_view_set_context(app->view_ctx, context, on_clear);
  202. }
  203. void xremote_app_set_user_context(XRemoteApp* app, void *context, XRemoteAppClearCallback on_clear)
  204. {
  205. furi_assert(app);
  206. app->on_clear = on_clear;
  207. app->context = context;
  208. }
  209. void xremote_app_user_context_free(XRemoteApp* app)
  210. {
  211. furi_assert(app);
  212. xremote_app_assert_void(app->context);
  213. xremote_app_assert_void(app->on_clear);
  214. app->on_clear(app->context);
  215. app->on_clear = NULL;
  216. app->context = NULL;
  217. }
  218. XRemoteApp* xremote_app_alloc(XRemoteAppContext *ctx)
  219. {
  220. furi_assert(ctx);
  221. XRemoteApp* app = malloc(sizeof(XRemoteApp));
  222. app->submenu_id = XRemoteViewNone;
  223. app->view_id = XRemoteViewNone;
  224. app->app_ctx = ctx;
  225. app->submenu = NULL;
  226. app->view_ctx = NULL;
  227. app->on_clear = NULL;
  228. app->context = NULL;
  229. return app;
  230. }
  231. void xremote_app_free(XRemoteApp* app)
  232. {
  233. xremote_app_assert_void(app);
  234. xremote_app_submenu_free(app);
  235. xremote_app_view_free(app);
  236. if (app->on_clear != NULL)
  237. app->on_clear(app->context);
  238. free(app);
  239. }