diff --git a/applications/external/camera-suite/application.fam b/applications/external/camera-suite/application.fam new file mode 100644 index 000000000..d2beefcde --- /dev/null +++ b/applications/external/camera-suite/application.fam @@ -0,0 +1,16 @@ +App( + appid="camerasuite", + apptype=FlipperAppType.EXTERNAL, + cdefines=["APP_CAMERA_SUITE"], + entry_point="camera_suite_app", + fap_author="Cody Tolene", + fap_category="GPIO", + fap_description="A camera suite application for the Flipper Zero ESP32-CAM module.", + fap_icon="icons/camera-suite.png", + fap_libs=["assets"], + fap_weburl="https://github.com/CodyTolene/Flipper-Zero-Cam", + name="[ESP32] Camera Suite", + order=1, + requires=["gui", "storage"], + stack_size=8 * 1024 +) \ No newline at end of file diff --git a/applications/external/camera-suite/camera-suite.c b/applications/external/camera-suite/camera-suite.c new file mode 100644 index 000000000..361bfef4e --- /dev/null +++ b/applications/external/camera-suite/camera-suite.c @@ -0,0 +1,139 @@ +#include "camera-suite.h" +#include + +bool camera_suite_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + CameraSuite* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +void camera_suite_tick_event_callback(void* context) { + furi_assert(context); + CameraSuite* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +//leave app if back button pressed +bool camera_suite_navigation_event_callback(void* context) { + furi_assert(context); + CameraSuite* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +CameraSuite* camera_suite_app_alloc() { + CameraSuite* app = malloc(sizeof(CameraSuite)); + app->gui = furi_record_open(RECORD_GUI); + app->notification = furi_record_open(RECORD_NOTIFICATION); + + //Turn backlight on, believe me this makes testing your app easier + notification_message(app->notification, &sequence_display_backlight_on); + + //Scene additions + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&camera_suite_scene_handlers, app); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, camera_suite_navigation_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, camera_suite_tick_event_callback, 100); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, camera_suite_custom_event_callback); + app->submenu = submenu_alloc(); + + // Set defaults, in case no config loaded + app->orientation = 0; // Orientation is "portrait", zero degrees by default. + app->haptic = 1; // Haptic is on by default + app->speaker = 1; // Speaker is on by default + app->led = 1; // LED is on by default + + // Load configs + camera_suite_read_settings(app); + + view_dispatcher_add_view( + app->view_dispatcher, CameraSuiteViewIdMenu, submenu_get_view(app->submenu)); + + app->camera_suite_view_start = camera_suite_view_start_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + CameraSuiteViewIdStartscreen, + camera_suite_view_start_get_view(app->camera_suite_view_start)); + + app->camera_suite_view_style_1 = camera_suite_view_style_1_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + CameraSuiteViewIdScene1, + camera_suite_view_style_1_get_view(app->camera_suite_view_style_1)); + + app->camera_suite_view_style_2 = camera_suite_view_style_2_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + CameraSuiteViewIdScene2, + camera_suite_view_style_2_get_view(app->camera_suite_view_style_2)); + + app->camera_suite_view_guide = camera_suite_view_guide_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + CameraSuiteViewIdGuide, + camera_suite_view_guide_get_view(app->camera_suite_view_guide)); + + app->button_menu = button_menu_alloc(); + + app->variable_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + CameraSuiteViewIdSettings, + variable_item_list_get_view(app->variable_item_list)); + + //End Scene Additions + + return app; +} + +void camera_suite_app_free(CameraSuite* app) { + furi_assert(app); + + // Scene manager + scene_manager_free(app->scene_manager); + + // View Dispatcher + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdMenu); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene1); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdScene2); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdGuide); + view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdSettings); + submenu_free(app->submenu); + + view_dispatcher_free(app->view_dispatcher); + furi_record_close(RECORD_GUI); + + // Free remaining resources + camera_suite_view_start_free(app->camera_suite_view_start); + camera_suite_view_style_1_free(app->camera_suite_view_style_1); + camera_suite_view_style_2_free(app->camera_suite_view_style_2); + camera_suite_view_guide_free(app->camera_suite_view_guide); + button_menu_free(app->button_menu); + variable_item_list_free(app->variable_item_list); + + app->gui = NULL; + app->notification = NULL; + + //Remove whatever is left + free(app); +} + +/** Main entry point for initialization. */ +int32_t camera_suite_app(void* p) { + UNUSED(p); + CameraSuite* app = camera_suite_app_alloc(); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // Init with start scene. + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStart); + furi_hal_power_suppress_charge_enter(); + view_dispatcher_run(app->view_dispatcher); + camera_suite_save_settings(app); + furi_hal_power_suppress_charge_exit(); + camera_suite_app_free(app); + return 0; +} diff --git a/applications/external/camera-suite/camera-suite.h b/applications/external/camera-suite/camera-suite.h new file mode 100644 index 000000000..2b6a7748b --- /dev/null +++ b/applications/external/camera-suite/camera-suite.h @@ -0,0 +1,71 @@ +#pragma once + +#include "helpers/camera_suite_storage.h" +#include "scenes/camera_suite_scene.h" +#include "views/camera_suite_view_guide.h" +#include "views/camera_suite_view_start.h" +#include "views/camera_suite_view_style_1.h" +#include "views/camera_suite_view_style_2.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TAG "Camera Suite" + +typedef struct { + Gui* gui; + NotificationApp* notification; + ViewDispatcher* view_dispatcher; + Submenu* submenu; + SceneManager* scene_manager; + VariableItemList* variable_item_list; + CameraSuiteViewStart* camera_suite_view_start; + CameraSuiteViewStyle1* camera_suite_view_style_1; + CameraSuiteViewStyle2* camera_suite_view_style_2; + CameraSuiteViewGuide* camera_suite_view_guide; + uint32_t orientation; + uint32_t haptic; + uint32_t speaker; + uint32_t led; + ButtonMenu* button_menu; +} CameraSuite; + +typedef enum { + CameraSuiteViewIdStartscreen, + CameraSuiteViewIdMenu, + CameraSuiteViewIdScene1, + CameraSuiteViewIdScene2, + CameraSuiteViewIdGuide, + CameraSuiteViewIdSettings, +} CameraSuiteViewId; + +typedef enum { + CameraSuiteOrientation0, + CameraSuiteOrientation90, + CameraSuiteOrientation180, + CameraSuiteOrientation270, +} CameraSuiteOrientationState; + +typedef enum { + CameraSuiteHapticOff, + CameraSuiteHapticOn, +} CameraSuiteHapticState; + +typedef enum { + CameraSuiteSpeakerOff, + CameraSuiteSpeakerOn, +} CameraSuiteSpeakerState; + +typedef enum { + CameraSuiteLedOff, + CameraSuiteLedOn, +} CameraSuiteLedState; diff --git a/applications/external/camera-suite/helpers/camera_suite_custom_event.h b/applications/external/camera-suite/helpers/camera_suite_custom_event.h new file mode 100644 index 000000000..7727a0de3 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_custom_event.h @@ -0,0 +1,74 @@ +#pragma once + +typedef enum { + // Scene events: Start menu + CameraSuiteCustomEventStartUp, + CameraSuiteCustomEventStartDown, + CameraSuiteCustomEventStartLeft, + CameraSuiteCustomEventStartRight, + CameraSuiteCustomEventStartOk, + CameraSuiteCustomEventStartBack, + // Scene events: Camera style 1 + CameraSuiteCustomEventSceneStyle1Up, + CameraSuiteCustomEventSceneStyle1Down, + CameraSuiteCustomEventSceneStyle1Left, + CameraSuiteCustomEventSceneStyle1Right, + CameraSuiteCustomEventSceneStyle1Ok, + CameraSuiteCustomEventSceneStyle1Back, + // Scene events: Camera style 2 + CameraSuiteCustomEventSceneStyle2Up, + CameraSuiteCustomEventSceneStyle2Down, + CameraSuiteCustomEventSceneStyle2Left, + CameraSuiteCustomEventSceneStyle2Right, + CameraSuiteCustomEventSceneStyle2Ok, + CameraSuiteCustomEventSceneStyle2Back, + // Scene events: Guide + CameraSuiteCustomEventSceneGuideUp, + CameraSuiteCustomEventSceneGuideDown, + CameraSuiteCustomEventSceneGuideLeft, + CameraSuiteCustomEventSceneGuideRight, + CameraSuiteCustomEventSceneGuideOk, + CameraSuiteCustomEventSceneGuideBack, +} CameraSuiteCustomEvent; + +enum CameraSuiteCustomEventType { + // Reserve first 100 events for button types and indexes, starting from 0. + CameraSuiteCustomEventMenuVoid, + CameraSuiteCustomEventMenuSelected, +}; + +#pragma pack(push, 1) + +typedef union { + uint32_t packed_value; + struct { + uint16_t type; + int16_t value; + } content; +} CameraSuiteCustomEventMenu; + +#pragma pack(pop) + +static inline uint32_t camera_suite_custom_menu_event_pack(uint16_t type, int16_t value) { + CameraSuiteCustomEventMenu event = {.content = {.type = type, .value = value}}; + return event.packed_value; +} + +static inline void + camera_suite_custom_menu_event_unpack(uint32_t packed_value, uint16_t* type, int16_t* value) { + CameraSuiteCustomEventMenu event = {.packed_value = packed_value}; + if(type) *type = event.content.type; + if(value) *value = event.content.value; +} + +static inline uint16_t camera_suite_custom_menu_event_get_type(uint32_t packed_value) { + uint16_t type; + camera_suite_custom_menu_event_unpack(packed_value, &type, NULL); + return type; +} + +static inline int16_t camera_suite_custom_menu_event_get_value(uint32_t packed_value) { + int16_t value; + camera_suite_custom_menu_event_unpack(packed_value, NULL, &value); + return value; +} diff --git a/applications/external/camera-suite/helpers/camera_suite_haptic.c b/applications/external/camera-suite/helpers/camera_suite_haptic.c new file mode 100644 index 000000000..27ea81868 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_haptic.c @@ -0,0 +1,35 @@ +#include "camera_suite_haptic.h" +#include "../camera-suite.h" + +void camera_suite_play_happy_bump(void* context) { + CameraSuite* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 20); + notification_message(app->notification, &sequence_reset_vibro); +} + +void camera_suite_play_bad_bump(void* context) { + CameraSuite* app = context; + if(app->haptic != 1) { + return; + } + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + notification_message(app->notification, &sequence_reset_vibro); +} + +void camera_suite_play_long_bump(void* context) { + CameraSuite* app = context; + if(app->haptic != 1) { + return; + } + for(int i = 0; i < 4; i++) { + notification_message(app->notification, &sequence_set_vibro_on); + furi_thread_flags_wait(0, FuriFlagWaitAny, 50); + notification_message(app->notification, &sequence_reset_vibro); + furi_thread_flags_wait(0, FuriFlagWaitAny, 100); + } +} diff --git a/applications/external/camera-suite/helpers/camera_suite_haptic.h b/applications/external/camera-suite/helpers/camera_suite_haptic.h new file mode 100644 index 000000000..9b7651f97 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_haptic.h @@ -0,0 +1,7 @@ +#include + +void camera_suite_play_happy_bump(void* context); + +void camera_suite_play_bad_bump(void* context); + +void camera_suite_play_long_bump(void* context); diff --git a/applications/external/camera-suite/helpers/camera_suite_led.c b/applications/external/camera-suite/helpers/camera_suite_led.c new file mode 100644 index 000000000..c7211a996 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_led.c @@ -0,0 +1,38 @@ +#include "camera_suite_led.h" +#include "../camera-suite.h" + +void camera_suite_led_set_rgb(void* context, int red, int green, int blue) { + CameraSuite* app = context; + if(app->led != 1) { + return; + } + NotificationMessage notification_led_message_1; + notification_led_message_1.type = NotificationMessageTypeLedRed; + NotificationMessage notification_led_message_2; + notification_led_message_2.type = NotificationMessageTypeLedGreen; + NotificationMessage notification_led_message_3; + notification_led_message_3.type = NotificationMessageTypeLedBlue; + + notification_led_message_1.data.led.value = red; + notification_led_message_2.data.led.value = green; + notification_led_message_3.data.led.value = blue; + const NotificationSequence notification_sequence = { + ¬ification_led_message_1, + ¬ification_led_message_2, + ¬ification_led_message_3, + &message_do_not_reset, + NULL, + }; + notification_message(app->notification, ¬ification_sequence); + //Delay, prevent removal from RAM before LED value set. + furi_thread_flags_wait(0, FuriFlagWaitAny, 10); +} + +void camera_suite_led_reset(void* context) { + CameraSuite* app = context; + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + //Delay, prevent removal from RAM before LED value set. + furi_thread_flags_wait(0, FuriFlagWaitAny, 300); +} diff --git a/applications/external/camera-suite/helpers/camera_suite_led.h b/applications/external/camera-suite/helpers/camera_suite_led.h new file mode 100644 index 000000000..074947da1 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_led.h @@ -0,0 +1,3 @@ +void camera_suite_led_set_rgb(void* context, int red, int green, int blue); + +void camera_suite_led_reset(void* context); diff --git a/applications/external/camera-suite/helpers/camera_suite_speaker.c b/applications/external/camera-suite/helpers/camera_suite_speaker.c new file mode 100644 index 000000000..35d712109 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_speaker.c @@ -0,0 +1,26 @@ +#include "camera_suite_speaker.h" +#include "../camera-suite.h" + +#define NOTE_INPUT 587.33f + +void camera_suite_play_input_sound(void* context) { + CameraSuite* app = context; + if(app->speaker != 1) { + return; + } + float volume = 1.0f; + if(furi_hal_speaker_is_mine() || furi_hal_speaker_acquire(30)) { + furi_hal_speaker_start(NOTE_INPUT, volume); + } +} + +void camera_suite_stop_all_sound(void* context) { + CameraSuite* app = context; + if(app->speaker != 1) { + return; + } + if(furi_hal_speaker_is_mine()) { + furi_hal_speaker_stop(); + furi_hal_speaker_release(); + } +} diff --git a/applications/external/camera-suite/helpers/camera_suite_speaker.h b/applications/external/camera-suite/helpers/camera_suite_speaker.h new file mode 100644 index 000000000..2119bbec5 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_speaker.h @@ -0,0 +1,5 @@ +#define NOTE_INPUT 587.33f + +void camera_suite_play_input_sound(void* context); + +void camera_suite_stop_all_sound(void* context); diff --git a/applications/external/camera-suite/helpers/camera_suite_storage.c b/applications/external/camera-suite/helpers/camera_suite_storage.c new file mode 100644 index 000000000..38a5f0813 --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_storage.c @@ -0,0 +1,113 @@ +#include "camera_suite_storage.h" + +static Storage* camera_suite_open_storage() { + return furi_record_open(RECORD_STORAGE); +} + +static void camera_suite_close_storage() { + furi_record_close(RECORD_STORAGE); +} + +static void camera_suite_close_config_file(FlipperFormat* file) { + if(file == NULL) return; + flipper_format_file_close(file); + flipper_format_free(file); +} + +void camera_suite_save_settings(void* context) { + CameraSuite* app = context; + + FURI_LOG_D(TAG, "Saving Settings"); + Storage* storage = camera_suite_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + // Overwrite wont work, so delete first + if(storage_file_exists(storage, BOILERPLATE_SETTINGS_SAVE_PATH)) { + storage_simply_remove(storage, BOILERPLATE_SETTINGS_SAVE_PATH); + } + + // Open File, create if not exists + if(!storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) == FSE_OK) { + FURI_LOG_D( + TAG, "Config file %s is not found. Will create new.", BOILERPLATE_SETTINGS_SAVE_PATH); + if(storage_common_stat(storage, CONFIG_FILE_DIRECTORY_PATH, NULL) == FSE_NOT_EXIST) { + FURI_LOG_D( + TAG, "Directory %s doesn't exist. Will create new.", CONFIG_FILE_DIRECTORY_PATH); + if(!storage_simply_mkdir(storage, CONFIG_FILE_DIRECTORY_PATH)) { + FURI_LOG_E(TAG, "Error creating directory %s", CONFIG_FILE_DIRECTORY_PATH); + } + } + } + + if(!flipper_format_file_open_new(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { + //totp_close_config_file(fff_file); + FURI_LOG_E(TAG, "Error creating new file %s", BOILERPLATE_SETTINGS_SAVE_PATH); + camera_suite_close_storage(); + return; + } + + // Store Settings + flipper_format_write_header_cstr( + fff_file, BOILERPLATE_SETTINGS_HEADER, BOILERPLATE_SETTINGS_FILE_VERSION); + flipper_format_write_uint32( + fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_write_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); + + if(!flipper_format_rewind(fff_file)) { + camera_suite_close_config_file(fff_file); + FURI_LOG_E(TAG, "Rewind error"); + camera_suite_close_storage(); + return; + } + + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); +} + +void camera_suite_read_settings(void* context) { + CameraSuite* app = context; + Storage* storage = camera_suite_open_storage(); + FlipperFormat* fff_file = flipper_format_file_alloc(storage); + + if(storage_common_stat(storage, BOILERPLATE_SETTINGS_SAVE_PATH, NULL) != FSE_OK) { + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); + return; + } + uint32_t file_version; + FuriString* temp_str = furi_string_alloc(); + + if(!flipper_format_file_open_existing(fff_file, BOILERPLATE_SETTINGS_SAVE_PATH)) { + FURI_LOG_E(TAG, "Cannot open file %s", BOILERPLATE_SETTINGS_SAVE_PATH); + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); + return; + } + + if(!flipper_format_read_header(fff_file, temp_str, &file_version)) { + FURI_LOG_E(TAG, "Missing Header Data"); + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); + return; + } + + if(file_version < BOILERPLATE_SETTINGS_FILE_VERSION) { + FURI_LOG_I(TAG, "old config version, will be removed."); + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); + return; + } + + flipper_format_read_uint32( + fff_file, BOILERPLATE_SETTINGS_KEY_ORIENTATION, &app->orientation, 1); + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_HAPTIC, &app->haptic, 1); + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_SPEAKER, &app->speaker, 1); + flipper_format_read_uint32(fff_file, BOILERPLATE_SETTINGS_KEY_LED, &app->led, 1); + + flipper_format_rewind(fff_file); + + camera_suite_close_config_file(fff_file); + camera_suite_close_storage(); +} \ No newline at end of file diff --git a/applications/external/camera-suite/helpers/camera_suite_storage.h b/applications/external/camera-suite/helpers/camera_suite_storage.h new file mode 100644 index 000000000..e56a4e5ac --- /dev/null +++ b/applications/external/camera-suite/helpers/camera_suite_storage.h @@ -0,0 +1,20 @@ +#include +#include +#include +#include +#include "../camera-suite.h" + +#define BOILERPLATE_SETTINGS_FILE_VERSION 1 +#define CONFIG_FILE_DIRECTORY_PATH EXT_PATH("apps_data/camera-suite") +#define BOILERPLATE_SETTINGS_SAVE_PATH CONFIG_FILE_DIRECTORY_PATH "/camera-suite.conf" +#define BOILERPLATE_SETTINGS_SAVE_PATH_TMP BOILERPLATE_SETTINGS_SAVE_PATH ".tmp" +#define BOILERPLATE_SETTINGS_HEADER "Camera Suite Config File" +#define BOILERPLATE_SETTINGS_KEY_ORIENTATION "Orientation" +#define BOILERPLATE_SETTINGS_KEY_HAPTIC "Haptic" +#define BOILERPLATE_SETTINGS_KEY_LED "Led" +#define BOILERPLATE_SETTINGS_KEY_SPEAKER "Speaker" +#define BOILERPLATE_SETTINGS_KEY_SAVE_SETTINGS "SaveSettings" + +void camera_suite_save_settings(void* context); + +void camera_suite_read_settings(void* context); diff --git a/applications/external/camera-suite/icons/camera-suite.png b/applications/external/camera-suite/icons/camera-suite.png new file mode 100644 index 000000000..cee545bb9 Binary files /dev/null and b/applications/external/camera-suite/icons/camera-suite.png differ diff --git a/applications/external/camera-suite/scenes/camera_suite_scene.c b/applications/external/camera-suite/scenes/camera_suite_scene.c new file mode 100644 index 000000000..c503fab5d --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene.c @@ -0,0 +1,30 @@ +#include "camera_suite_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const camera_suite_on_enter_handlers[])(void*) = { +#include "camera_suite_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const camera_suite_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "camera_suite_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const camera_suite_on_exit_handlers[])(void* context) = { +#include "camera_suite_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers camera_suite_scene_handlers = { + .on_enter_handlers = camera_suite_on_enter_handlers, + .on_event_handlers = camera_suite_on_event_handlers, + .on_exit_handlers = camera_suite_on_exit_handlers, + .scene_num = CameraSuiteSceneNum, +}; diff --git a/applications/external/camera-suite/scenes/camera_suite_scene.h b/applications/external/camera-suite/scenes/camera_suite_scene.h new file mode 100644 index 000000000..2d88b126d --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) CameraSuiteScene##id, +typedef enum { +#include "camera_suite_scene_config.h" + CameraSuiteSceneNum, +} CameraSuiteScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers camera_suite_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "camera_suite_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "camera_suite_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "camera_suite_scene_config.h" +#undef ADD_SCENE diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_config.h b/applications/external/camera-suite/scenes/camera_suite_scene_config.h new file mode 100644 index 000000000..fca73ae16 --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(camera_suite, start, Start) +ADD_SCENE(camera_suite, menu, Menu) +ADD_SCENE(camera_suite, style_1, Style_1) +ADD_SCENE(camera_suite, style_2, Style_2) +ADD_SCENE(camera_suite, guide, Guide) +ADD_SCENE(camera_suite, settings, Settings) \ No newline at end of file diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_guide.c b/applications/external/camera-suite/scenes/camera_suite_scene_guide.c new file mode 100644 index 000000000..12682fb24 --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_guide.c @@ -0,0 +1,51 @@ +#include "../camera-suite.h" +#include "../helpers/camera_suite_custom_event.h" +#include "../views/camera_suite_view_guide.h" + +void camera_suite_view_guide_callback(CameraSuiteCustomEvent event, void* context) { + furi_assert(context); + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void camera_suite_scene_guide_on_enter(void* context) { + furi_assert(context); + CameraSuite* app = context; + camera_suite_view_guide_set_callback( + app->camera_suite_view_guide, camera_suite_view_guide_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdGuide); +} + +bool camera_suite_scene_guide_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case CameraSuiteCustomEventSceneGuideLeft: + case CameraSuiteCustomEventSceneGuideRight: + case CameraSuiteCustomEventSceneGuideUp: + case CameraSuiteCustomEventSceneGuideDown: + // Do nothing. + break; + case CameraSuiteCustomEventSceneGuideBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, CameraSuiteSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void camera_suite_scene_guide_on_exit(void* context) { + CameraSuite* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_menu.c b/applications/external/camera-suite/scenes/camera_suite_scene_menu.c new file mode 100644 index 000000000..538e64252 --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_menu.c @@ -0,0 +1,87 @@ +#include "../camera-suite.h" + +enum SubmenuIndex { + /** Atkinson Dithering Algorithm. */ + SubmenuIndexSceneStyle1 = 10, + /** Floyd-Steinberg Dithering Algorithm. */ + SubmenuIndexSceneStyle2, + /** Guide/how-to. */ + SubmenuIndexGuide, + /** Settings menu. */ + SubmenuIndexSettings, +}; + +void camera_suite_scene_menu_submenu_callback(void* context, uint32_t index) { + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void camera_suite_scene_menu_on_enter(void* context) { + CameraSuite* app = context; + + submenu_add_item( + app->submenu, + "Open Camera", + SubmenuIndexSceneStyle1, + camera_suite_scene_menu_submenu_callback, + app); + // Staged view for the future. + // submenu_add_item( + // app->submenu, + // "Test", + // SubmenuIndexSceneStyle2, + // camera_suite_scene_menu_submenu_callback, + // app); + submenu_add_item( + app->submenu, "Guide", SubmenuIndexGuide, camera_suite_scene_menu_submenu_callback, app); + submenu_add_item( + app->submenu, + "Settings", + SubmenuIndexSettings, + camera_suite_scene_menu_submenu_callback, + app); + + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, CameraSuiteSceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdMenu); +} + +bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + UNUSED(app); + if(event.type == SceneManagerEventTypeBack) { + // Exit application. + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + return true; + } else if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexSceneStyle1) { + scene_manager_set_scene_state( + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle1); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_1); + return true; + } else if(event.event == SubmenuIndexSceneStyle2) { + scene_manager_set_scene_state( + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneStyle2); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneStyle_2); + return true; + } else if(event.event == SubmenuIndexGuide) { + scene_manager_set_scene_state( + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexGuide); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneGuide); + return true; + } else if(event.event == SubmenuIndexSettings) { + scene_manager_set_scene_state( + app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSettings); + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneSettings); + return true; + } + } + return false; +} + +void camera_suite_scene_menu_on_exit(void* context) { + CameraSuite* app = context; + submenu_reset(app->submenu); +} \ No newline at end of file diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_settings.c b/applications/external/camera-suite/scenes/camera_suite_scene_settings.c new file mode 100644 index 000000000..aba80d08c --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_settings.c @@ -0,0 +1,137 @@ +#include "../camera-suite.h" +#include + +// Camera orientation, in degrees. +const char* const orientation_text[4] = { + "0", + "90", + "180", + "270", +}; + +const uint32_t orientation_value[4] = { + CameraSuiteOrientation0, + CameraSuiteOrientation90, + CameraSuiteOrientation180, + CameraSuiteOrientation270, +}; + +const char* const haptic_text[2] = { + "OFF", + "ON", +}; + +const uint32_t haptic_value[2] = { + CameraSuiteHapticOff, + CameraSuiteHapticOn, +}; + +const char* const speaker_text[2] = { + "OFF", + "ON", +}; + +const uint32_t speaker_value[2] = { + CameraSuiteSpeakerOff, + CameraSuiteSpeakerOn, +}; + +const char* const led_text[2] = { + "OFF", + "ON", +}; + +const uint32_t led_value[2] = { + CameraSuiteLedOff, + CameraSuiteLedOn, +}; + +static void camera_suite_scene_settings_set_camera_orientation(VariableItem* item) { + CameraSuite* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, orientation_text[index]); + app->orientation = orientation_value[index]; +} + +static void camera_suite_scene_settings_set_haptic(VariableItem* item) { + CameraSuite* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, haptic_text[index]); + app->haptic = haptic_value[index]; +} + +static void camera_suite_scene_settings_set_speaker(VariableItem* item) { + CameraSuite* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, speaker_text[index]); + app->speaker = speaker_value[index]; +} + +static void camera_suite_scene_settings_set_led(VariableItem* item) { + CameraSuite* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, led_text[index]); + app->led = led_value[index]; +} + +void camera_suite_scene_settings_submenu_callback(void* context, uint32_t index) { + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void camera_suite_scene_settings_on_enter(void* context) { + CameraSuite* app = context; + VariableItem* item; + uint8_t value_index; + + // Camera Orientation + item = variable_item_list_add( + app->variable_item_list, + "Orientation:", + 4, + camera_suite_scene_settings_set_camera_orientation, + app); + value_index = value_index_uint32(app->orientation, orientation_value, 4); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, orientation_text[value_index]); + + // Haptic FX ON/OFF + item = variable_item_list_add( + app->variable_item_list, "Haptic FX:", 2, camera_suite_scene_settings_set_haptic, app); + value_index = value_index_uint32(app->haptic, haptic_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, haptic_text[value_index]); + + // Sound FX ON/OFF + item = variable_item_list_add( + app->variable_item_list, "Sound FX:", 2, camera_suite_scene_settings_set_speaker, app); + value_index = value_index_uint32(app->speaker, speaker_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + + // LED FX ON/OFF + item = variable_item_list_add( + app->variable_item_list, "LED FX:", 2, camera_suite_scene_settings_set_led, app); + value_index = value_index_uint32(app->led, led_value, 2); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, led_text[value_index]); + + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdSettings); +} + +bool camera_suite_scene_settings_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + UNUSED(app); + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + } + return consumed; +} + +void camera_suite_scene_settings_on_exit(void* context) { + CameraSuite* app = context; + variable_item_list_set_selected_item(app->variable_item_list, 0); + variable_item_list_reset(app->variable_item_list); +} \ No newline at end of file diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_start.c b/applications/external/camera-suite/scenes/camera_suite_scene_start.c new file mode 100644 index 000000000..9f128b761 --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_start.c @@ -0,0 +1,55 @@ +#include "../camera-suite.h" +#include "../helpers/camera_suite_custom_event.h" +#include "../views/camera_suite_view_start.h" + +void camera_suite_scene_start_callback(CameraSuiteCustomEvent event, void* context) { + furi_assert(context); + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void camera_suite_scene_start_on_enter(void* context) { + furi_assert(context); + CameraSuite* app = context; + camera_suite_view_start_set_callback( + app->camera_suite_view_start, camera_suite_scene_start_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdStartscreen); +} + +bool camera_suite_scene_start_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case CameraSuiteCustomEventStartLeft: + case CameraSuiteCustomEventStartRight: + break; + case CameraSuiteCustomEventStartUp: + case CameraSuiteCustomEventStartDown: + break; + case CameraSuiteCustomEventStartOk: + scene_manager_next_scene(app->scene_manager, CameraSuiteSceneMenu); + consumed = true; + break; + case CameraSuiteCustomEventStartBack: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, CameraSuiteSceneStart)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void camera_suite_scene_start_on_exit(void* context) { + CameraSuite* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_style_1.c b/applications/external/camera-suite/scenes/camera_suite_scene_style_1.c new file mode 100644 index 000000000..1341c6173 --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_style_1.c @@ -0,0 +1,52 @@ +#include "../camera-suite.h" +#include "../helpers/camera_suite_custom_event.h" +#include "../views/camera_suite_view_style_1.h" + +static void camera_suite_view_style_1_callback(CameraSuiteCustomEvent event, void* context) { + furi_assert(context); + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void camera_suite_scene_style_1_on_enter(void* context) { + furi_assert(context); + CameraSuite* app = context; + camera_suite_view_style_1_set_callback( + app->camera_suite_view_style_1, camera_suite_view_style_1_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene1); +} + +bool camera_suite_scene_style_1_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case CameraSuiteCustomEventSceneStyle1Left: + case CameraSuiteCustomEventSceneStyle1Right: + case CameraSuiteCustomEventSceneStyle1Up: + case CameraSuiteCustomEventSceneStyle1Down: + case CameraSuiteCustomEventSceneStyle1Ok: + // Do nothing. + break; + case CameraSuiteCustomEventSceneStyle1Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, CameraSuiteSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void camera_suite_scene_style_1_on_exit(void* context) { + CameraSuite* app = context; + UNUSED(app); +} diff --git a/applications/external/camera-suite/scenes/camera_suite_scene_style_2.c b/applications/external/camera-suite/scenes/camera_suite_scene_style_2.c new file mode 100644 index 000000000..df8c875ba --- /dev/null +++ b/applications/external/camera-suite/scenes/camera_suite_scene_style_2.c @@ -0,0 +1,54 @@ +#include "../camera-suite.h" +#include "../helpers/camera_suite_custom_event.h" +#include "../helpers/camera_suite_haptic.h" +#include "../helpers/camera_suite_led.h" +#include "../views/camera_suite_view_style_2.h" + +void camera_suite_view_style_2_callback(CameraSuiteCustomEvent event, void* context) { + furi_assert(context); + CameraSuite* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void camera_suite_scene_style_2_on_enter(void* context) { + furi_assert(context); + CameraSuite* app = context; + camera_suite_view_style_2_set_callback( + app->camera_suite_view_style_2, camera_suite_view_style_2_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdScene2); +} + +bool camera_suite_scene_style_2_on_event(void* context, SceneManagerEvent event) { + CameraSuite* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case CameraSuiteCustomEventSceneStyle2Left: + case CameraSuiteCustomEventSceneStyle2Right: + case CameraSuiteCustomEventSceneStyle2Up: + case CameraSuiteCustomEventSceneStyle2Down: + case CameraSuiteCustomEventSceneStyle2Ok: + // Do nothing. + break; + case CameraSuiteCustomEventSceneStyle2Back: + notification_message(app->notification, &sequence_reset_red); + notification_message(app->notification, &sequence_reset_green); + notification_message(app->notification, &sequence_reset_blue); + if(!scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, CameraSuiteSceneMenu)) { + scene_manager_stop(app->scene_manager); + view_dispatcher_stop(app->view_dispatcher); + } + consumed = true; + break; + } + } + + return consumed; +} + +void camera_suite_scene_style_2_on_exit(void* context) { + CameraSuite* app = context; + UNUSED(app); +} diff --git a/applications/external/camera-suite/views/camera_suite_view_guide.c b/applications/external/camera-suite/views/camera_suite_view_guide.c new file mode 100644 index 000000000..f27047d47 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_guide.c @@ -0,0 +1,120 @@ +#include "../camera-suite.h" +#include +#include +#include +#include +#include + +struct CameraSuiteViewGuide { + View* view; + CameraSuiteViewGuideCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} CameraSuiteViewGuideModel; + +void camera_suite_view_guide_set_callback( + CameraSuiteViewGuide* instance, + CameraSuiteViewGuideCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void camera_suite_view_guide_draw(Canvas* canvas, CameraSuiteViewGuideModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Guide"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 0, 12, AlignLeft, AlignTop, "Left = Toggle Invert"); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle Dithering"); + canvas_draw_str_aligned(canvas, 0, 32, AlignLeft, AlignTop, "Up = Contrast Up"); + canvas_draw_str_aligned(canvas, 0, 42, AlignLeft, AlignTop, "Down = Contrast Down"); + // TODO: Possibly update to take picture instead. + canvas_draw_str_aligned(canvas, 0, 52, AlignLeft, AlignTop, "Center = Toggle Dither Type"); +} + +static void camera_suite_view_guide_model_init(CameraSuiteViewGuideModel* const model) { + model->some_value = 1; +} + +bool camera_suite_view_guide_input(InputEvent* event, void* context) { + furi_assert(context); + CameraSuiteViewGuide* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + CameraSuiteViewGuideModel * model, + { + UNUSED(model); + instance->callback(CameraSuiteCustomEventSceneGuideBack, instance->context); + }, + true); + break; + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + case InputKeyOk: + case InputKeyMAX: + // Do nothing. + break; + } + } + return true; +} + +void camera_suite_view_guide_exit(void* context) { + furi_assert(context); +} + +void camera_suite_view_guide_enter(void* context) { + furi_assert(context); + CameraSuiteViewGuide* instance = (CameraSuiteViewGuide*)context; + with_view_model( + instance->view, + CameraSuiteViewGuideModel * model, + { camera_suite_view_guide_model_init(model); }, + true); +} + +CameraSuiteViewGuide* camera_suite_view_guide_alloc() { + CameraSuiteViewGuide* instance = malloc(sizeof(CameraSuiteViewGuide)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewGuideModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_guide_draw); + view_set_input_callback(instance->view, camera_suite_view_guide_input); + view_set_enter_callback(instance->view, camera_suite_view_guide_enter); + view_set_exit_callback(instance->view, camera_suite_view_guide_exit); + + with_view_model( + instance->view, + CameraSuiteViewGuideModel * model, + { camera_suite_view_guide_model_init(model); }, + true); + + return instance; +} + +void camera_suite_view_guide_free(CameraSuiteViewGuide* instance) { + furi_assert(instance); + + with_view_model( + instance->view, CameraSuiteViewGuideModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* camera_suite_view_guide_get_view(CameraSuiteViewGuide* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/camera-suite/views/camera_suite_view_guide.h b/applications/external/camera-suite/views/camera_suite_view_guide.h new file mode 100644 index 000000000..cd78d4b01 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_guide.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/camera_suite_custom_event.h" + +typedef struct CameraSuiteViewGuide CameraSuiteViewGuide; + +typedef void (*CameraSuiteViewGuideCallback)(CameraSuiteCustomEvent event, void* context); + +void camera_suite_view_guide_set_callback( + CameraSuiteViewGuide* camera_suite_view_guide, + CameraSuiteViewGuideCallback callback, + void* context); + +View* camera_suite_view_guide_get_view(CameraSuiteViewGuide* camera_suite_static); + +CameraSuiteViewGuide* camera_suite_view_guide_alloc(); + +void camera_suite_view_guide_free(CameraSuiteViewGuide* camera_suite_static); \ No newline at end of file diff --git a/applications/external/camera-suite/views/camera_suite_view_start.c b/applications/external/camera-suite/views/camera_suite_view_start.c new file mode 100644 index 000000000..dbb374cbf --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_start.c @@ -0,0 +1,126 @@ +#include "../camera-suite.h" +#include +#include +#include +#include + +struct CameraSuiteViewStart { + View* view; + CameraSuiteViewStartCallback callback; + void* context; +}; + +typedef struct { + int some_value; +} CameraSuiteViewStartModel; + +void camera_suite_view_start_set_callback( + CameraSuiteViewStart* instance, + CameraSuiteViewStartCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void camera_suite_view_start_draw(Canvas* canvas, CameraSuiteViewStartModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignTop, "Camera Suite"); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 22, AlignCenter, AlignTop, "Flipper Zero"); + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, "ESP32 CAM"); + elements_button_center(canvas, "Start"); +} + +static void camera_suite_view_start_model_init(CameraSuiteViewStartModel* const model) { + model->some_value = 1; +} + +bool camera_suite_view_start_input(InputEvent* event, void* context) { + furi_assert(context); + CameraSuiteViewStart* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + // Exit application. + with_view_model( + instance->view, + CameraSuiteViewStartModel * model, + { + UNUSED(model); + instance->callback(CameraSuiteCustomEventStartBack, instance->context); + }, + true); + break; + case InputKeyOk: + // Start the application. + with_view_model( + instance->view, + CameraSuiteViewStartModel * model, + { + UNUSED(model); + instance->callback(CameraSuiteCustomEventStartOk, instance->context); + }, + true); + break; + case InputKeyMAX: + case InputKeyLeft: + case InputKeyRight: + case InputKeyUp: + case InputKeyDown: + // Do nothing. + break; + } + } + return true; +} + +void camera_suite_view_start_exit(void* context) { + furi_assert(context); +} + +void camera_suite_view_start_enter(void* context) { + furi_assert(context); + CameraSuiteViewStart* instance = (CameraSuiteViewStart*)context; + with_view_model( + instance->view, + CameraSuiteViewStartModel * model, + { camera_suite_view_start_model_init(model); }, + true); +} + +CameraSuiteViewStart* camera_suite_view_start_alloc() { + CameraSuiteViewStart* instance = malloc(sizeof(CameraSuiteViewStart)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewStartModel)); + // furi_assert crashes in events without this + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_start_draw); + view_set_input_callback(instance->view, camera_suite_view_start_input); + + with_view_model( + instance->view, + CameraSuiteViewStartModel * model, + { camera_suite_view_start_model_init(model); }, + true); + + return instance; +} + +void camera_suite_view_start_free(CameraSuiteViewStart* instance) { + furi_assert(instance); + + with_view_model( + instance->view, CameraSuiteViewStartModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* camera_suite_view_start_get_view(CameraSuiteViewStart* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/camera-suite/views/camera_suite_view_start.h b/applications/external/camera-suite/views/camera_suite_view_start.h new file mode 100644 index 000000000..e991cce92 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_start.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/camera_suite_custom_event.h" + +typedef struct CameraSuiteViewStart CameraSuiteViewStart; + +typedef void (*CameraSuiteViewStartCallback)(CameraSuiteCustomEvent event, void* context); + +void camera_suite_view_start_set_callback( + CameraSuiteViewStart* camera_suite_view_start, + CameraSuiteViewStartCallback callback, + void* context); + +View* camera_suite_view_start_get_view(CameraSuiteViewStart* camera_suite_static); + +CameraSuiteViewStart* camera_suite_view_start_alloc(); + +void camera_suite_view_start_free(CameraSuiteViewStart* camera_suite_static); \ No newline at end of file diff --git a/applications/external/camera-suite/views/camera_suite_view_style_1.c b/applications/external/camera-suite/views/camera_suite_view_style_1.c new file mode 100644 index 000000000..34aeaf272 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_style_1.c @@ -0,0 +1,401 @@ +#include "../camera-suite.h" +#include +#include +#include +#include +#include +#include "../helpers/camera_suite_haptic.h" +#include "../helpers/camera_suite_speaker.h" +#include "../helpers/camera_suite_led.h" + +static CameraSuiteViewStyle1* current_instance = NULL; +// Dithering type: +// 0 = Floyd Steinberg (default) +// 1 = Atkinson +static int current_dithering = 0; + +struct CameraSuiteViewStyle1 { + CameraSuiteViewStyle1Callback callback; + FuriStreamBuffer* rx_stream; + FuriThread* worker_thread; + View* view; + void* context; +}; + +void camera_suite_view_style_1_set_callback( + CameraSuiteViewStyle1* instance, + CameraSuiteViewStyle1Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) { + // Clear the screen. + canvas_set_color(canvas, ColorBlack); + + // Draw the frame. + canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + + CameraSuite* app = current_instance->context; + + // Draw the pixels with rotation. + for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) { + uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15 + uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63 + + // Apply rotation + int16_t rotated_x, rotated_y; + switch(app->orientation) { + case 1: // 90 degrees + rotated_x = y; + rotated_y = FRAME_WIDTH - 1 - x; + break; + case 2: // 180 degrees + rotated_x = FRAME_WIDTH - 1 - x; + rotated_y = FRAME_HEIGHT - 1 - y; + break; + case 3: // 270 degrees + rotated_x = FRAME_HEIGHT - 1 - y; + rotated_y = x; + break; + case 0: // 0 degrees + default: + rotated_x = x; + rotated_y = y; + break; + } + + for(uint8_t i = 0; i < 8; ++i) { + if((model->pixels[p] & (1 << i)) != 0) { + // Adjust the coordinates based on the new screen dimensions + uint16_t screen_x, screen_y; + switch(app->orientation) { + case 1: // 90 degrees + screen_x = rotated_x; + screen_y = FRAME_HEIGHT - 8 + (rotated_y * 8) + i; + break; + case 2: // 180 degrees + screen_x = FRAME_WIDTH - 8 + (rotated_x * 8) + i; + screen_y = FRAME_HEIGHT - 1 - rotated_y; + break; + case 3: // 270 degrees + screen_x = FRAME_WIDTH - 1 - rotated_x; + screen_y = rotated_y * 8 + i; + break; + case 0: // 0 degrees + default: + screen_x = rotated_x * 8 + i; + screen_y = rotated_y; + break; + } + canvas_draw_dot(canvas, screen_x, screen_y); + } + } + } + // Draw the guide if the camera is not initialized. + if(!model->initialized) { + canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM"); + canvas_draw_str(canvas, 20, 24, "VCC - 3V3"); + canvas_draw_str(canvas, 20, 34, "GND - GND"); + canvas_draw_str(canvas, 20, 44, "U0R - TX"); + canvas_draw_str(canvas, 20, 54, "U0T - RX"); + } +} + +static void camera_suite_view_style_1_model_init(UartDumpModel* const model) { + for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) { + model->pixels[i] = 0; + } +} + +static bool camera_suite_view_style_1_input(InputEvent* event, void* context) { + furi_assert(context); + CameraSuiteViewStyle1* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + default: // Stop all sounds, reset the LED. + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 0); + }, + true); + break; + } + // Send `data` to the ESP32-CAM + } else if(event->type == InputTypePress) { + uint8_t data[1]; + switch(event->key) { + case InputKeyBack: + // Stop the camera stream. + data[0] = 's'; + // Go back to the main menu. + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + instance->callback(CameraSuiteCustomEventSceneStyle1Back, instance->context); + }, + true); + break; + case InputKeyLeft: + // Camera: Invert. + data[0] = '<'; + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 255); + instance->callback(CameraSuiteCustomEventSceneStyle1Left, instance->context); + }, + true); + break; + case InputKeyRight: + // Camera: Enable/disable dithering. + data[0] = '>'; + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 255); + instance->callback(CameraSuiteCustomEventSceneStyle1Right, instance->context); + }, + true); + break; + case InputKeyUp: + // Camera: Increase contrast. + data[0] = 'C'; + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 255); + instance->callback(CameraSuiteCustomEventSceneStyle1Up, instance->context); + }, + true); + break; + case InputKeyDown: + // Camera: Reduce contrast. + data[0] = 'c'; + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 255); + instance->callback(CameraSuiteCustomEventSceneStyle1Down, instance->context); + }, + true); + break; + case InputKeyOk: + if(current_dithering == 0) { + data[0] = 'd'; // Update to Floyd Steinberg dithering. + current_dithering = 1; + } else { + data[0] = 'D'; // Update to Atkinson dithering. + current_dithering = 0; + } + with_view_model( + instance->view, + UartDumpModel * model, + { + UNUSED(model); + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 0, 255); + instance->callback(CameraSuiteCustomEventSceneStyle1Ok, instance->context); + }, + true); + break; + case InputKeyMAX: + break; + } + // Send `data` to the ESP32-CAM + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + } + return true; +} + +static void camera_suite_view_style_1_exit(void* context) { + furi_assert(context); +} + +static void camera_suite_view_style_1_enter(void* context) { + // Check `context` for null. If it is null, abort program, else continue. + furi_assert(context); + + // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. + CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context; + + // Assign the current instance to the global variable + current_instance = instance; + + uint8_t data[1]; + data[0] = 'S'; // Uppercase `S` to start the camera + // Send `data` to the ESP32-CAM + furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1); + + with_view_model( + instance->view, + UartDumpModel * model, + { camera_suite_view_style_1_model_init(model); }, + true); +} + +static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) { + // Check `context` for null. If it is null, abort program, else continue. + furi_assert(context); + + // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`. + CameraSuiteViewStyle1* instance = context; + + // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the + // `rx_stream` and set the `WorkerEventRx` flag. + if(uartIrqEvent == UartIrqEventRXNE) { + furi_stream_buffer_send(instance->rx_stream, &data, 1, 0); + furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), WorkerEventRx); + } +} + +static void process_ringbuffer(UartDumpModel* model, uint8_t byte) { + // First char has to be 'Y' in the buffer. + if(model->ringbuffer_index == 0 && byte != 'Y') { + return; + } + + // Second char has to be ':' in the buffer or reset. + if(model->ringbuffer_index == 1 && byte != ':') { + model->ringbuffer_index = 0; + process_ringbuffer(model, byte); + return; + } + + // Assign current byte to the ringbuffer. + model->row_ringbuffer[model->ringbuffer_index] = byte; + // Increment the ringbuffer index. + ++model->ringbuffer_index; + + // Let's wait 'till the buffer fills. + if(model->ringbuffer_index < RING_BUFFER_LENGTH) { + return; + } + + // Flush the ringbuffer to the framebuffer. + model->ringbuffer_index = 0; // Reset the ringbuffer + model->initialized = true; // Established the connection successfully. + size_t row_start_index = + model->row_ringbuffer[2] * ROW_BUFFER_LENGTH; // Third char will determine the row number + + if(row_start_index > LAST_ROW_INDEX) { // Failsafe + row_start_index = 0; + } + + for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) { + model->pixels[row_start_index + i] = + model->row_ringbuffer[i + 3]; // Writing the remaining 16 bytes into the frame buffer + } +} + +static int32_t camera_worker(void* context) { + furi_assert(context); + CameraSuiteViewStyle1* instance = context; + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) { + break; + } else if(events & WorkerEventRx) { + size_t length = 0; + do { + size_t intended_data_size = 64; + uint8_t data[intended_data_size]; + length = + furi_stream_buffer_receive(instance->rx_stream, data, intended_data_size, 0); + + if(length > 0) { + with_view_model( + instance->view, + UartDumpModel * model, + { + for(size_t i = 0; i < length; i++) { + process_ringbuffer(model, data[i]); + } + }, + false); + } + } while(length > 0); + } + } + + return 0; +} + +CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() { + CameraSuiteViewStyle1* instance = malloc(sizeof(CameraSuiteViewStyle1)); + + instance->view = view_alloc(); + + instance->rx_stream = furi_stream_buffer_alloc(2048, 1); + + // Set up views + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel)); + view_set_context(instance->view, instance); // furi_assert crashes in events without this + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_1_draw); + view_set_input_callback(instance->view, camera_suite_view_style_1_input); + view_set_enter_callback(instance->view, camera_suite_view_style_1_enter); + view_set_exit_callback(instance->view, camera_suite_view_style_1_exit); + + with_view_model( + instance->view, + UartDumpModel * model, + { camera_suite_view_style_1_model_init(model); }, + true); + + instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance); + furi_thread_start(instance->worker_thread); + + // Enable uart listener + furi_hal_console_disable(); + furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400); + furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, camera_on_irq_cb, instance); + + return instance; +} + +void camera_suite_view_style_1_free(CameraSuiteViewStyle1* instance) { + furi_assert(instance); + + with_view_model( + instance->view, UartDumpModel * model, { UNUSED(model); }, true); + view_free(instance->view); + free(instance); +} + +View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/applications/external/camera-suite/views/camera_suite_view_style_1.h b/applications/external/camera-suite/views/camera_suite_view_style_1.h new file mode 100644 index 000000000..c460c94ba --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_style_1.h @@ -0,0 +1,60 @@ +#include "../helpers/camera_suite_custom_event.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma once + +#define FRAME_WIDTH 128 +#define FRAME_HEIGHT 64 +#define FRAME_BIT_DEPTH 1 +#define FRAME_BUFFER_LENGTH \ + (FRAME_WIDTH * FRAME_HEIGHT * FRAME_BIT_DEPTH / 8) // 128*64*1 / 8 = 1024 +#define ROW_BUFFER_LENGTH (FRAME_WIDTH / 8) // 128/8 = 16 +#define RING_BUFFER_LENGTH (ROW_BUFFER_LENGTH + 3) // ROW_BUFFER_LENGTH + Header => 16 + 3 = 19 +#define LAST_ROW_INDEX (FRAME_BUFFER_LENGTH - ROW_BUFFER_LENGTH) // 1024 - 16 = 1008 + +typedef struct UartDumpModel UartDumpModel; + +struct UartDumpModel { + bool initialized; + int rotation_angle; + uint8_t pixels[FRAME_BUFFER_LENGTH]; + uint8_t ringbuffer_index; + uint8_t row_ringbuffer[RING_BUFFER_LENGTH]; +}; + +typedef struct CameraSuiteViewStyle1 CameraSuiteViewStyle1; + +typedef void (*CameraSuiteViewStyle1Callback)(CameraSuiteCustomEvent event, void* context); + +void camera_suite_view_style_1_set_callback( + CameraSuiteViewStyle1* camera_suite_view_style_1, + CameraSuiteViewStyle1Callback callback, + void* context); + +CameraSuiteViewStyle1* camera_suite_view_style_1_alloc(); + +void camera_suite_view_style_1_free(CameraSuiteViewStyle1* camera_suite_static); + +View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* camera_suite_static); + +typedef enum { + // Reserved for StreamBuffer internal event + WorkerEventReserved = (1 << 0), + WorkerEventStop = (1 << 1), + WorkerEventRx = (1 << 2), +} WorkerEventFlags; + +#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx) diff --git a/applications/external/camera-suite/views/camera_suite_view_style_2.c b/applications/external/camera-suite/views/camera_suite_view_style_2.c new file mode 100644 index 000000000..eefa4d637 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_style_2.c @@ -0,0 +1,249 @@ +#include "../camera-suite.h" +#include +#include +#include +#include +#include +#include "../helpers/camera_suite_haptic.h" +#include "../helpers/camera_suite_speaker.h" +#include "../helpers/camera_suite_led.h" + +struct CameraSuiteViewStyle2 { + View* view; + CameraSuiteViewStyle2Callback callback; + void* context; +}; + +typedef struct { + int screen_text; +} CameraSuiteViewStyle2Model; + +char buttonText[11][14] = { + "", + "Press Up", + "Press Down", + "Press Left", + "Press Right", + "Press Ok", + "Release Up", + "Release Down", + "Release Left", + "Release Right", + "Release Ok", +}; + +void camera_suite_view_style_2_set_callback( + CameraSuiteViewStyle2* instance, + CameraSuiteViewStyle2Callback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void camera_suite_view_style_2_draw(Canvas* canvas, CameraSuiteViewStyle2Model* model) { + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, "Scene 2: Input Examples"); + canvas_set_font(canvas, FontSecondary); + char* strInput = malloc(15); + strcpy(strInput, buttonText[model->screen_text]); + canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, strInput); + free(strInput); +} + +static void camera_suite_view_style_2_model_init(CameraSuiteViewStyle2Model* const model) { + model->screen_text = 0; +} + +bool camera_suite_view_style_2_input(InputEvent* event, void* context) { + furi_assert(context); + CameraSuiteViewStyle2* instance = context; + if(event->type == InputTypeRelease) { + switch(event->key) { + case InputKeyBack: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + UNUSED(model); + camera_suite_stop_all_sound(instance->context); + instance->callback(CameraSuiteCustomEventSceneStyle2Back, instance->context); + camera_suite_play_long_bump(instance->context); + }, + true); + break; + case InputKeyUp: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 6; + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 255, 0, 255); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 7; + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 255, 255, 0); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 8; + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 0, 255, 255); + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 9; + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 255, 0, 0); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 10; + camera_suite_play_bad_bump(instance->context); + camera_suite_stop_all_sound(instance->context); + camera_suite_led_set_rgb(instance->context, 255, 255, 255); + }, + true); + break; + case InputKeyMAX: + break; + } + } else if(event->type == InputTypePress) { + switch(event->key) { + case InputKeyUp: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 1; + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + }, + true); + break; + case InputKeyDown: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 2; + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + }, + true); + break; + case InputKeyLeft: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 3; + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + }, + true); + break; + case InputKeyRight: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 4; + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + }, + true); + break; + case InputKeyOk: + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { + model->screen_text = 5; + camera_suite_play_happy_bump(instance->context); + camera_suite_play_input_sound(instance->context); + }, + true); + break; + case InputKeyBack: + case InputKeyMAX: + break; + } + } + + return true; +} + +void camera_suite_view_style_2_exit(void* context) { + furi_assert(context); + CameraSuite* app = context; + camera_suite_stop_all_sound(app); + //camera_suite_led_reset(app); +} + +void camera_suite_view_style_2_enter(void* context) { + furi_assert(context); + dolphin_deed(DolphinDeedPluginStart); +} + +CameraSuiteViewStyle2* camera_suite_view_style_2_alloc() { + CameraSuiteViewStyle2* instance = malloc(sizeof(CameraSuiteViewStyle2)); + instance->view = view_alloc(); + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewStyle2Model)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_2_draw); + view_set_input_callback(instance->view, camera_suite_view_style_2_input); + //view_set_enter_callback(instance->view, camera_suite_view_style_2_enter); + view_set_exit_callback(instance->view, camera_suite_view_style_2_exit); + + with_view_model( + instance->view, + CameraSuiteViewStyle2Model * model, + { camera_suite_view_style_2_model_init(model); }, + true); + + return instance; +} + +void camera_suite_view_style_2_free(CameraSuiteViewStyle2* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* instance) { + furi_assert(instance); + + return instance->view; +} diff --git a/applications/external/camera-suite/views/camera_suite_view_style_2.h b/applications/external/camera-suite/views/camera_suite_view_style_2.h new file mode 100644 index 000000000..d24b895f8 --- /dev/null +++ b/applications/external/camera-suite/views/camera_suite_view_style_2.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include "../helpers/camera_suite_custom_event.h" + +typedef struct CameraSuiteViewStyle2 CameraSuiteViewStyle2; + +typedef void (*CameraSuiteViewStyle2Callback)(CameraSuiteCustomEvent event, void* context); + +void camera_suite_view_style_2_set_callback( + CameraSuiteViewStyle2* instance, + CameraSuiteViewStyle2Callback callback, + void* context); + +CameraSuiteViewStyle2* camera_suite_view_style_2_alloc(); + +void camera_suite_view_style_2_free(CameraSuiteViewStyle2* camera_suite_static); + +View* camera_suite_view_style_2_get_view(CameraSuiteViewStyle2* boilerpate_static);