scene_items.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. #include <furi.h>
  2. #include <gui/view_dispatcher.h>
  3. #include <gui/scene_manager.h>
  4. #include <gui/modules/dialog_ex.h>
  5. #include <notification/notification_messages.h>
  6. #include "quac.h"
  7. #include "scenes.h"
  8. #include "scene_items.h"
  9. #include "../actions/action.h"
  10. #include "../views/action_menu.h"
  11. #include <lib/toolbox/path.h>
  12. static const ActionMenuItemType ItemToMenuItem[] = {
  13. [Item_SubGhz] = ActionMenuItemTypeSubGHz,
  14. [Item_RFID] = ActionMenuItemTypeRFID,
  15. [Item_IR] = ActionMenuItemTypeIR,
  16. [Item_NFC] = ActionMenuItemTypeNFC,
  17. [Item_iButton] = ActionMenuItemTypeiButton,
  18. [Item_Playlist] = ActionMenuItemTypePlaylist,
  19. [Item_Group] = ActionMenuItemTypeGroup,
  20. [Item_Settings] = ActionMenuItemTypeSettings,
  21. [Item_Unknown] = ActionMenuItemTypeUnknown,
  22. };
  23. void scene_items_item_callback(void* context, int32_t index, InputType type) {
  24. App* app = context;
  25. // FURI_LOG_I(TAG, "scene_items callback, type == %s", input_get_type_name(type));
  26. if(type == InputTypeShort) {
  27. app->selected_item = index;
  28. view_dispatcher_send_custom_event(app->view_dispatcher, Event_ButtonPressed);
  29. } else if(type == InputTypeLong) {
  30. app->selected_item = index;
  31. view_dispatcher_send_custom_event(app->view_dispatcher, Event_ButtonPressedLong);
  32. } else {
  33. // do nothing
  34. }
  35. }
  36. // For each scene, implement handler callbacks
  37. void scene_items_on_enter(void* context) {
  38. App* app = context;
  39. ActionMenu* menu = app->action_menu;
  40. action_menu_reset(menu);
  41. if(app->settings.layout == QUAC_APP_LANDSCAPE)
  42. action_menu_set_layout(menu, ActionMenuLayoutLandscape);
  43. else
  44. action_menu_set_layout(menu, ActionMenuLayoutPortrait);
  45. action_menu_set_show_icons(menu, app->settings.show_icons);
  46. action_menu_set_show_headers(menu, app->settings.show_headers);
  47. ItemsView* items_view = app->items_view;
  48. FURI_LOG_I(
  49. TAG, "Generating scene: [depth=%d] %s", app->depth, furi_string_get_cstr(items_view->path));
  50. action_menu_set_header(menu, furi_string_get_cstr(items_view->name));
  51. size_t item_view_size = ItemArray_size(items_view->items);
  52. if(item_view_size > 0) {
  53. ItemArray_it_t iter;
  54. int32_t index = 0;
  55. for(ItemArray_it(iter, items_view->items); !ItemArray_end_p(iter);
  56. ItemArray_next(iter), ++index) {
  57. const Item* item = ItemArray_cref(iter);
  58. const char* label = furi_string_get_cstr(item->name);
  59. ActionMenuItemType type = ItemToMenuItem[item->type];
  60. ActionMenuItem* menu_item =
  61. action_menu_add_item(menu, label, index, scene_items_item_callback, type, app);
  62. action_menu_item_set_link(menu_item, item->is_link);
  63. }
  64. } else {
  65. FURI_LOG_W(TAG, "No items for: %s", furi_string_get_cstr(items_view->path));
  66. // Add a bogus item - this lets the user still access the Action menu to import, etc
  67. action_menu_add_item(
  68. menu,
  69. "<Empty, Hold Right>",
  70. EMPTY_ACTION_INDEX,
  71. scene_items_item_callback,
  72. ActionMenuItemTypeGroup,
  73. app);
  74. }
  75. // Always add the "Settings" item at the end of our list - but only at top level!
  76. if(app->depth == 0) {
  77. action_menu_add_item(
  78. menu,
  79. "Settings",
  80. item_view_size, // last item!
  81. scene_items_item_callback,
  82. ActionMenuItemTypeSettings,
  83. app);
  84. }
  85. view_dispatcher_switch_to_view(app->view_dispatcher, QView_ActionMenu);
  86. }
  87. bool scene_items_on_event(void* context, SceneManagerEvent event) {
  88. App* app = context;
  89. bool consumed = false;
  90. switch(event.type) {
  91. case SceneManagerEventTypeCustom:
  92. if(event.event == Event_ButtonPressed && app->selected_item != EMPTY_ACTION_INDEX) {
  93. consumed = true;
  94. // FURI_LOG_I(TAG, "button pressed is %d", app->selected_item);
  95. if(app->selected_item < (int)ItemArray_size(app->items_view->items)) {
  96. Item* item = ItemArray_get(app->items_view->items, app->selected_item);
  97. if(item->type == Item_Group) {
  98. app->depth++;
  99. ItemsView* new_items = item_get_items_view_from_path(app, item->path);
  100. item_items_view_free(app->items_view);
  101. app->items_view = new_items;
  102. scene_manager_next_scene(app->scene_manager, QScene_Items);
  103. } else {
  104. FURI_LOG_I(
  105. TAG, "Initiating item action: %s", furi_string_get_cstr(item->name));
  106. // LED goes blinky blinky
  107. App* app = context;
  108. notification_message(app->notifications, &sequence_blink_start_blue);
  109. // Prepare error string for action calls
  110. FuriString* error;
  111. error = furi_string_alloc();
  112. action_tx(app, item, error);
  113. if(furi_string_size(error)) {
  114. FURI_LOG_E(TAG, furi_string_get_cstr(error));
  115. // Fire up the LED and vibrate!
  116. notification_message(app->notifications, &sequence_error);
  117. }
  118. furi_string_free(error);
  119. // Turn off LED light
  120. notification_message(app->notifications, &sequence_blink_stop);
  121. }
  122. } else {
  123. // FURI_LOG_I(TAG, "Selected Settings!");
  124. // TODO: Do we need to free this current items_view??
  125. scene_manager_next_scene(app->scene_manager, QScene_Settings);
  126. }
  127. } else if(event.event == Event_ButtonPressedLong) {
  128. if(app->selected_item < (int)ItemArray_size(app->items_view->items)) {
  129. consumed = true;
  130. scene_manager_next_scene(app->scene_manager, QScene_ActionSettings);
  131. }
  132. }
  133. break;
  134. case SceneManagerEventTypeBack:
  135. // FURI_LOG_I(TAG, "Back button pressed!");
  136. consumed = false; // Ensure Back event continues to propagate
  137. if(app->depth > 0) {
  138. // take our current ItemsView path, and go back up a level
  139. FuriString* parent_path;
  140. parent_path = furi_string_alloc();
  141. path_extract_dirname(furi_string_get_cstr(app->items_view->path), parent_path);
  142. app->depth--;
  143. ItemsView* new_items = item_get_items_view_from_path(app, parent_path);
  144. item_items_view_free(app->items_view);
  145. app->items_view = new_items;
  146. furi_string_free(parent_path);
  147. } else {
  148. // FURI_LOG_I(TAG, "At the root level!");
  149. }
  150. break;
  151. default:
  152. FURI_LOG_I(TAG, "Custom event not handled");
  153. break;
  154. }
  155. // FURI_LOG_I(TAG, "Generic event not handled");
  156. return consumed;
  157. }
  158. void scene_items_on_exit(void* context) {
  159. App* app = context;
  160. ActionMenu* menu = app->action_menu;
  161. action_menu_reset(menu);
  162. }