From b192975cb1eeb4e6b7c7bf53cdf42701c55f034a Mon Sep 17 00:00:00 2001 From: jacqueline Date: Fri, 22 Sep 2023 15:48:41 +1000 Subject: make bluetooth pairing ui functional --- src/ui/screen_settings.cpp | 152 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 12 deletions(-) (limited to 'src/ui/screen_settings.cpp') diff --git a/src/ui/screen_settings.cpp b/src/ui/screen_settings.cpp index d8c867dc..faeac865 100644 --- a/src/ui/screen_settings.cpp +++ b/src/ui/screen_settings.cpp @@ -9,8 +9,11 @@ #include #include "audio_events.hpp" +#include "bluetooth.hpp" +#include "bluetooth_types.hpp" #include "core/lv_event.h" #include "core/lv_obj.h" +#include "core/lv_obj_tree.h" #include "display.hpp" #include "esp_log.h" @@ -100,7 +103,18 @@ static auto label_pair(lv_obj_t* parent, return right_label; } -Bluetooth::Bluetooth() : MenuScreen("Bluetooth") { +static auto toggle_bt_cb(lv_event_t* ev) { + Bluetooth* instance = reinterpret_cast(ev->user_data); + instance->ChangeEnabledState(lv_obj_has_state(ev->target, LV_STATE_CHECKED)); +} + +static auto select_device_cb(lv_event_t* ev) { + Bluetooth* instance = reinterpret_cast(ev->user_data); + instance->OnDeviceSelected(lv_obj_get_index(ev->target)); +} + +Bluetooth::Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs) + : MenuScreen("Bluetooth"), bt_(bt), nvs_(nvs) { lv_obj_t* toggle_container = settings_container(content_); lv_obj_t* toggle_label = lv_label_create(toggle_container); lv_label_set_text(toggle_label, "Enable"); @@ -108,20 +122,134 @@ Bluetooth::Bluetooth() : MenuScreen("Bluetooth") { lv_obj_t* toggle = lv_switch_create(toggle_container); lv_group_add_obj(group_, toggle); + if (bt.IsEnabled()) { + lv_obj_add_state(toggle, LV_STATE_CHECKED); + } + + lv_obj_add_event_cb(toggle, toggle_bt_cb, LV_EVENT_VALUE_CHANGED, this); + lv_obj_t* devices_label = lv_label_create(content_); lv_label_set_text(devices_label, "Devices"); - lv_obj_t* devices_list = lv_list_create(content_); - lv_list_add_text(devices_list, "My Headphones"); - lv_group_add_obj(group_, - lv_list_add_btn(devices_list, NULL, "Something else")); - lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL, "A car??")); - lv_group_add_obj(group_, - lv_list_add_btn(devices_list, NULL, "OLED TV ANDROID")); - lv_group_add_obj( - group_, lv_list_add_btn(devices_list, NULL, "there could be another")); - lv_group_add_obj(group_, lv_list_add_btn(devices_list, NULL, - "this one has a really long name")); + devices_list_ = lv_list_create(content_); + RefreshDevicesList(); +} + +auto Bluetooth::ChangeEnabledState(bool enabled) -> void { + if (enabled) { + events::System().RunOnTask([&]() { bt_.Enable(); }); + nvs_.OutputMode(drivers::NvsStorage::Output::kBluetooth).get(); + } else { + events::System().RunOnTask([&]() { bt_.Disable(); }); + nvs_.OutputMode(drivers::NvsStorage::Output::kHeadphones).get(); + } + events::Audio().Dispatch(audio::OutputModeChanged{}); + RefreshDevicesList(); +} + +auto Bluetooth::RefreshDevicesList() -> void { + if (!bt_.IsEnabled()) { + // Bluetooth is disabled, so we just clear the list. + RemoveAllDevices(); + return; + } + + auto devices = bt_.KnownDevices(); + std::optional preferred_device = + nvs_.PreferredBluetoothDevice().get(); + + // If the user's current selection is within the devices list, then we need + // to be careful not to rearrange the list items underneath them. + lv_obj_t* current_selection = lv_group_get_focused(group_); + bool is_in_list = current_selection != NULL && + lv_obj_get_parent(current_selection) == devices_list_; + + if (!is_in_list) { + // The user isn't in the list! We can blow everything away and recreate it + // without issues. + RemoveAllDevices(); + + // First look to see if the user's preferred device is in the list. If it + // is, we hoist it up to the top of the list. + if (preferred_device) { + for (size_t i = 0; i < devices.size(); i++) { + if (devices[i].address == *preferred_device) { + AddPreferredDevice(devices[i]); + devices.erase(devices.begin() + i); + break; + } + } + } + + // The rest of the list is already sorted by signal strength. + for (const auto& device : devices) { + AddDevice(device); + } + } else { + // The user's selection is within the device list. We need to work out + // which devices are new, then add them to the end. + for (const auto& mac : macs_in_list_) { + auto pos = std::find_if( + devices.begin(), devices.end(), + [&mac](const auto& device) { return device.address == mac; }); + + if (pos != devices.end()) { + devices.erase(pos); + } + } + + // The remaining list is now just the new devices. + for (const auto& device : devices) { + if (preferred_device && device.address == *preferred_device) { + AddPreferredDevice(device); + } else { + AddDevice(device); + } + } + } +} + +auto Bluetooth::RemoveAllDevices() -> void { + while (lv_obj_get_child_cnt(devices_list_) > 0) { + lv_obj_del(lv_obj_get_child(devices_list_, 0)); + } + macs_in_list_.clear(); + preferred_device_ = nullptr; +} + +auto Bluetooth::AddPreferredDevice(const drivers::bluetooth::Device& dev) + -> void { + preferred_device_ = lv_list_add_btn(devices_list_, NULL, dev.name.c_str()); + macs_in_list_.push_back(dev.address); +} + +auto Bluetooth::AddDevice(const drivers::bluetooth::Device& dev) -> void { + lv_obj_t* item = lv_list_add_btn(devices_list_, NULL, dev.name.c_str()); + lv_group_add_obj(group_, item); + lv_obj_add_event_cb(item, select_device_cb, LV_EVENT_CLICKED, this); + macs_in_list_.push_back(dev.address); +} + +auto Bluetooth::OnDeviceSelected(size_t index) -> void { + // Tell the bluetooth driver that our preference changed. + auto it = macs_in_list_.begin(); + std::advance(it, index); + events::System().RunOnTask([=]() { bt_.SetPreferredDevice(*it); }); + + // Update which devices are selectable. + if (preferred_device_) { + lv_group_add_obj(group_, preferred_device_); + // Bubble the newly added object up to its visible position in the list. + size_t pos = lv_obj_get_index(preferred_device_); + while (pos > 0) { + lv_group_swap_obj(preferred_device_, + lv_obj_get_child(devices_list_, pos - 1)); + pos--; + } + } + + preferred_device_ = lv_obj_get_child(devices_list_, index); + lv_group_remove_obj(preferred_device_); } static void change_vol_limit_cb(lv_event_t* ev) { -- cgit v1.2.3