diff --git a/applications/system/findmy/application.fam b/applications/system/findmy/application.fam new file mode 100644 index 0000000000..0b6006923c --- /dev/null +++ b/applications/system/findmy/application.fam @@ -0,0 +1,14 @@ +App( + appid="findmy", + name="FindMy Flipper", + apptype=FlipperAppType.EXTERNAL, + entry_point="findmy_main", + requires=["gui"], + stack_size=1 * 1024, + order=35, + fap_icon="location_icon.png", + fap_category="Bluetooth", + fap_author="@MatthewKuKanich", + fap_version="1.0", + fap_description="BLE FindMy Location Beacon", +) diff --git a/applications/system/findmy/findmy.c b/applications/system/findmy/findmy.c new file mode 100644 index 0000000000..2722f32ab7 --- /dev/null +++ b/applications/system/findmy/findmy.c @@ -0,0 +1,164 @@ +#include "findmy_i.h" + +static bool findmy_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FindMy* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool findmy_back_event_callback(void* context) { + furi_assert(context); + FindMy* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static FindMy* findmy_app_alloc() { + FindMy* app = malloc(sizeof(FindMy)); + + app->gui = furi_record_open(RECORD_GUI); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&findmy_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_custom_event_callback(app->view_dispatcher, findmy_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, findmy_back_event_callback); + + app->findmy_main = findmy_main_alloc(app); + view_dispatcher_add_view( + app->view_dispatcher, FindMyViewMain, findmy_main_get_view(app->findmy_main)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FindMyViewByteInput, byte_input_get_view(app->byte_input)); + + app->var_item_list = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FindMyViewVarItemList, + variable_item_list_get_view(app->var_item_list)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + app->beacon_active = false; + findmy_main_update_active(app->findmy_main, app->beacon_active); + app->broadcast_interval = 5; + findmy_main_update_interval(app->findmy_main, app->broadcast_interval); + app->transmit_power = 6; + app->apple = true; + findmy_main_update_apple(app->findmy_main, app->apple); + + return app; +} + +static void findmy_app_free(FindMy* app) { + furi_assert(app); + + view_dispatcher_remove_view(app->view_dispatcher, FindMyViewVarItemList); + variable_item_list_free(app->var_item_list); + + view_dispatcher_remove_view(app->view_dispatcher, FindMyViewByteInput); + byte_input_free(app->byte_input); + + view_dispatcher_remove_view(app->view_dispatcher, FindMyViewMain); + findmy_main_free(app->findmy_main); + + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + furi_record_close(RECORD_GUI); + + free(app); +} + +static void findmy_start(FindMy* app) { + furi_hal_bt_extra_beacon_stop(); // Stop any running beacon + + app->config.min_adv_interval_ms = app->broadcast_interval * 1000; // Converting s to ms + app->config.max_adv_interval_ms = (app->broadcast_interval * 1000) + 150; + app->config.adv_channel_map = GapAdvChannelMapAll; + app->config.adv_power_level = GapAdvPowerLevel_0dBm + app->transmit_power; + app->config.address_type = GapAddressTypePublic; + + uint8_t mac[EXTRA_BEACON_MAC_ADDR_SIZE] = {0x4D, 0x61, 0x74, 0x4B, 0x75, 0x4B}; + furi_hal_bt_reverse_mac_addr(mac); + memcpy(&app->config.address, mac, sizeof(app->config.address)); + furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); + + uint8_t data[EXTRA_BEACON_MAX_DATA_SIZE]; + uint8_t* it = data; + + // For Apple AirTags + *it++ = 0x1E; // Length + *it++ = 0xFF; // Manufacturer Specific Data + *it++ = 0x4C; // Company ID (Apple, Inc.) + *it++ = 0x00; // State + *it++ = 0x12; // Data - Public Key without the MAC address + *it++ = 0x81; // ... + *it++ = 0xB9; // ... + *it++ = 0x02; // First 2 bits are the version, the rest is the battery level + *it++ = 0x7E; // Hint (0x00) + + furi_check(furi_hal_bt_extra_beacon_set_data(data, it - data)); +} + +int32_t findmy_main(void* p) { + UNUSED(p); + FindMy* app = findmy_app_alloc(); + + findmy_start(app); + + scene_manager_next_scene(app->scene_manager, FindMySceneMain); + + view_dispatcher_run(app->view_dispatcher); + + findmy_app_free(app); + return 0; +} + +void findmy_change_broadcast_interval(FindMy* app, uint8_t value) { + if(value > 10 || value < 1) { + return; + } + app->broadcast_interval = value; + findmy_main_update_interval(app->findmy_main, app->broadcast_interval); + if(app->beacon_active) { + // Always check if beacon is active before changing config + furi_check(furi_hal_bt_extra_beacon_stop()); + } + app->config.min_adv_interval_ms = app->broadcast_interval * 1000; + app->config.max_adv_interval_ms = app->config.min_adv_interval_ms + 150; + furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); + if(app->beacon_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } +} + +void findmy_change_transmit_power(FindMy* app, uint8_t value) { + if(value > 6) { + return; + } + app->transmit_power = value; + if(app->beacon_active) { + furi_check(furi_hal_bt_extra_beacon_stop()); + } + app->config.adv_power_level = GapAdvPowerLevel_0dBm + app->transmit_power; + furi_check(furi_hal_bt_extra_beacon_set_config(&app->config)); + if(app->beacon_active) { + furi_check(furi_hal_bt_extra_beacon_start()); + } +} + +void findmy_toggle_beacon(FindMy* app) { + app->beacon_active = !app->beacon_active; + findmy_main_update_active(app->findmy_main, app->beacon_active); + findmy_main_update_apple(app->findmy_main, app->apple); + if(app->beacon_active) { + furi_hal_bt_extra_beacon_start(); + } else { + furi_hal_bt_extra_beacon_stop(); + } +} \ No newline at end of file diff --git a/applications/system/findmy/findmy.h b/applications/system/findmy/findmy.h new file mode 100644 index 0000000000..344882ef10 --- /dev/null +++ b/applications/system/findmy/findmy.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct FindMy FindMy; diff --git a/applications/system/findmy/findmy_i.h b/applications/system/findmy/findmy_i.h new file mode 100644 index 0000000000..fd09e5324a --- /dev/null +++ b/applications/system/findmy/findmy_i.h @@ -0,0 +1,42 @@ +#pragma once + +#include "findmy.h" +#include +#include +#include +#include +#include +#include +#include "views/findmy_main.h" +#include +#include +#include "scenes/findmy_scene.h" + +struct FindMy { + Gui* gui; + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + + FindMyMain* findmy_main; + ByteInput* byte_input; + VariableItemList* var_item_list; + + uint8_t mac_buf[EXTRA_BEACON_MAC_ADDR_SIZE]; + uint8_t packet_buf[EXTRA_BEACON_MAX_DATA_SIZE]; + + GapExtraBeaconConfig config; + bool apple; + bool beacon_active; + uint8_t broadcast_interval; + uint8_t transmit_power; +}; + +typedef enum { + FindMyViewMain, + FindMyViewByteInput, + FindMyViewVarItemList, +} FindMyView; + +void findmy_change_broadcast_interval(FindMy* app, uint8_t value); +void findmy_change_transmit_power(FindMy* app, uint8_t value); +void findmy_toggle_beacon(FindMy* app); diff --git a/applications/system/findmy/location_icon.png b/applications/system/findmy/location_icon.png new file mode 100644 index 0000000000..841787a2a6 Binary files /dev/null and b/applications/system/findmy/location_icon.png differ diff --git a/applications/system/findmy/scenes/findmy_scene.c b/applications/system/findmy/scenes/findmy_scene.c new file mode 100644 index 0000000000..dca1adbb4f --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene.c @@ -0,0 +1,31 @@ +#include "findmy_scene.h" +#include "findmy.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const findmy_on_enter_handlers[])(void*) = { +#include "findmy_scenes.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const findmy_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "findmy_scenes.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const findmy_on_exit_handlers[])(void* context) = { +#include "findmy_scenes.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers findmy_scene_handlers = { + .on_enter_handlers = findmy_on_enter_handlers, + .on_event_handlers = findmy_on_event_handlers, + .on_exit_handlers = findmy_on_exit_handlers, + .scene_num = FindMySceneNum, +}; \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scene.h b/applications/system/findmy/scenes/findmy_scene.h new file mode 100644 index 0000000000..993b9d0542 --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include "findmy.h" + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FindMyScene##id, +typedef enum { +#include "findmy_scenes.h" + FindMySceneNum, +} FindMyScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers findmy_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "findmy_scenes.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 "findmy_scenes.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 "findmy_scenes.h" +#undef ADD_SCENE \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scene_config.c b/applications/system/findmy/scenes/findmy_scene_config.c new file mode 100644 index 0000000000..8f16d7b190 --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene_config.c @@ -0,0 +1,98 @@ +#include "../findmy_i.h" + +enum VarItemListIndex { + VarItemListIndexBroadcastInterval, + VarItemListIndexTransmitPower, + VarItemListIndexRegisterTag, + VarItemListIndexAbout, +}; + +void findmy_scene_config_broadcast_interval_changed(VariableItem* item) { + FindMy* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + findmy_change_broadcast_interval(app, index + 1); + char str[5]; + snprintf(str, sizeof(str), "%ds", app->broadcast_interval); + variable_item_set_current_value_text(item, str); + variable_item_set_current_value_index(item, app->broadcast_interval - 1); +} + +void findmy_scene_config_transmit_power_changed(VariableItem* item) { + FindMy* app = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + findmy_change_transmit_power(app, index); + char str[7]; + snprintf(str, sizeof(str), "%ddBm", app->transmit_power); + variable_item_set_current_value_text(item, str); + variable_item_set_current_value_index(item, app->transmit_power); +} + +void findmy_scene_config_callback(void* context, uint32_t index) { + furi_assert(context); + FindMy* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void findmy_scene_config_on_enter(void* context) { + FindMy* app = context; + VariableItemList* var_item_list = app->var_item_list; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, + "Broadcast Interval", + 10, + findmy_scene_config_broadcast_interval_changed, + app); + // Broadcast Interval is 1-10, so use 0-9 and offset indexes by 1 + variable_item_set_current_value_index(item, app->broadcast_interval - 1); + char broadcast_interval_s[5]; + snprintf(broadcast_interval_s, sizeof(broadcast_interval_s), "%ds", app->broadcast_interval); + variable_item_set_current_value_text(item, broadcast_interval_s); + + item = variable_item_list_add( + var_item_list, "Transmit Power", 7, findmy_scene_config_transmit_power_changed, app); + variable_item_set_current_value_index(item, app->transmit_power); + char transmit_power_s[7]; + snprintf(transmit_power_s, sizeof(transmit_power_s), "%ddBm", app->transmit_power); + variable_item_set_current_value_text(item, transmit_power_s); + + item = variable_item_list_add(var_item_list, "Register Tag", 0, NULL, NULL); + item = variable_item_list_add(var_item_list, "Matthew KuKanich, Thanks to Chapoly1305, WillyJL, OpenHaystack, Testers", 1, NULL, NULL); + variable_item_set_current_value_text(item, "Credits"); + + variable_item_list_set_enter_callback(var_item_list, findmy_scene_config_callback, app); + + variable_item_list_set_selected_item( + var_item_list, scene_manager_get_scene_state(app->scene_manager, FindMySceneConfig)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FindMyViewVarItemList); +} + +bool findmy_scene_config_on_event(void* context, SceneManagerEvent event) { + FindMy* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, FindMySceneConfig, event.event); + consumed = true; + switch(event.event) { + case VarItemListIndexRegisterTag: + scene_manager_next_scene(app->scene_manager, FindMySceneConfigMac); + break; + case VarItemListIndexAbout: + break; + default: + break; + } + } + + return consumed; +} + +void findmy_scene_config_on_exit(void* context) { + FindMy* app = context; + VariableItemList* var_item_list = app->var_item_list; + + variable_item_list_reset(var_item_list); +} \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scene_config_mac.c b/applications/system/findmy/scenes/findmy_scene_config_mac.c new file mode 100644 index 0000000000..c86e3dc1e7 --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene_config_mac.c @@ -0,0 +1,60 @@ +#include "../findmy_i.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void findmy_scene_config_mac_callback(void* context) { + FindMy* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void findmy_scene_config_mac_on_enter(void* context) { + FindMy* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Enter Bluetooth MAC:"); + + memcpy(app->mac_buf, &app->config.address, sizeof(app->mac_buf)); + furi_hal_bt_reverse_mac_addr(app->mac_buf); + + byte_input_set_result_callback( + byte_input, + findmy_scene_config_mac_callback, + NULL, + app, + app->mac_buf, + sizeof(app->mac_buf)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FindMyViewByteInput); +} + +bool findmy_scene_config_mac_on_event(void* context, SceneManagerEvent event) { + FindMy* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + furi_hal_bt_reverse_mac_addr(app->mac_buf); + memcpy(&app->config.address, app->mac_buf, sizeof(app->config.address)); + furi_hal_bt_extra_beacon_set_config(&app->config); + scene_manager_next_scene(app->scene_manager, FindMySceneConfigPacket); + break; + default: + break; + } + } + + return consumed; +} + +void findmy_scene_config_mac_on_exit(void* context) { + FindMy* app = context; + + byte_input_set_result_callback(app->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(app->byte_input, ""); + +} \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scene_config_packet.c b/applications/system/findmy/scenes/findmy_scene_config_packet.c new file mode 100644 index 0000000000..4dddf6c02b --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene_config_packet.c @@ -0,0 +1,64 @@ +#include "../findmy_i.h" + +enum ByteInputResult { + ByteInputResultOk, +}; + +static void findmy_scene_config_packet_callback(void* context) { + FindMy* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, ByteInputResultOk); +} + +void findmy_scene_config_packet_on_enter(void* context) { + FindMy* app = context; + ByteInput* byte_input = app->byte_input; + + byte_input_set_header_text(byte_input, "Enter Bluetooth Payload:"); + + memset(app->packet_buf, 0, sizeof(app->packet_buf)); + furi_hal_bt_extra_beacon_get_data(app->packet_buf); + + byte_input_set_result_callback( + byte_input, + findmy_scene_config_packet_callback, + NULL, + app, + app->packet_buf, + sizeof(app->packet_buf)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FindMyViewByteInput); +} + +bool findmy_scene_config_packet_on_event(void* context, SceneManagerEvent event) { + FindMy* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case ByteInputResultOk: + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, FindMySceneConfig); + furi_check(furi_hal_bt_extra_beacon_set_data(app->packet_buf, sizeof(app->packet_buf))); + if (app->packet_buf[0] == 0x1E && app->packet_buf[3] == 0x00) { + app->apple = true; // Checks payload data for Apple identifier + } else { + app->apple = false; + } + findmy_main_update_apple(app->findmy_main, app->apple); + break; + default: + break; + } + } + + return consumed; +} + +void findmy_scene_config_packet_on_exit(void* context) { + FindMy* app = context; + + byte_input_set_result_callback(app->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(app->byte_input, ""); +} \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scene_main.c b/applications/system/findmy/scenes/findmy_scene_main.c new file mode 100644 index 0000000000..5738370cf8 --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scene_main.c @@ -0,0 +1,54 @@ +#include "../findmy_i.h" + +void findmy_scene_main_callback(FindMyMainEvent event, void* context) { + furi_assert(context); + FindMy* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void findmy_scene_main_on_enter(void* context) { + FindMy* app = context; + + findmy_main_set_callback(app->findmy_main, findmy_scene_main_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FindMyViewMain); +} + +bool findmy_scene_main_on_event(void* context, SceneManagerEvent event) { + FindMy* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + switch(event.event) { + case FindMyMainEventToggle: + findmy_toggle_beacon(app); + break; + case FindMyMainEventBackground: + furi_hal_bt_extra_beacon_start(); + view_dispatcher_stop(app->view_dispatcher); + break; + case FindMyMainEventConfig: + scene_manager_next_scene(app->scene_manager, FindMySceneConfig); + break; + case FindMyMainEventIntervalUp: + findmy_change_broadcast_interval(app, app->broadcast_interval + 1); + break; + case FindMyMainEventIntervalDown: + findmy_change_broadcast_interval(app, app->broadcast_interval - 1); + break; + case FindMyMainEventQuit: + break; + default: + consumed = false; + break; + } + } + + return consumed; +} + +void findmy_scene_main_on_exit(void* context) { + FindMy* app = context; + UNUSED(app); +} \ No newline at end of file diff --git a/applications/system/findmy/scenes/findmy_scenes.h b/applications/system/findmy/scenes/findmy_scenes.h new file mode 100644 index 0000000000..683c52c5b7 --- /dev/null +++ b/applications/system/findmy/scenes/findmy_scenes.h @@ -0,0 +1,4 @@ +ADD_SCENE(findmy, main, Main) +ADD_SCENE(findmy, config, Config) +ADD_SCENE(findmy, config_mac, ConfigMac) +ADD_SCENE(findmy, config_packet, ConfigPacket) diff --git a/applications/system/findmy/views/findmy_main.c b/applications/system/findmy/views/findmy_main.c new file mode 100644 index 0000000000..f7b2485462 --- /dev/null +++ b/applications/system/findmy/views/findmy_main.c @@ -0,0 +1,143 @@ +#include "findmy_main.h" +#include "../findmy_i.h" + +struct FindMyMain { + View* view; + FindMyMainCallback callback; + void* context; +}; + +typedef struct { + bool active; + bool apple; + uint8_t interval; +} FindMyMainModel; + +static void findmy_main_draw_callback(Canvas* canvas, void* _model) { + FindMyMainModel* model = _model; + canvas_clear(canvas); + canvas_set_bitmap_mode(canvas, true); + canvas_set_font(canvas, FontPrimary); + + canvas_draw_str(canvas, 4, 11, "FindMy Flipper"); + canvas_set_font(canvas, FontSecondary); + if(model->active) { + canvas_draw_str(canvas, 4, 49, "Broadcast Active"); + canvas_draw_icon(canvas, 78, 42, &I_Ok_btn_9x9); + } else { + canvas_draw_str(canvas, 4, 49, "Broadcast Inactive"); + } + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 4, 21, "Press <- to run in background"); + canvas_set_font(canvas, FontSecondary); + char interval_str[20]; + snprintf(interval_str, sizeof(interval_str), "Ping Interval: %ds", model->interval); + canvas_draw_str(canvas, 4, 62, interval_str); + canvas_set_font(canvas, FontPrimary); + if(model->apple){ + canvas_draw_str(canvas, 4, 32, "Apple Network"); + canvas_draw_icon(canvas, 80, 24, &I_Lock_7x8); + } else { + canvas_draw_str(canvas, 4, 32, "Samsung Network"); + canvas_draw_icon(canvas, 97, 24, &I_Lock_7x8); + } + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 100, 61, "Config"); + canvas_draw_line(canvas, 100, 51, 127, 51); + canvas_draw_line(canvas, 97, 53, 97, 63); + canvas_draw_line(canvas, 97, 53, 99, 51); + canvas_draw_line(canvas, 3, 52, 87, 52); +} + +static bool findmy_main_input_callback(InputEvent* event, void* context) { + furi_assert(context); + FindMyMain* findmy_main = context; + bool consumed = false; + + if(event->type == InputTypePress) { + consumed = true; + // FIXME: finish implementing handlers in scene side + switch(event->key) { + case InputKeyBack: + findmy_main->callback(FindMyMainEventQuit, findmy_main->context); + // furi_hal_bt_extra_beacon_stop(); + break; + case InputKeyOk: + findmy_main->callback(FindMyMainEventToggle, findmy_main->context); + break; + case InputKeyLeft: + findmy_main->callback(FindMyMainEventBackground, findmy_main->context); + break; + case InputKeyRight: + findmy_main->callback(FindMyMainEventConfig, findmy_main->context); + break; + case InputKeyUp: + findmy_main->callback(FindMyMainEventIntervalUp, findmy_main->context); + break; + case InputKeyDown: + findmy_main->callback(FindMyMainEventIntervalDown, findmy_main->context); + break; + default: + break; + } + } + + return consumed; +} + +FindMyMain* findmy_main_alloc(FindMy* app) { + FindMyMain* findmy_main = malloc(sizeof(FindMyMain)); + + findmy_main->view = view_alloc(); + view_allocate_model(findmy_main->view, ViewModelTypeLocking, sizeof(FindMyMainModel)); + with_view_model( + findmy_main->view, + FindMyMainModel * model, + { + model->active = app->beacon_active; + model->apple = app->apple; + model->interval = app->broadcast_interval; + }, + false); + view_set_context(findmy_main->view, findmy_main); + view_set_draw_callback(findmy_main->view, findmy_main_draw_callback); + view_set_input_callback(findmy_main->view, findmy_main_input_callback); + + return findmy_main; +} + +void findmy_main_free(FindMyMain* findmy_main) { + furi_assert(findmy_main); + view_free(findmy_main->view); + free(findmy_main); +} + +View* findmy_main_get_view(FindMyMain* findmy_main) { + furi_assert(findmy_main); + return findmy_main->view; +} + +void findmy_main_set_callback(FindMyMain* findmy_main, FindMyMainCallback callback, void* context) { + furi_assert(findmy_main); + furi_assert(callback); + findmy_main->callback = callback; + findmy_main->context = context; +} + +void findmy_main_update_active(FindMyMain* findmy_main, bool active) { + furi_assert(findmy_main); + with_view_model( + findmy_main->view, FindMyMainModel * model, { model->active = active; }, true); +} + +void findmy_main_update_interval(FindMyMain* findmy_main, uint8_t interval) { + furi_assert(findmy_main); + with_view_model( + findmy_main->view, FindMyMainModel * model, { model->interval = interval; }, true); +} + +void findmy_main_update_apple(FindMyMain* findmy_main, bool apple) { + furi_assert(findmy_main); + with_view_model( + findmy_main->view, FindMyMainModel * model, { model->apple = apple; }, true); +} \ No newline at end of file diff --git a/applications/system/findmy/views/findmy_main.h b/applications/system/findmy/views/findmy_main.h new file mode 100644 index 0000000000..977f574d71 --- /dev/null +++ b/applications/system/findmy/views/findmy_main.h @@ -0,0 +1,29 @@ +#pragma once + +#include "../findmy.h" +#include + +typedef enum { + FindMyMainEventToggle, + FindMyMainEventBackground, + FindMyMainEventConfig, + FindMyMainEventIntervalUp, + FindMyMainEventIntervalDown, + FindMyMainEventQuit, +} FindMyMainEvent; + +typedef struct FindMyMain FindMyMain; +typedef void (*FindMyMainCallback)(FindMyMainEvent event, void* context); + +// Main functionality +FindMyMain* findmy_main_alloc(FindMy* app); +void findmy_main_free(FindMyMain* findmy_main); +View* findmy_main_get_view(FindMyMain* findmy_main); + +// To communicate with scene +void findmy_main_set_callback(FindMyMain* findmy_main, FindMyMainCallback callback, void* context); + +// To redraw when info changes +void findmy_main_update_active(FindMyMain* findmy_main, bool active); +void findmy_main_update_interval(FindMyMain* findmy_main, uint8_t interval); +void findmy_main_update_apple(FindMyMain* findmy_main, bool apple); \ No newline at end of file