summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/audio/audio_fsm.cpp25
-rw-r--r--src/audio/bt_audio_output.cpp6
-rw-r--r--src/audio/include/audio_events.hpp2
-rw-r--r--src/audio/include/audio_fsm.hpp2
-rw-r--r--src/audio/include/bt_audio_output.hpp4
-rw-r--r--src/drivers/bluetooth.cpp34
-rw-r--r--src/drivers/include/bluetooth.hpp9
-rw-r--r--src/drivers/include/bluetooth_types.hpp5
-rw-r--r--src/drivers/nvs.cpp5
-rw-r--r--src/events/include/event_queue.hpp4
-rw-r--r--src/system_fsm/booting.cpp19
-rw-r--r--src/system_fsm/include/system_events.hpp2
-rw-r--r--src/ui/include/screen_settings.hpp22
-rw-r--r--src/ui/include/ui_fsm.hpp9
-rw-r--r--src/ui/screen_settings.cpp152
-rw-r--r--src/ui/ui_fsm.cpp13
16 files changed, 278 insertions, 35 deletions
diff --git a/src/audio/audio_fsm.cpp b/src/audio/audio_fsm.cpp
index af43c9b9..06b98420 100644
--- a/src/audio/audio_fsm.cpp
+++ b/src/audio/audio_fsm.cpp
@@ -26,6 +26,7 @@
#include "future_fetcher.hpp"
#include "i2s_audio_output.hpp"
#include "i2s_dac.hpp"
+#include "nvs.hpp"
#include "service_locator.hpp"
#include "system_events.hpp"
#include "track.hpp"
@@ -42,6 +43,7 @@ std::shared_ptr<FatfsAudioInput> AudioState::sFileSource;
std::unique_ptr<Decoder> AudioState::sDecoder;
std::shared_ptr<SampleConverter> AudioState::sSampleConverter;
std::shared_ptr<I2SAudioOutput> AudioState::sI2SOutput;
+std::shared_ptr<BluetoothAudioOutput> AudioState::sBtOutput;
std::shared_ptr<IAudioOutput> AudioState::sOutput;
std::optional<database::TrackId> AudioState::sCurrentTrack;
@@ -75,6 +77,20 @@ void AudioState::react(const ChangeMaxVolume& ev) {
sServices->nvs().AmpMaxVolume(ev.new_max);
}
+void AudioState::react(const OutputModeChanged& ev) {
+ // TODO: handle SetInUse
+ ESP_LOGI(kTag, "output mode changed");
+ auto new_mode = sServices->nvs().OutputMode();
+ switch (new_mode.get()) {
+ case drivers::NvsStorage::Output::kBluetooth:
+ sOutput = sBtOutput;
+ break;
+ case drivers::NvsStorage::Output::kHeadphones:
+ sOutput = sI2SOutput;
+ break;
+ }
+}
+
namespace states {
void Uninitialised::react(const system_fsm::BootComplete& ev) {
@@ -90,13 +106,18 @@ void Uninitialised::react(const system_fsm::BootComplete& ev) {
sFileSource.reset(new FatfsAudioInput(sServices->tag_parser()));
sI2SOutput.reset(new I2SAudioOutput(sServices->gpios(),
std::unique_ptr<drivers::I2SDac>{*dac}));
+ sBtOutput.reset(new BluetoothAudioOutput(sServices->bluetooth()));
auto& nvs = sServices->nvs();
sI2SOutput->SetMaxVolume(nvs.AmpMaxVolume().get());
sI2SOutput->SetVolumeDb(nvs.AmpCurrentVolume().get());
- sOutput = sI2SOutput;
- // sOutput.reset(new BluetoothAudioOutput(bluetooth));
+ if (sServices->nvs().OutputMode().get() ==
+ drivers::NvsStorage::Output::kHeadphones) {
+ sOutput = sI2SOutput;
+ } else {
+ sOutput = sBtOutput;
+ }
sSampleConverter.reset(new SampleConverter());
sSampleConverter->SetOutput(sOutput);
diff --git a/src/audio/bt_audio_output.cpp b/src/audio/bt_audio_output.cpp
index 2e54f69a..374906fd 100644
--- a/src/audio/bt_audio_output.cpp
+++ b/src/audio/bt_audio_output.cpp
@@ -29,16 +29,16 @@ namespace audio {
static constexpr size_t kDrainBufferSize = 48 * 1024;
-BluetoothAudioOutput::BluetoothAudioOutput(drivers::Bluetooth* bt)
+BluetoothAudioOutput::BluetoothAudioOutput(drivers::Bluetooth& bt)
: IAudioOutput(kDrainBufferSize, MALLOC_CAP_SPIRAM), bluetooth_(bt) {}
BluetoothAudioOutput::~BluetoothAudioOutput() {}
auto BluetoothAudioOutput::SetInUse(bool in_use) -> void {
if (in_use) {
- bluetooth_->SetSource(stream());
+ bluetooth_.SetSource(stream());
} else {
- bluetooth_->SetSource(nullptr);
+ bluetooth_.SetSource(nullptr);
}
}
diff --git a/src/audio/include/audio_events.hpp b/src/audio/include/audio_events.hpp
index db6a3297..5af419ab 100644
--- a/src/audio/include/audio_events.hpp
+++ b/src/audio/include/audio_events.hpp
@@ -41,6 +41,8 @@ struct ChangeMaxVolume : tinyfsm::Event {
struct TogglePlayPause : tinyfsm::Event {};
+struct OutputModeChanged : tinyfsm::Event {};
+
namespace internal {
struct InputFileOpened : tinyfsm::Event {};
diff --git a/src/audio/include/audio_fsm.hpp b/src/audio/include/audio_fsm.hpp
index 46d3d338..1c0b8aaa 100644
--- a/src/audio/include/audio_fsm.hpp
+++ b/src/audio/include/audio_fsm.hpp
@@ -45,6 +45,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
void react(const system_fsm::KeyDownChanged&);
void react(const system_fsm::HasPhonesChanged&);
void react(const ChangeMaxVolume&);
+ void react(const OutputModeChanged&);
virtual void react(const system_fsm::BootComplete&) {}
@@ -65,6 +66,7 @@ class AudioState : public tinyfsm::Fsm<AudioState> {
static std::unique_ptr<Decoder> sDecoder;
static std::shared_ptr<SampleConverter> sSampleConverter;
static std::shared_ptr<I2SAudioOutput> sI2SOutput;
+ static std::shared_ptr<BluetoothAudioOutput> sBtOutput;
static std::shared_ptr<IAudioOutput> sOutput;
static std::optional<database::TrackId> sCurrentTrack;
diff --git a/src/audio/include/bt_audio_output.hpp b/src/audio/include/bt_audio_output.hpp
index a8e44f31..734a7ed1 100644
--- a/src/audio/include/bt_audio_output.hpp
+++ b/src/audio/include/bt_audio_output.hpp
@@ -21,7 +21,7 @@ namespace audio {
class BluetoothAudioOutput : public IAudioOutput {
public:
- BluetoothAudioOutput(drivers::Bluetooth* bt);
+ BluetoothAudioOutput(drivers::Bluetooth& bt);
~BluetoothAudioOutput();
auto SetInUse(bool) -> void override;
@@ -39,7 +39,7 @@ class BluetoothAudioOutput : public IAudioOutput {
BluetoothAudioOutput& operator=(const BluetoothAudioOutput&) = delete;
private:
- drivers::Bluetooth* bluetooth_;
+ drivers::Bluetooth& bluetooth_;
};
} // namespace audio
diff --git a/src/drivers/bluetooth.cpp b/src/drivers/bluetooth.cpp
index f3373849..924cdf42 100644
--- a/src/drivers/bluetooth.cpp
+++ b/src/drivers/bluetooth.cpp
@@ -8,6 +8,7 @@
#include <ostream>
#include <sstream>
+#include "bluetooth_types.hpp"
#include "esp_a2dp_api.h"
#include "esp_avrc_api.h"
#include "esp_bt.h"
@@ -56,7 +57,7 @@ auto a2dp_data_cb(uint8_t* buf, int32_t buf_size) -> int32_t {
return xStreamBufferReceive(stream, buf, buf_size, 0);
}
-Bluetooth::Bluetooth(NvsStorage* storage) {
+Bluetooth::Bluetooth(NvsStorage& storage) {
bluetooth::BluetoothState::Init(storage);
}
@@ -72,6 +73,10 @@ auto Bluetooth::Disable() -> void {
bluetooth::events::Disable{});
}
+auto Bluetooth::IsEnabled() -> bool {
+ return !bluetooth::BluetoothState::is_in_state<bluetooth::Disabled>();
+}
+
auto Bluetooth::KnownDevices() -> std::vector<bluetooth::Device> {
std::vector<bluetooth::Device> out = bluetooth::BluetoothState::devices();
std::sort(out.begin(), out.end(), [](const auto& a, const auto& b) -> bool {
@@ -98,6 +103,11 @@ auto Bluetooth::SetSource(StreamBufferHandle_t src) -> void {
bluetooth::events::SourceChanged{});
}
+auto Bluetooth::SetEventHandler(std::function<void(bluetooth::Event)> cb)
+ -> void {
+ bluetooth::BluetoothState::event_handler(cb);
+}
+
auto DeviceName() -> std::string {
uint8_t mac[8]{0};
esp_efuse_mac_get_default(mac);
@@ -111,16 +121,17 @@ namespace bluetooth {
NvsStorage* BluetoothState::sStorage_;
-std::mutex BluetoothState::sDevicesMutex_;
-std::map<mac_addr_t, Device> BluetoothState::sDevices_;
-std::optional<mac_addr_t> BluetoothState::sPreferredDevice_;
+std::mutex BluetoothState::sDevicesMutex_{};
+std::map<mac_addr_t, Device> BluetoothState::sDevices_{};
+std::optional<mac_addr_t> BluetoothState::sPreferredDevice_{};
mac_addr_t BluetoothState::sCurrentDevice_;
std::atomic<StreamBufferHandle_t> BluetoothState::sSource_;
+std::function<void(Event)> BluetoothState::sEventHandler_;
-auto BluetoothState::Init(NvsStorage* storage) -> void {
- sStorage_ = storage;
- sPreferredDevice_ = storage->PreferredBluetoothDevice().get();
+auto BluetoothState::Init(NvsStorage& storage) -> void {
+ sStorage_ = &storage;
+ sPreferredDevice_ = storage.PreferredBluetoothDevice().get();
tinyfsm::FsmList<bluetooth::BluetoothState>::start();
}
@@ -153,6 +164,11 @@ auto BluetoothState::source(StreamBufferHandle_t src) -> void {
sSource_.store(src);
}
+auto BluetoothState::event_handler(std::function<void(Event)> cb) -> void {
+ std::lock_guard lock{sDevicesMutex_};
+ sEventHandler_ = cb;
+}
+
static bool sIsFirstEntry = true;
void Disabled::entry() {
@@ -303,6 +319,10 @@ auto Scanning::OnDeviceDiscovered(esp_bt_gap_cb_param_t* param) -> void {
sCurrentDevice_ = device.address;
is_preferred = true;
}
+
+ if (sEventHandler_) {
+ std::invoke(sEventHandler_, Event::kKnownDevicesChanged);
+ }
}
if (is_preferred) {
diff --git a/src/drivers/include/bluetooth.hpp b/src/drivers/include/bluetooth.hpp
index fc72d393..1489b790 100644
--- a/src/drivers/include/bluetooth.hpp
+++ b/src/drivers/include/bluetooth.hpp
@@ -26,15 +26,17 @@ namespace drivers {
*/
class Bluetooth {
public:
- Bluetooth(NvsStorage* storage);
+ Bluetooth(NvsStorage& storage);
auto Enable() -> bool;
auto Disable() -> void;
+ auto IsEnabled() -> bool;
auto KnownDevices() -> std::vector<bluetooth::Device>;
auto SetPreferredDevice(const bluetooth::mac_addr_t& mac) -> void;
auto SetSource(StreamBufferHandle_t) -> void;
+ auto SetEventHandler(std::function<void(bluetooth::Event)> cb) -> void;
};
namespace bluetooth {
@@ -64,7 +66,7 @@ struct Avrc : public tinyfsm::Event {
class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
public:
- static auto Init(NvsStorage* storage) -> void;
+ static auto Init(NvsStorage& storage) -> void;
static auto devices() -> std::vector<Device>;
static auto preferred_device() -> std::optional<mac_addr_t>;
@@ -73,6 +75,8 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
static auto source() -> StreamBufferHandle_t;
static auto source(StreamBufferHandle_t) -> void;
+ static auto event_handler(std::function<void(Event)>) -> void;
+
virtual ~BluetoothState(){};
virtual void entry() {}
@@ -96,6 +100,7 @@ class BluetoothState : public tinyfsm::Fsm<BluetoothState> {
static mac_addr_t sCurrentDevice_;
static std::atomic<StreamBufferHandle_t> sSource_;
+ static std::function<void(Event)> sEventHandler_;
};
class Disabled : public BluetoothState {
diff --git a/src/drivers/include/bluetooth_types.hpp b/src/drivers/include/bluetooth_types.hpp
index 03100651..12ed5cb3 100644
--- a/src/drivers/include/bluetooth_types.hpp
+++ b/src/drivers/include/bluetooth_types.hpp
@@ -16,5 +16,10 @@ struct Device {
int8_t signal_strength;
};
+enum class Event {
+ kKnownDevicesChanged,
+ kConnectionStateChanged,
+};
+
} // namespace bluetooth
} // namespace drivers
diff --git a/src/drivers/nvs.cpp b/src/drivers/nvs.cpp
index 11dde08c..67867c07 100644
--- a/src/drivers/nvs.cpp
+++ b/src/drivers/nvs.cpp
@@ -128,7 +128,7 @@ auto NvsStorage::OutputMode() -> std::future<Output> {
nvs_get_u8(handle_, kKeyOutput, &out);
switch (out) {
case static_cast<uint8_t>(Output::kBluetooth):
- return Output::kHeadphones;
+ return Output::kBluetooth;
case static_cast<uint8_t>(Output::kHeadphones):
default:
return Output::kHeadphones;
@@ -138,7 +138,8 @@ auto NvsStorage::OutputMode() -> std::future<Output> {
auto NvsStorage::OutputMode(Output out) -> std::future<bool> {
return writer_->Dispatch<bool>([&]() {
- nvs_set_u8(handle_, kKeyOutput, static_cast<uint8_t>(out));
+ uint8_t as_int = static_cast<uint8_t>(out);
+ nvs_set_u8(handle_, kKeyOutput, as_int);
return nvs_commit(handle_) == ESP_OK;
});
}
diff --git a/src/events/include/event_queue.hpp b/src/events/include/event_queue.hpp
index 1ea67446..332f7be5 100644
--- a/src/events/include/event_queue.hpp
+++ b/src/events/include/event_queue.hpp
@@ -80,6 +80,10 @@ class Dispatcher {
queue_->Add(dispatch_fn);
}
+ auto RunOnTask(const std::function<void(void)>& fn) -> void {
+ queue_->Add(fn);
+ }
+
Dispatcher(Dispatcher const&) = delete;
void operator=(Dispatcher const&) = delete;
diff --git a/src/system_fsm/booting.cpp b/src/system_fsm/booting.cpp
index 006ed395..28cd8cf6 100644
--- a/src/system_fsm/booting.cpp
+++ b/src/system_fsm/booting.cpp
@@ -11,6 +11,7 @@
#include "audio_fsm.hpp"
#include "battery.hpp"
#include "bluetooth.hpp"
+#include "bluetooth_types.hpp"
#include "core/lv_obj.h"
#include "display_init.hpp"
#include "esp_err.h"
@@ -41,6 +42,12 @@ namespace states {
static const char kTag[] = "BOOT";
+static auto bt_event_cb(drivers::bluetooth::Event ev) -> void {
+ if (ev == drivers::bluetooth::Event::kKnownDevicesChanged) {
+ events::Ui().Dispatch(BluetoothDevicesChanged{});
+ }
+}
+
auto Booting::entry() -> void {
ESP_LOGI(kTag, "beginning tangara boot");
sServices.reset(new ServiceLocator());
@@ -71,9 +78,15 @@ auto Booting::entry() -> void {
sServices->track_queue(std::make_unique<audio::TrackQueue>());
sServices->tag_parser(std::make_unique<database::TagParserImpl>());
- // ESP_LOGI(kTag, "starting bluetooth");
- // sBluetooth.reset(new drivers::Bluetooth(sNvs.get()));
- // sBluetooth->Enable();
+ ESP_LOGI(kTag, "init bluetooth");
+ sServices->bluetooth(std::make_unique<drivers::Bluetooth>(sServices->nvs()));
+ sServices->bluetooth().SetEventHandler(bt_event_cb);
+
+ if (sServices->nvs().OutputMode().get() ==
+ drivers::NvsStorage::Output::kBluetooth) {
+ ESP_LOGI(kTag, "enabling bluetooth");
+ sServices->bluetooth().Enable();
+ }
BootComplete ev{.services = sServices};
events::Audio().Dispatch(ev);
diff --git a/src/system_fsm/include/system_events.hpp b/src/system_fsm/include/system_events.hpp
index e22fe2ae..9cd75d4d 100644
--- a/src/system_fsm/include/system_events.hpp
+++ b/src/system_fsm/include/system_events.hpp
@@ -56,6 +56,8 @@ struct HasPhonesChanged : tinyfsm::Event {
struct ChargingStatusChanged : tinyfsm::Event {};
struct BatteryStateChanged : tinyfsm::Event {};
+struct BluetoothDevicesChanged : tinyfsm::Event {};
+
namespace internal {
struct GpioInterrupt : tinyfsm::Event {};
diff --git a/src/ui/include/screen_settings.hpp b/src/ui/include/screen_settings.hpp
index caa23fd4..4e1936a2 100644
--- a/src/ui/include/screen_settings.hpp
+++ b/src/ui/include/screen_settings.hpp
@@ -8,9 +8,12 @@
#include <stdint.h>
#include <cstdint>
+#include <list>
#include <memory>
#include <vector>
+#include "bluetooth.hpp"
+#include "bluetooth_types.hpp"
#include "display.hpp"
#include "index.hpp"
#include "lvgl.h"
@@ -28,7 +31,24 @@ class Settings : public MenuScreen {
class Bluetooth : public MenuScreen {
public:
- Bluetooth();
+ Bluetooth(drivers::Bluetooth& bt, drivers::NvsStorage& nvs);
+
+ auto ChangeEnabledState(bool enabled) -> void;
+ auto RefreshDevicesList() -> void;
+ auto OnDeviceSelected(size_t index) -> void;
+
+ private:
+ auto RemoveAllDevices() -> void;
+ auto AddPreferredDevice(const drivers::bluetooth::Device&) -> void;
+ auto AddDevice(const drivers::bluetooth::Device&) -> void;
+
+ drivers::Bluetooth& bt_;
+ drivers::NvsStorage& nvs_;
+
+ lv_obj_t* devices_list_;
+ lv_obj_t* preferred_device_;
+
+ std::list<drivers::bluetooth::mac_addr_t> macs_in_list_;
};
class Headphones : public MenuScreen {
diff --git a/src/ui/include/ui_fsm.hpp b/src/ui/include/ui_fsm.hpp
index 5363e1a4..9980dac6 100644
--- a/src/ui/include/ui_fsm.hpp
+++ b/src/ui/include/ui_fsm.hpp
@@ -17,6 +17,7 @@
#include "nvs.hpp"
#include "relative_wheel.hpp"
#include "screen_playing.hpp"
+#include "screen_settings.hpp"
#include "service_locator.hpp"
#include "tinyfsm.hpp"
@@ -72,6 +73,7 @@ class UiState : public tinyfsm::Fsm<UiState> {
virtual void react(const system_fsm::DisplayReady&) {}
virtual void react(const system_fsm::BootComplete&) {}
virtual void react(const system_fsm::StorageMounted&) {}
+ virtual void react(const system_fsm::BluetoothDevicesChanged&) {}
protected:
void PushScreen(std::shared_ptr<Screen>);
@@ -112,6 +114,7 @@ class Onboarding : public UiState {
};
class Browse : public UiState {
+ public:
void entry() override;
void react(const internal::RecordSelected&) override;
@@ -122,10 +125,16 @@ class Browse : public UiState {
void react(const internal::ShowSettingsPage&) override;
void react(const system_fsm::StorageMounted&) override;
+ void react(const system_fsm::BluetoothDevicesChanged&) override;
+
using UiState::react;
+
+ private:
+ std::weak_ptr<screens::Bluetooth> bluetooth_screen_;
};
class Playing : public UiState {
+ public:
void entry() override;
void exit() override;
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 <string>
#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<Bluetooth*>(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<Bluetooth*>(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<drivers::bluetooth::mac_addr_t> 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) {
diff --git a/src/ui/ui_fsm.cpp b/src/ui/ui_fsm.cpp
index c0c06bb0..18e9caf4 100644
--- a/src/ui/ui_fsm.cpp
+++ b/src/ui/ui_fsm.cpp
@@ -241,12 +241,16 @@ void Browse::react(const internal::ShowNowPlaying& ev) {
void Browse::react(const internal::ShowSettingsPage& ev) {
std::shared_ptr<Screen> screen;
+ std::shared_ptr<screens::Bluetooth> bt_screen;
switch (ev.page) {
case internal::ShowSettingsPage::Page::kRoot:
screen.reset(new screens::Settings());
break;
case internal::ShowSettingsPage::Page::kBluetooth:
- screen.reset(new screens::Bluetooth());
+ bt_screen = std::make_shared<screens::Bluetooth>(sServices->bluetooth(),
+ sServices->nvs());
+ screen = bt_screen;
+ bluetooth_screen_ = bt_screen;
break;
case internal::ShowSettingsPage::Page::kHeadphones:
screen.reset(new screens::Headphones(sServices->nvs()));
@@ -315,6 +319,13 @@ void Browse::react(const internal::BackPressed& ev) {
PopScreen();
}
+void Browse::react(const system_fsm::BluetoothDevicesChanged&) {
+ auto bt = bluetooth_screen_.lock();
+ if (bt) {
+ bt->RefreshDevicesList();
+ }
+}
+
static std::shared_ptr<screens::Playing> sPlayingScreen;
void Playing::entry() {