make bluetooth pairing ui functional
This commit is contained in:
+23
-2
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,8 @@ struct ChangeMaxVolume : tinyfsm::Event {
|
||||
|
||||
struct TogglePlayPause : tinyfsm::Event {};
|
||||
|
||||
struct OutputModeChanged : tinyfsm::Event {};
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct InputFileOpened : tinyfsm::Event {};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -16,5 +16,10 @@ struct Device {
|
||||
int8_t signal_strength;
|
||||
};
|
||||
|
||||
enum class Event {
|
||||
kKnownDevicesChanged,
|
||||
kConnectionStateChanged,
|
||||
};
|
||||
|
||||
} // namespace bluetooth
|
||||
} // namespace drivers
|
||||
|
||||
+3
-2
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+140
-12
@@ -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) {
|
||||
|
||||
+12
-1
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user