diff --git a/src/display_module.c b/src/display_module.c index 0ab8692..3d6bcf1 100644 --- a/src/display_module.c +++ b/src/display_module.c @@ -5,6 +5,7 @@ #define MODULE display_module #include +#include #include #include @@ -76,6 +77,7 @@ static struct display_module_ctx ctx = { BLINKY_THEME_DEFAULT_B), .inactive_border_color = LV_COLOR_MAKE(0x3A, 0x44, 0x52), .mode = MODE_SWITCH_BLE, + .ble_link_state = UI_BLE_LINK_HIDDEN, }, .date_text = "1970/01/01", .time_text = "00:00:00", @@ -210,6 +212,9 @@ static bool app_event_handler(const struct app_event_header *aeh) const struct mode_switch_event *event = cast_mode_switch_event(aeh); ctx.ui_model.mode = event->mode; + if (ctx.ui_model.mode != MODE_SWITCH_BLE) { + ctx.ui_model.ble_link_state = UI_BLE_LINK_HIDDEN; + } refresh_ui(); return false; } @@ -219,6 +224,45 @@ static bool app_event_handler(const struct app_event_header *aeh) return false; } + if (is_ble_peer_search_event(aeh)) { + const struct ble_peer_search_event *event = + cast_ble_peer_search_event(aeh); + + if (ctx.ui_model.mode == MODE_SWITCH_BLE) { + ctx.ui_model.ble_link_state = event->active ? + UI_BLE_LINK_SEARCHING : UI_BLE_LINK_HIDDEN; + refresh_ui(); + } + + return false; + } + + if (is_ble_peer_event(aeh)) { + const struct ble_peer_event *event = cast_ble_peer_event(aeh); + + if (ctx.ui_model.mode != MODE_SWITCH_BLE) { + return false; + } + + switch (event->state) { + case PEER_STATE_CONNECTED: + case PEER_STATE_SECURED: + ctx.ui_model.ble_link_state = UI_BLE_LINK_CONNECTED; + break; + + case PEER_STATE_DISCONNECTED: + case PEER_STATE_CONN_FAILED: + ctx.ui_model.ble_link_state = UI_BLE_LINK_SEARCHING; + break; + + default: + return false; + } + + refresh_ui(); + return false; + } + if (is_hid_led_event(aeh)) { const struct hid_led_event *event = cast_hid_led_event(aeh); @@ -323,6 +367,8 @@ static bool app_event_handler(const struct app_event_header *aeh) APP_EVENT_LISTENER(MODULE, app_event_handler); APP_EVENT_SUBSCRIBE(MODULE, bat_state_event); APP_EVENT_SUBSCRIBE(MODULE, ble_bond_multi_event); +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_event); +APP_EVENT_SUBSCRIBE(MODULE, ble_peer_search_event); APP_EVENT_SUBSCRIBE(MODULE, datetime_event); APP_EVENT_SUBSCRIBE(MODULE, hid_led_event); APP_EVENT_SUBSCRIBE(MODULE, module_state_event); diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c index 67e4702..78a55a8 100644 --- a/src/ui/ui_main.c +++ b/src/ui/ui_main.c @@ -25,6 +25,9 @@ struct ui_main_ctx { lv_obj_t *battery_icon; lv_obj_t *battery_label; lv_obj_t *battery_state_label; + lv_obj_t *ble_link_wrap; + lv_obj_t *ble_link_spinner; + lv_obj_t *ble_link_icon; lv_obj_t *date_label; lv_obj_t *time_label; }; @@ -42,6 +45,8 @@ static const char *const status_texts[UI_STATUS_COUNT] = { "A", }; +#define UI_BLE_LINK_BLUE lv_color_hex(0x0082FC) + static lv_color_t ui_main_get_battery_color(uint8_t battery_level) { if (battery_level > 70U) { @@ -176,6 +181,31 @@ void ui_main_refresh_battery(const struct ui_main_model *model) lv_label_set_text(g_ui.battery_label, battery_text); lv_label_set_text(g_ui.battery_state_label, state_symbol); lv_obj_set_style_text_color(g_ui.battery_state_label, state_color, 0); + + if ((g_ui.ble_link_wrap == NULL) || (g_ui.ble_link_spinner == NULL) || + (g_ui.ble_link_icon == NULL)) { + return; + } + + switch (model->ble_link_state) { + case UI_BLE_LINK_SEARCHING: + lv_obj_clear_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN); + break; + + case UI_BLE_LINK_CONNECTED: + lv_obj_clear_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN); + lv_obj_add_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_HIDDEN); + lv_obj_clear_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_style_text_color(g_ui.ble_link_icon, UI_BLE_LINK_BLUE, 0); + break; + + case UI_BLE_LINK_HIDDEN: + default: + lv_obj_add_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN); + break; + } } void ui_main_refresh_datetime(const char *date_text, const char *time_text) @@ -270,6 +300,31 @@ void ui_main_init(const struct ui_main_model *model, g_ui.battery_state_label = lv_label_create(battery_wrap); lv_obj_set_style_text_font(g_ui.battery_state_label, &lv_font_montserrat_14, 0); + g_ui.ble_link_wrap = lv_obj_create(battery_wrap); + lv_obj_remove_style_all(g_ui.ble_link_wrap); + lv_obj_set_size(g_ui.ble_link_wrap, 18, 18); + lv_obj_add_flag(g_ui.ble_link_wrap, LV_OBJ_FLAG_HIDDEN); + + g_ui.ble_link_spinner = lv_spinner_create(g_ui.ble_link_wrap); + lv_obj_center(g_ui.ble_link_spinner); + lv_obj_set_size(g_ui.ble_link_spinner, 16, 16); + lv_spinner_set_anim_params(g_ui.ble_link_spinner, 2000, 200); + lv_obj_set_style_arc_width(g_ui.ble_link_spinner, 2, LV_PART_MAIN); + lv_obj_set_style_arc_width(g_ui.ble_link_spinner, 2, LV_PART_INDICATOR); + lv_obj_set_style_arc_color(g_ui.ble_link_spinner, lv_color_hex(0x2A3442), + LV_PART_MAIN); + lv_obj_set_style_arc_color(g_ui.ble_link_spinner, model->theme_color, + LV_PART_INDICATOR); + lv_obj_remove_style(g_ui.ble_link_spinner, NULL, LV_PART_KNOB); + lv_obj_clear_flag(g_ui.ble_link_spinner, LV_OBJ_FLAG_CLICKABLE); + + g_ui.ble_link_icon = lv_label_create(g_ui.ble_link_wrap); + lv_label_set_text(g_ui.ble_link_icon, LV_SYMBOL_BLUETOOTH); + lv_obj_center(g_ui.ble_link_icon); + lv_obj_set_style_text_font(g_ui.ble_link_icon, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(g_ui.ble_link_icon, UI_BLE_LINK_BLUE, 0); + lv_obj_add_flag(g_ui.ble_link_icon, LV_OBJ_FLAG_HIDDEN); + middle_row = lv_obj_create(content); lv_obj_remove_style_all(middle_row); lv_obj_set_width(middle_row, LV_PCT(100)); diff --git a/src/ui/ui_main.h b/src/ui/ui_main.h index d75b6fe..cce3a0c 100644 --- a/src/ui/ui_main.h +++ b/src/ui/ui_main.h @@ -13,11 +13,18 @@ extern "C" { #endif +enum ui_ble_link_state { + UI_BLE_LINK_HIDDEN = 0, + UI_BLE_LINK_SEARCHING, + UI_BLE_LINK_CONNECTED, +}; + struct ui_main_model { lv_color_t theme_color; lv_color_t inactive_border_color; uint8_t battery_level; enum mode_switch_mode mode; + enum ui_ble_link_state ble_link_state; uint8_t led_mask; bool charging; bool full;