Refactor native windows (#148)

* Error on windows scan

* Implement thread safe cache handling

* Fix start scan method

* Update windows/src/universal_ble_thread_safe.h

Co-authored-by: Foti Dim <foti@navideck.com>

* Refactor native Windows

* Fix guid parsing

* Fix FlutterError on Windows

* Fix Merge Conflicts

* Fix char subscription and store in characteristic struct instead of global map

---------

Co-authored-by: Foti Dim <fdimanidis@gmail.com>
Co-authored-by: Foti Dim <foti@navideck.com>
This commit is contained in:
Rohit Sangwan
2025-03-19 15:53:22 +05:30
committed by GitHub
parent 0b51a5bbbd
commit 3ce064d20f
7 changed files with 911 additions and 957 deletions
+1
View File
@@ -45,6 +45,7 @@ list(APPEND PLUGIN_SOURCES
"src/universal_ble_filter_util.cpp"
"src/universal_ble_filter_util.h"
"src/universal_ble_thread_safe.h"
"src/enum_parser.h"
)
add_library(${PLUGIN_NAME} SHARED
+127
View File
@@ -0,0 +1,127 @@
#pragma once
#include <string>
namespace universal_ble
{
inline std::string device_watcher_status_to_string(const DeviceWatcherStatus result)
{
switch (result)
{
case DeviceWatcherStatus::Created: return "Created";
case DeviceWatcherStatus::Aborted: return "Aborted";
case DeviceWatcherStatus::EnumerationCompleted: return "EnumerationCompleted";
case DeviceWatcherStatus::Started: return "Started";
case DeviceWatcherStatus::Stopped: return "Stopped";
case DeviceWatcherStatus::Stopping: return "Stopping";
}
return "";
}
inline std::optional<std::string> gatt_communication_status_to_error(const GattCommunicationStatus result)
{
switch (result)
{
case GattCommunicationStatus::Success: return std::nullopt;
case GattCommunicationStatus::Unreachable: return "Unreachable";
case GattCommunicationStatus::ProtocolError: return "ProtocolError";
case GattCommunicationStatus::AccessDenied: return "AccessDenied";
}
return std::nullopt;
}
inline std::optional<std::string> device_unpairing_result_to_string(const DeviceUnpairingResultStatus result)
{
switch (result)
{
case DeviceUnpairingResultStatus::Failed: return "Failed to unpair device";
case DeviceUnpairingResultStatus::AccessDenied: return "Access denied";
case DeviceUnpairingResultStatus::AlreadyUnpaired: return "Device is already unpaired";
case DeviceUnpairingResultStatus::OperationAlreadyInProgress: return "OperationAlreadyInProgress";
case DeviceUnpairingResultStatus::Unpaired: return std::nullopt;
}
return std::nullopt;
}
inline std::optional<std::string> parse_pairing_fail_error(const DevicePairingResult& result)
{
switch (result.Status())
{
case DevicePairingResultStatus::Paired: return std::nullopt;
case DevicePairingResultStatus::AlreadyPaired: return "AlreadyPaired";
case DevicePairingResultStatus::ConnectionRejected: return "ConnectionRejected";
case DevicePairingResultStatus::NotPaired: return "NotPaired";
case DevicePairingResultStatus::NotReadyToPair: return "NotReadyToPair";
case DevicePairingResultStatus::TooManyConnections: return "TooManyConnections";
case DevicePairingResultStatus::HardwareFailure: return "HardwareFailure";
case DevicePairingResultStatus::AuthenticationTimeout: return "AuthenticationTimeout";
case DevicePairingResultStatus::AuthenticationNotAllowed: return "AuthenticationNotAllowed";
case DevicePairingResultStatus::AuthenticationFailure: return "AuthenticationFailure";
case DevicePairingResultStatus::NoSupportedProfiles: return "NoSupportedProfiles";
case DevicePairingResultStatus::ProtectionLevelCouldNotBeMet: return "ProtectionLevelCouldNotBeMet";
case DevicePairingResultStatus::AccessDenied: return "AccessDenied";
case DevicePairingResultStatus::InvalidCeremonyData: return "InvalidCeremonyData";
case DevicePairingResultStatus::PairingCanceled: return "PairingCanceled";
case DevicePairingResultStatus::OperationAlreadyInProgress: return "OperationAlreadyInProgress";
case DevicePairingResultStatus::RequiredHandlerNotRegistered: return "RequiredHandlerNotRegistered";
case DevicePairingResultStatus::RejectedByHandler: return "RejectedByHandler";
case DevicePairingResultStatus::RemoteDeviceHasAssociation: return "RemoteDeviceHasAssociation";
default: return "Failed to pair";
}
}
inline AvailabilityState get_availability_state_from_radio(const RadioState radio_state)
{
switch (radio_state)
{
case RadioState::On: return AvailabilityState::poweredOn;
case RadioState::Off: return AvailabilityState::poweredOff;
case RadioState::Disabled: return AvailabilityState::unsupported;
case RadioState::Unknown: return AvailabilityState::unknown;
}
return AvailabilityState::unknown;
}
inline flutter::EncodableList properties_to_flutter_encodable (const GattCharacteristicProperties properties_value)
{
auto properties = flutter::EncodableList();
if ((properties_value & GattCharacteristicProperties::Broadcast) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::broadcast));
}
if ((properties_value & GattCharacteristicProperties::Read) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::read));
}
if ((properties_value & GattCharacteristicProperties::Write) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::write));
}
if ((properties_value & GattCharacteristicProperties::WriteWithoutResponse) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::writeWithoutResponse));
}
if ((properties_value & GattCharacteristicProperties::Notify) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::notify));
}
if ((properties_value & GattCharacteristicProperties::Indicate) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::indicate));
}
if ((properties_value & GattCharacteristicProperties::AuthenticatedSignedWrites) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::authenticatedSignedWrites));
}
if ((properties_value & GattCharacteristicProperties::ExtendedProperties) != GattCharacteristicProperties::None)
{
properties.push_back(static_cast<int>(CharacteristicProperty::extendedProperties));
}
return properties;
}
}
+27 -27
View File
@@ -21,25 +21,25 @@ typedef NTSTATUS(WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
namespace universal_ble
{
std::string _mac_address_to_str(uint64_t mac_address)
std::string mac_address_to_str(uint64_t mac_address)
{
uint8_t *mac_ptr = (uint8_t *)&mac_address;
char mac_str[MAC_ADDRESS_STR_LENGTH + 1] = {0};
uint8_t* mac_ptr = (uint8_t*)&mac_address;
char mac_str[MAC_ADDRESS_STR_LENGTH + 1] = { 0 };
snprintf(mac_str, MAC_ADDRESS_STR_LENGTH + 1, "%02x:%02x:%02x:%02x:%02x:%02x", mac_ptr[5], mac_ptr[4], mac_ptr[3],
mac_ptr[2], mac_ptr[1], mac_ptr[0]);
mac_ptr[2], mac_ptr[1], mac_ptr[0]);
return std::string(mac_str);
}
uint64_t _str_to_mac_address(std::string mac_str)
uint64_t str_to_mac_address(const std::string& mac_str)
{
uint64_t mac_address_number = 0;
uint8_t *mac_ptr = (uint8_t *)&mac_address_number;
uint8_t* mac_ptr = (uint8_t*)&mac_address_number;
sscanf_s(mac_str.c_str(), "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", &mac_ptr[5], &mac_ptr[4], &mac_ptr[3],
&mac_ptr[2], &mac_ptr[1], &mac_ptr[0]);
&mac_ptr[2], &mac_ptr[1], &mac_ptr[0]);
return mac_address_number;
}
winrt::guid uuid_to_guid(const std::string &uuid)
guid uuid_to_guid(const std::string &uuid)
{
std::stringstream helper;
for (int i = 0; i < uuid.length(); i++)
@@ -51,7 +51,7 @@ namespace universal_ble
}
std::string clean_uuid = helper.str();
winrt::guid guid;
uint64_t *data4_ptr = (uint64_t *)guid.Data4;
uint64_t* data4_ptr = (uint64_t*)guid.Data4;
guid.Data1 = static_cast<uint32_t>(std::strtoul(clean_uuid.substr(0, 8).c_str(), nullptr, 16));
guid.Data2 = static_cast<uint16_t>(std::strtoul(clean_uuid.substr(8, 4).c_str(), nullptr, 16));
@@ -61,22 +61,22 @@ namespace universal_ble
return guid;
}
std::string guid_to_uuid(const winrt::guid &guid)
std::string guid_to_uuid(const guid &guid)
{
std::stringstream helper;
for (uint32_t i = 0; i < 4; i++)
{
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t *)&guid.Data1)[3 - i];
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t*)&guid.Data1)[3 - i];
}
helper << '-';
for (uint32_t i = 0; i < 2; i++)
{
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t *)&guid.Data2)[1 - i];
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t*)&guid.Data2)[1 - i];
}
helper << '-';
for (uint32_t i = 0; i < 2; i++)
{
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t *)&guid.Data3)[1 - i];
helper << std::hex << std::setw(2) << std::setfill('0') << (int)((uint8_t*)&guid.Data3)[1 - i];
}
helper << '-';
for (uint32_t i = 0; i < 2; i++)
@@ -91,7 +91,7 @@ namespace universal_ble
return helper.str();
}
std::vector<uint8_t> to_bytevc(IBuffer buffer)
std::vector<uint8_t> to_bytevc(const IBuffer& buffer)
{
auto reader = DataReader::FromBuffer(buffer);
auto result = std::vector<uint8_t>(reader.UnconsumedBufferLength());
@@ -106,7 +106,7 @@ namespace universal_ble
return writer.DetachBuffer();
}
std::string to_hexstring(std::vector<uint8_t> bytes)
std::string to_hexstring(const std::vector<uint8_t>& bytes)
{
auto ss = std::stringstream();
for (auto b : bytes)
@@ -114,33 +114,33 @@ namespace universal_ble
return ss.str();
}
std::string to_uuidstr(winrt::guid guid)
std::string to_uuidstr(const guid guid)
{
char chars[36 + 1];
sprintf_s(chars, "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::string{chars};
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2],
guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
return std::string{ chars };
}
bool isLittleEndian()
bool is_little_endian()
{
uint16_t number = 0x1;
char *numPtr = (char *)&number;
char* numPtr = (char*)&number;
return (numPtr[0] == 1);
}
bool isWindows11OrGreater()
bool is_windows11_or_greater()
{
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (!hMod)
const HMODULE h_mod = GetModuleHandleW(L"ntdll.dll");
if (!h_mod)
{
std::cout << "Failed to get ntdll" << std::endl;
return false;
}
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr == nullptr)
const auto fx_ptr = reinterpret_cast<RtlGetVersionPtr>(GetProcAddress(h_mod, "RtlGetVersion"));
if (fx_ptr == nullptr)
{
std::cout << "Failed to get RtlGetVersionPtr" << std::endl;
return false;
@@ -148,7 +148,7 @@ namespace universal_ble
RTL_OSVERSIONINFOW rove = {0};
rove.dwOSVersionInfoSize = sizeof(rove);
if (STATUS_SUCCESS != fxPtr(&rove))
if (STATUS_SUCCESS != fx_ptr(&rove))
{
std::cout << "Failed to get RTL_OSVERSIONINFOW" << std::endl;
return false;
+15 -15
View File
@@ -14,25 +14,25 @@ constexpr uint32_t TEN_SECONDS_IN_MSECS = 10000;
namespace universal_ble
{
std::string _mac_address_to_str(uint64_t mac_address);
uint64_t _str_to_mac_address(std::string mac_address);
std::string mac_address_to_str(uint64_t mac_address);
uint64_t str_to_mac_address(const std::string& mac_str);
winrt::guid uuid_to_guid(const std::string &uuid);
std::string guid_to_uuid(const winrt::guid &guid);
guid uuid_to_guid(const std::string &uuid);
std::string guid_to_uuid(const guid &guid);
std::vector<uint8_t> to_bytevc(IBuffer buffer);
std::vector<uint8_t> to_bytevc(const IBuffer& buffer);
IBuffer from_bytevc(std::vector<uint8_t> bytes);
std::string to_hexstring(std::vector<uint8_t> bytes);
std::string to_hexstring(const std::vector<uint8_t>& bytes);
std::string to_uuidstr(winrt::guid guid);
bool isLittleEndian();
bool isWindows11OrGreater();
std::string to_uuidstr(guid guid);
bool is_little_endian();
bool is_windows11_or_greater();
/// To call async functions synchronously
template <typename async_t>
static auto async_get(async_t const &async)
template <typename AsyncT>
static auto async_get(AsyncT const &async)
{
if (async.Status() == Foundation::AsyncStatus::Started)
if (async.Status() == AsyncStatus::Started)
{
wait_for_completed(async, TEN_SECONDS_IN_MSECS);
}
@@ -40,13 +40,13 @@ namespace universal_ble
{
return async.GetResults();
}
catch (const winrt::hresult_error &err)
catch (const hresult_error &err)
{
throw FlutterError(winrt::to_string(err.message()));
throw FlutterError("Failed", to_string(err.message()));
}
catch (...)
{
throw FlutterError("Unknown error");
throw FlutterError("Failed", "Unknown error");
}
}
@@ -1,9 +1,3 @@
#include <iomanip>
#include <iostream>
#include <sstream>
#include <windows.h>
#include <stdio.h>
#include <sdkddkver.h>
#include <vector>
#include "helper/utils.h"
#include "generated/universal_ble.g.h"
File diff suppressed because it is too large Load Diff
+72 -79
View File
@@ -28,6 +28,7 @@ namespace universal_ble
struct GattCharacteristicObject
{
GattCharacteristic obj = nullptr;
std::optional<event_token> subscription_token;
};
struct GattServiceObject
@@ -39,27 +40,34 @@ namespace universal_ble
struct BluetoothDeviceAgent
{
BluetoothLEDevice device;
winrt::event_token connnectionStatusChangedToken;
std::unordered_map<std::string, GattServiceObject> gatt_map_;
event_token connection_status_changed_token;
std::unordered_map<std::string, GattServiceObject> gatt_map;
BluetoothDeviceAgent(BluetoothLEDevice device, winrt::event_token connnectionStatusChangedToken, std::unordered_map<std::string, GattServiceObject> gatt_map_)
BluetoothDeviceAgent(const BluetoothLEDevice &device, const event_token connection_status_changed_token,
const std::unordered_map<std::string, GattServiceObject> &gatt_map)
: device(device),
connnectionStatusChangedToken(connnectionStatusChangedToken),
gatt_map_(gatt_map_) {}
connection_status_changed_token(connection_status_changed_token),
gatt_map(gatt_map)
{
}
~BluetoothDeviceAgent()
{
device = nullptr;
}
GattCharacteristicObject &_fetch_characteristic(const std::string &service_uuid,
const std::string &characteristic_uuid)
GattCharacteristicObject &FetchCharacteristic(const std::string &service_uuid,
const std::string &characteristic_uuid)
{
if (gatt_map_.count(service_uuid) == 0)
throw FlutterError("Service not found");
if (gatt_map_[service_uuid].characteristics.count(characteristic_uuid) == 0)
throw FlutterError("Characteristic not found");
return gatt_map_[service_uuid].characteristics.at(characteristic_uuid);
if (gatt_map.count(service_uuid) == 0)
{
throw FlutterError("IllegalArgument", "Service not found");
}
if (gatt_map[service_uuid].characteristics.count(characteristic_uuid) == 0)
{
throw FlutterError("IllegalArgument", "Characteristic not found");
}
return gatt_map[service_uuid].characteristics.at(characteristic_uuid);
}
};
@@ -72,6 +80,12 @@ namespace universal_ble
~UniversalBlePlugin();
// Disallow copy and assign.
UniversalBlePlugin(const UniversalBlePlugin&) = delete;
UniversalBlePlugin& operator=(const UniversalBlePlugin&) = delete;
private:
static void SuccessCallback() {}
static void ErrorCallback(const FlutterError &error)
{
@@ -82,59 +96,58 @@ namespace universal_ble
}
}
// Disallow copy and assign.
UniversalBlePlugin(const UniversalBlePlugin &) = delete;
UniversalBlePlugin &operator=(const UniversalBlePlugin &) = delete;
flutter::PluginRegistrarWindows *registrar_;
bool initialized_ = false;
UniversalBleUiThreadHandler uiThreadHandler_;
Radio bluetoothRadio{nullptr};
RadioState oldRadioState = RadioState::Unknown;
BluetoothLEAdvertisementWatcher bluetoothLEWatcher{nullptr};
DeviceWatcher deviceWatcher{nullptr};
UniversalBleUiThreadHandler ui_thread_handler_;
Radio bluetooth_radio_{nullptr};
RadioState old_radio_state_ = RadioState::Unknown;
BluetoothLEAdvertisementWatcher bluetooth_le_watcher_{nullptr};
DeviceWatcher device_watcher_{nullptr};
std::unordered_map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>> connectedDevices{};
ThreadSafeMap<std::string, DeviceInformation> deviceWatcherDevices{};
ThreadSafeMap<std::string, UniversalBleScanResult> scanResults{};
std::unordered_map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>> connected_devices_{};
ThreadSafeMap<std::string, DeviceInformation> device_watcher_devices_{};
ThreadSafeMap<std::string, UniversalBleScanResult> scan_results_{};
winrt::event_token bluetoothLEWatcherReceivedToken;
winrt::event_token deviceWatcherAddedToken;
winrt::event_token deviceWatcherUpdatedToken;
winrt::event_token deviceWatcherRemovedToken;
winrt::event_token deviceWatcherEnumerationCompletedToken;
winrt::event_token deviceWatcherStoppedToken;
event_token bluetooth_le_watcher_received_token_;
event_token device_watcher_added_token_;
event_token device_watcher_updated_token_;
event_token device_watcher_removed_token_;
event_token device_watcher_enumeration_completed_token_;
event_token device_watcher_stopped_token_;
event_revoker<IRadio> radio_state_changed_revoker_;
winrt::fire_and_forget InitializeAsync();
void Radio_StateChanged(Radio sender, IInspectable args);
void setupDeviceWatcher();
void disposeDeviceWatcher();
void pushUniversalScanResult(UniversalBleScanResult scanResult, bool isConnectable);
void BluetoothLEWatcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args);
void onDeviceInfoReceived(DeviceInformation deviceInfo);
fire_and_forget InitializeAsync();
fire_and_forget ConnectAsync(uint64_t bluetooth_address);
fire_and_forget SetNotifiableAsync(
const std::string& device_id,
const std::string& service,
const std::string& characteristic,
int64_t ble_input_property,
std::function<void(std::optional<FlutterError> reply)> result);
fire_and_forget PairAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
fire_and_forget CustomPairAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
static fire_and_forget GetSystemDevicesAsync(
std::vector<std::string> with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
static fire_and_forget IsPairedAsync(const std::string& device_id, std::function<void(ErrorOr<bool> reply)> result);
std::string GattCommunicationStatusToString(GattCommunicationStatus status);
winrt::event_revoker<IRadio> radioStateChangedRevoker;
winrt::fire_and_forget ConnectAsync(uint64_t bluetoothAddress);
void BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args);
void CleanConnection(uint64_t bluetoothAddress);
void DiscoverServicesAsync(BluetoothDeviceAgent &bluetoothDeviceAgent, std::function<void(ErrorOr<flutter::EncodableList> reply)>);
winrt::fire_and_forget SetNotifiableAsync(BluetoothDeviceAgent &bluetoothDeviceAgent, const std::string &service,
const std::string &characteristic, GattClientCharacteristicConfigurationDescriptorValue descriptorValue,
std::function<void(std::optional<FlutterError> reply)> result);
void GattCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args);
AvailabilityState getAvailabilityStateFromRadio(RadioState radioState);
std::string parsePairingFailError(Enumeration::DevicePairingResult result);
winrt::fire_and_forget GetSystemDevicesAsync(std::vector<std::string> with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
winrt::fire_and_forget IsPairedAsync(std::string device_id, std::function<void(ErrorOr<bool> reply)> result);
winrt::fire_and_forget WriteAsync(GattCharacteristic characteristic, GattWriteOption writeOption,
const std::vector<uint8_t> &value,
std::function<void(std::optional<FlutterError> reply)> result);
winrt::fire_and_forget PairAsync(std::string device_id, std::function<void(ErrorOr<bool> reply)> result);
winrt::fire_and_forget CustomPairAsync(std::string device_id, std::function<void(ErrorOr<bool> reply)> result);
void PairingRequestedHandler(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs eventArgs);
static void DiscoverServicesAsync(BluetoothDeviceAgent& bluetooth_device_agent, const std::function<void(ErrorOr<flutter::EncodableList> reply)>&);
void PairingRequestedHandler(DeviceInformationCustomPairing sender, const DevicePairingRequestedEventArgs& event_args);
void RadioStateChanged(const Radio& sender, const IInspectable&);
void SetupDeviceWatcher();
void DisposeDeviceWatcher();
void PushUniversalScanResult(UniversalBleScanResult scan_result, bool is_connectable);
void BluetoothLeWatcherReceived(const BluetoothLEAdvertisementWatcher& sender, const
BluetoothLEAdvertisementReceivedEventArgs& args);
void OnDeviceInfoReceived(const DeviceInformation& device_info);
void BluetoothLeDeviceConnectionStatusChanged(const BluetoothLEDevice& sender, const IInspectable& args);
void CleanConnection(uint64_t bluetooth_address);
void GattCharacteristicValueChanged(const GattCharacteristic& sender, const GattValueChangedEventArgs& args);
// UniversalBlePlatformChannel implementation.
void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) override;
@@ -179,27 +192,7 @@ namespace universal_ble
std::optional<FlutterError> UnPair(const std::string &device_id) override;
void GetSystemDevices(
const flutter::EncodableList &with_services,
std::function<void(ErrorOr<flutter::EncodableList> reply)> result);
std::string DeviceWatcherStatusToString(DeviceWatcherStatus result)
{
switch (result)
{
case DeviceWatcherStatus::Created:
return "Created";
case DeviceWatcherStatus::Aborted:
return "Aborted";
case DeviceWatcherStatus::EnumerationCompleted:
return "EnumerationCompleted";
case DeviceWatcherStatus::Started:
return "Started";
case DeviceWatcherStatus::Stopped:
return "Stopped";
case DeviceWatcherStatus::Stopping:
return "Stopping";
}
return "";
}
std::function<void(ErrorOr<flutter::EncodableList> reply)> result) override;
};
} // namespace universal_ble