Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Pair Method #33

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import io.flutter.plugin.common.MethodChannel.Result
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.*
import android.content.Intent
import android.content.IntentFilter
import android.content.BroadcastReceiver
import android.bluetooth.BluetoothDevice.*

private const val TAG = "QuickBluePlugin"

Expand All @@ -42,6 +46,7 @@ class QuickBluePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHand
context = flutterPluginBinding.applicationContext
mainThreadHandler = Handler(Looper.getMainLooper())
bluetoothManager = flutterPluginBinding.applicationContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
listenToBondStateChanges(context)
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
Expand Down Expand Up @@ -74,6 +79,15 @@ class QuickBluePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHand
bluetoothManager.adapter.bluetoothLeScanner?.stopScan(scanCallback)
result.success(null)
}
"pair" -> {
val deviceId = call.argument<String>("deviceId")!!
val remoteDevice : BluetoothDevice = bluetoothManager.adapter.getRemoteDevice(deviceId)
var requiresPairing = remoteDevice.bondState == BOND_NONE
if(requiresPairing){
remoteDevice.createBond()
}
result.success(null)
}
"connect" -> {
val deviceId = call.argument<String>("deviceId")!!
if (knownGatts.find { it.device.address == deviceId } != null) {
Expand Down Expand Up @@ -159,6 +173,35 @@ class QuickBluePlugin: FlutterPlugin, MethodCallHandler, EventChannel.StreamHand
}
}

fun listenToBondStateChanges(context: Context) {
context.applicationContext.registerReceiver(
broadcastReceiver,
IntentFilter(ACTION_BOND_STATE_CHANGED)
)
}

private val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
with(intent) {
if (action == ACTION_BOND_STATE_CHANGED) {
val device = getParcelableExtra<BluetoothDevice>(EXTRA_DEVICE)
val previousBondState = getIntExtra(EXTRA_PREVIOUS_BOND_STATE, -1)
val bondState = getIntExtra(EXTRA_BOND_STATE, -1)
val bondTransition = "${previousBondState.toBondStateDescription()} to " +
bondState.toBondStateDescription()
Log.v(TAG, "${device?.address} Pair state changed | $bondTransition")
}
}
}

private fun Int.toBondStateDescription() = when(this) {
BOND_BONDED -> "Paired"
BOND_BONDING -> "Pairing"
BOND_NONE -> "Not Paired"
else -> "ERROR: $this"
}
}

private fun cleanConnection(gatt: BluetoothGatt) {
knownGatts.remove(gatt)
gatt.disconnect()
Expand Down
14 changes: 11 additions & 3 deletions packages/quick_blue/example/lib/peripheral_detail_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
),
body: Column(
children: [
const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expand All @@ -85,6 +86,12 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
QuickBlue.connect(widget.deviceId);
},
),
ElevatedButton(
child: const Text('pair'),
onPressed: () {
QuickBlue.pair(widget.deviceId);
},
),
ElevatedButton(
child: const Text('disconnect'),
onPressed: () {
Expand All @@ -93,6 +100,7 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
),
],
),
const Divider(),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expand All @@ -102,16 +110,16 @@ class _PeripheralDetailPageState extends State<PeripheralDetailPage> {
QuickBlue.discoverServices(widget.deviceId);
},
),
],
),
ElevatedButton(
ElevatedButton(
child: const Text('setNotifiable'),
onPressed: () {
QuickBlue.setNotifiable(
widget.deviceId, WOODEMI_SERV__COMMAND, WOODEMI_CHAR__COMMAND_RESPONSE,
BleInputProperty.indication);
},
),
],
),
TextField(
controller: serviceUUID,
decoration: const InputDecoration(
Expand Down
2 changes: 2 additions & 0 deletions packages/quick_blue/lib/quick_blue.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class QuickBlue {

static void connect(String deviceId) => _platform.connect(deviceId);

static void pair(String deviceId) => _platform.pair(deviceId);

static void disconnect(String deviceId) => _platform.disconnect(deviceId);

static void setConnectionHandler(OnConnectionChanged? onConnectionChanged) {
Expand Down
13 changes: 11 additions & 2 deletions packages/quick_blue/lib/src/method_channel_quick_blue.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'dart:async';
import 'dart:typed_data';

import 'dart:io';
import 'package:flutter/services.dart';
import 'package:logging/logging.dart';

Expand Down Expand Up @@ -140,4 +140,13 @@ class MethodChannelQuickBlue extends QuickBluePlatform {
}).then((_) => _log('requestMtu invokeMethod success'));
return await _mtuConfigController.stream.first;
}
}

@override
void pair(String deviceId) {
bool supportedPlatforms = Platform.isAndroid || Platform.isWindows;
if (!supportedPlatforms) throw UnimplementedError();
_method.invokeMethod('pair', {
'deviceId': deviceId,
}).then((_) => _log('pair invokeMethod success'));
}
}
5 changes: 5 additions & 0 deletions packages/quick_blue/lib/src/quick_blue_linux.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ class QuickBlueLinux extends QuickBluePlatform {
// TODO: implement requestMtu
throw UnimplementedError();
}

@override
void pair(String deviceId) {
// TODO: implement pair
}
}

extension BlueZDeviceExtension on BlueZDevice {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ abstract class QuickBluePlatform extends PlatformInterface {

void connect(String deviceId);

void pair(String deviceId);

void disconnect(String deviceId);

OnConnectionChanged? onConnectionChanged;
Expand Down
31 changes: 31 additions & 0 deletions packages/quick_blue/windows/quick_blue_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <winrt/Windows.Devices.Bluetooth.h>
#include <winrt/Windows.Devices.Bluetooth.Advertisement.h>
#include <winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h>
#include <winrt/Windows.Devices.Enumeration.h>

#include <flutter/method_channel.h>
#include <flutter/basic_message_channel.h>
Expand Down Expand Up @@ -36,6 +37,7 @@ using namespace winrt::Windows::Devices::Radios;
using namespace winrt::Windows::Devices::Bluetooth;
using namespace winrt::Windows::Devices::Bluetooth::Advertisement;
using namespace winrt::Windows::Devices::Bluetooth::GenericAttributeProfile;
using namespace winrt::Windows::Devices::Enumeration;

using flutter::EncodableValue;
using flutter::EncodableMap;
Expand Down Expand Up @@ -152,6 +154,8 @@ class QuickBluePlugin : public flutter::Plugin, public flutter::StreamHandler<En
std::map<uint64_t, std::unique_ptr<BluetoothDeviceAgent>> connectedDevices{};

winrt::fire_and_forget ConnectAsync(uint64_t bluetoothAddress);
winrt::fire_and_forget PairAsync(uint64_t bluetoothAddress, DevicePairingProtectionLevel level);
void BluetoothLEDevice_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args);
void BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args);
void CleanConnection(uint64_t bluetoothAddress);
winrt::fire_and_forget DiscoverServicesAsync(BluetoothDeviceAgent &bluetoothDeviceAgent);
Expand Down Expand Up @@ -245,6 +249,11 @@ void QuickBluePlugin::HandleMethodCall(
} else {
result->Error("IllegalState", "Bluetooth unavailable");
}
}else if (method_name.compare("pair") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
PairAsync(std::stoull(deviceId), DevicePairingProtectionLevel::Encryption);
result->Success(nullptr);
} else if (method_name.compare("connect") == 0) {
auto args = std::get<EncodableMap>(*method_call.arguments());
auto deviceId = std::get<std::string>(args[EncodableValue("deviceId")]);
Expand Down Expand Up @@ -444,6 +453,28 @@ winrt::fire_and_forget QuickBluePlugin::ConnectAsync(uint64_t bluetoothAddress)
});
}

winrt::fire_and_forget QuickBluePlugin::PairAsync(uint64_t bluetoothAddress, DevicePairingProtectionLevel level) {
BluetoothLEDevice device = co_await BluetoothLEDevice::FromBluetoothAddressAsync(bluetoothAddress);
if(device != nullptr) {
if(device.DeviceInformation().Pairing().CanPair()) {
try {
device.DeviceInformation().Pairing().Custom().PairingRequested({ this, &QuickBluePlugin::BluetoothLEDevice_PairingRequested });
auto result = co_await device.DeviceInformation().Pairing().Custom().PairAsync(DevicePairingKinds::ConfirmOnly, DevicePairingProtectionLevel::Encryption);
if(result.Status() != DevicePairingResultStatus::Paired && result.Status() != DevicePairingResultStatus::AlreadyPaired) {
OutputDebugString((L"PairAsync error: " + winrt::to_hstring((int32_t)result.Status()) + L"\n").c_str());
}
} catch(winrt::hresult_error const& ex) {
OutputDebugString((L"PairAsync " + ex.message() + L"\n").c_str());
}
}
}
co_return;
}

void QuickBluePlugin::BluetoothLEDevice_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args) {
args.Accept();
}

void QuickBluePlugin::BluetoothLEDevice_ConnectionStatusChanged(BluetoothLEDevice sender, IInspectable args) {
OutputDebugString((L"ConnectionStatusChanged " + winrt::to_hstring((int32_t)sender.ConnectionStatus()) + L"\n").c_str());
if (sender.ConnectionStatus() == BluetoothConnectionStatus::Disconnected) {
Expand Down