Skip to content

Commit d4ebc1e

Browse files
committed
add: write, read, status functions, interface and example
1 parent e8e8ffd commit d4ebc1e

File tree

5 files changed

+159
-13
lines changed

5 files changed

+159
-13
lines changed

android/src/main/kotlin/com/matteogassend/bluetooth_classic/BluetoothClassicPlugin.kt

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ class BluetoothClassicPlugin: FlutterPlugin, MethodCallHandler, PluginRegistry.R
4242
private lateinit var bluetoothDeviceChannel: EventChannel
4343
private var bluetoothDeviceChannelSink: EventSink ?= null
4444
private lateinit var bluetoothReadChannel: EventChannel
45-
private var bluetoothReadChannelSink : EventChannel.EventSink ?= null
45+
private var bluetoothReadChannelSink : EventSink ?= null
4646
private lateinit var bluetoothStatusChannel: EventChannel
47-
private var bluetoothStatusChannelSink: EventChannel.EventSink ?= null
47+
private var bluetoothStatusChannelSink: EventSink ?= null
4848
private lateinit var ba: BluetoothAdapter
4949
private lateinit var pluginActivity: Activity
5050
private lateinit var application: Context
@@ -57,7 +57,7 @@ class BluetoothClassicPlugin: FlutterPlugin, MethodCallHandler, PluginRegistry.R
5757
private var device: BluetoothDevice ?= null
5858

5959

60-
private inner class ConnectedThread(private val socket: BluetoothSocket): Thread() {
60+
private inner class ConnectedThread(socket: BluetoothSocket): Thread() {
6161

6262
private val inputStream = socket.inputStream
6363
private val outputStream = socket.outputStream
@@ -137,12 +137,9 @@ class BluetoothClassicPlugin: FlutterPlugin, MethodCallHandler, PluginRegistry.R
137137
ba = BluetoothAdapter.getDefaultAdapter()
138138
channel.setMethodCallHandler(this)
139139
looper = flutterPluginBinding.applicationContext.mainLooper
140-
141140
application = flutterPluginBinding.applicationContext
142-
143-
144141
bluetoothDeviceChannel.setStreamHandler(object: StreamHandler {
145-
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
142+
override fun onListen(arguments: Any?, events: EventSink?) {
146143
bluetoothDeviceChannelSink = events
147144
}
148145

@@ -171,24 +168,50 @@ class BluetoothClassicPlugin: FlutterPlugin, MethodCallHandler, PluginRegistry.R
171168
}
172169

173170
override fun onMethodCall(call: MethodCall, result: Result) {
174-
171+
Log.i("method_call", call.method)
175172
when(call.method) {
176-
"getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
173+
"getPlatformVersion" -> result.success("Android ${Build.VERSION.RELEASE}")
177174
"initPermissions" -> initPermissions(result)
178175
"getDevices" -> getDevices(result)
179176
"startDiscovery" -> startScan(result)
180177
"stopDiscovery" -> stopScan(result)
181178
"connect" -> connect(result, call.argument<String>("deviceId")!!,
182179
call.argument<String>("serviceUUID")!!
183180
)
181+
"disconnect" -> disconnect(result)
182+
"write" -> write(result, call.argument<String>("message")!!)
184183
else -> result.notImplemented()
185184
}
186185
}
187186

187+
private fun write(result: Result, message: String) {
188+
Log.i("write_handle", "inside write handle")
189+
if (thread != null) {
190+
thread!!.write(message.toByteArray())
191+
result.success(true)
192+
} else {
193+
result.error("write_impossible", "could not send message to unconnected device", null)
194+
}
195+
}
196+
197+
private fun disconnect(result: Result) {
198+
device = null
199+
android.util.Log.i("Bluetooth Disconnect", "device removed from memory")
200+
thread!!.interrupt()
201+
android.util.Log.i("Bluetooth Disconnect", "read thread closed")
202+
thread = null
203+
android.util.Log.i("Bluetooth Disconnect", "read thread freed")
204+
socket!!.close()
205+
android.util.Log.i("Bluetooth Disconnect", "rfcomm socket closed")
206+
publishBluetoothStatus(0)
207+
android.util.Log.i("Bluetooth Disconnect", "disconnected")
208+
result.success(true)
209+
}
210+
188211
private fun connect(result: Result, deviceId: String, serviceUuid: String) {
189212
try {
190213
publishBluetoothStatus(1)
191-
device = ba!!.getRemoteDevice(deviceId)
214+
device = ba.getRemoteDevice(deviceId)
192215
android.util.Log.i("Bluetooth Connection", "device found")
193216
assert(device != null)
194217
socket = device?.createRfcommSocketToServiceRecord(UUID.fromString(serviceUuid))

example/lib/main.dart

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,22 @@ class _MyAppState extends State<MyApp> {
2222
List<Device> _devices = [];
2323
List<Device> _discoveredDevices = [];
2424
bool _scanning = false;
25-
25+
int _deviceStatus = Device.disconnected;
26+
Uint8List _data = Uint8List(0);
2627
@override
2728
void initState() {
2829
super.initState();
2930
initPlatformState();
31+
_bluetoothClassicPlugin.onDeviceStatusChanged().listen((event) {
32+
setState(() {
33+
_deviceStatus = event;
34+
});
35+
});
36+
_bluetoothClassicPlugin.onDeviceDataReceived().listen((event) {
37+
setState(() {
38+
_data = Uint8List.fromList([..._data, ...event]);
39+
});
40+
});
3041
}
3142

3243
// Platform messages are asynchronous, so we initialize in an async method.
@@ -89,6 +100,7 @@ class _MyAppState extends State<MyApp> {
89100
body: SingleChildScrollView(
90101
child: Column(
91102
children: [
103+
Text("Device status is $_deviceStatus"),
92104
TextButton(
93105
onPressed: () async {
94106
await _bluetoothClassicPlugin.initPermissions();
@@ -99,15 +111,35 @@ class _MyAppState extends State<MyApp> {
99111
onPressed: _getDevices,
100112
child: const Text("Get Paired Devices"),
101113
),
114+
TextButton(
115+
onPressed: _deviceStatus == Device.connected
116+
? () async {
117+
await _bluetoothClassicPlugin.disconnect();
118+
}
119+
: null,
120+
child: const Text("disconnect"),
121+
),
122+
TextButton(
123+
onPressed: _deviceStatus == Device.connected
124+
? () async {
125+
await _bluetoothClassicPlugin.write("ping");
126+
}
127+
: null,
128+
child: const Text("send ping"),
129+
),
102130
Center(
103131
child: Text('Running on: $_platformVersion\n'),
104132
),
105133
...[
106134
for (var device in _devices)
107135
TextButton(
108-
onPressed: () {
109-
_bluetoothClassicPlugin.connect(device.address,
136+
onPressed: () async {
137+
await _bluetoothClassicPlugin.connect(device.address,
110138
"00001101-0000-1000-8000-00805f9b34fb");
139+
setState(() {
140+
_discoveredDevices = [];
141+
_devices = [];
142+
});
111143
},
112144
child: Text(device.name ?? device.address))
113145
],
@@ -119,6 +151,7 @@ class _MyAppState extends State<MyApp> {
119151
for (var device in _discoveredDevices)
120152
Text(device.name ?? device.address)
121153
],
154+
Text("Received data: ${String.fromCharCodes(_data)}"),
122155
],
123156
),
124157
),

lib/bluetooth_classic.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:typed_data';
2+
13
import 'bluetooth_classic_platform_interface.dart';
24
import 'models/device.dart';
35

@@ -22,11 +24,27 @@ class BluetoothClassic {
2224
return BluetoothClassicPlatform.instance.stopScan();
2325
}
2426

27+
Future<bool> disconnect() {
28+
return BluetoothClassicPlatform.instance.disconnect();
29+
}
30+
2531
Stream<Device> onDeviceDiscovered() {
2632
return BluetoothClassicPlatform.instance.onDeviceDiscovered();
2733
}
2834

35+
Stream<int> onDeviceStatusChanged() {
36+
return BluetoothClassicPlatform.instance.onDeviceStatusChanged();
37+
}
38+
39+
Stream<Uint8List> onDeviceDataReceived() {
40+
return BluetoothClassicPlatform.instance.onDeviceDataReceived();
41+
}
42+
2943
Future<bool> connect(String address, String serviceUUID) {
3044
return BluetoothClassicPlatform.instance.connect(address, serviceUUID);
3145
}
46+
47+
Future<bool> write(String message) {
48+
return BluetoothClassicPlatform.instance.write(message);
49+
}
3250
}

lib/bluetooth_classic_method_channel.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,39 @@ class MethodChannelBluetoothClassic extends BluetoothClassicPlatform {
1616
/// The event channel used to receive discovered devices events
1717
final deviceDiscoveryChannel =
1818
const EventChannel("com.matteogassend/bluetooth_classic/devices");
19+
final deviceStatusChannel =
20+
const EventChannel("com.matteogassend/bluetooth_classic/status");
21+
final deviceDataChannel =
22+
const EventChannel("com.matteogassend/bluetooth_classic/read");
1923

2024
/// stream mapped to deviceDiscoveryChannel
2125
Stream<dynamic>? _deviceDiscoveryStream;
2226

27+
/// stream mapped to deviceStatusChannel
28+
Stream<dynamic>? _deviceStatusStream;
29+
30+
/// stream mapped to deviceDataChannel
31+
Stream<dynamic>? _deviceDataReceivedStream;
32+
2333
/// user facing stream controller for device discovery
2434
final StreamController<Device> discoveryStream = StreamController();
2535

36+
final StreamController<int> statusStream = StreamController();
37+
38+
final StreamController<Uint8List> dataReceivedStream = StreamController();
39+
2640
void _onDeviceDiscovered(Device device) {
2741
discoveryStream.add(device);
2842
}
2943

44+
void _onDeviceStatus(int status) {
45+
statusStream.add(status);
46+
}
47+
48+
void _onDeviceDataReceived(Uint8List data) {
49+
dataReceivedStream.add(data);
50+
}
51+
3052
@override
3153
Future<String?> getPlatformVersion() async {
3254
final version =
@@ -79,4 +101,35 @@ class MethodChannelBluetoothClassic extends BluetoothClassicPlatform {
79101
});
80102
return res!;
81103
}
104+
105+
@override
106+
Future<bool> disconnect() async {
107+
var res = await methodChannel.invokeMethod<bool>("disconnect");
108+
return res!;
109+
}
110+
111+
@override
112+
Stream<int> onDeviceStatusChanged() {
113+
_deviceStatusStream = deviceStatusChannel.receiveBroadcastStream();
114+
_deviceStatusStream!.listen((event) {
115+
_onDeviceStatus(event);
116+
});
117+
return statusStream.stream;
118+
}
119+
120+
@override
121+
Stream<Uint8List> onDeviceDataReceived() {
122+
_deviceDataReceivedStream = deviceDataChannel.receiveBroadcastStream();
123+
_deviceDataReceivedStream!.listen((event) {
124+
_onDeviceDataReceived(event);
125+
});
126+
return dataReceivedStream.stream;
127+
}
128+
129+
@override
130+
Future<bool> write(String message) async {
131+
var res = await methodChannel
132+
.invokeMethod<bool>("write", <String, String>{"message": message});
133+
return res!;
134+
}
82135
}

lib/bluetooth_classic_platform_interface.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:typed_data';
2+
13
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
24

35
import 'bluetooth_classic_method_channel.dart';
@@ -48,7 +50,24 @@ abstract class BluetoothClassicPlatform extends PlatformInterface {
4850
throw UnimplementedError('onDeviceDiscovered() has not been implemented.');
4951
}
5052

53+
Stream<int> onDeviceStatusChanged() {
54+
throw UnimplementedError('onDeviceStatus() has not been implemented.');
55+
}
56+
57+
Stream<Uint8List> onDeviceDataReceived() {
58+
throw UnimplementedError(
59+
'onDeviceDataReceived() has not been implemented.');
60+
}
61+
5162
Future<bool> connect(String address, String serviceUUID) {
5263
throw UnimplementedError('connect() has not been implemented.');
5364
}
65+
66+
Future<bool> disconnect() {
67+
throw UnimplementedError('disconnect() has not been implemented.');
68+
}
69+
70+
Future<bool> write(String message) {
71+
throw UnimplementedError('write() has not been implemented.');
72+
}
5473
}

0 commit comments

Comments
 (0)