Skip to content

Commit 197e0ce

Browse files
fix: BLE connection only allowed one update without app restart (#1334)
1 parent 5d5f41a commit 197e0ce

File tree

4 files changed

+71
-63
lines changed

4 files changed

+71
-63
lines changed

lib/bademagic_module/bluetooth/connect_state.dart

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,48 +11,37 @@ class ConnectState extends RetryBleState {
1111

1212
@override
1313
Future<BleState?> processState() async {
14-
bool connected = false;
15-
1614
try {
15+
try {
16+
await scanResult.device.disconnect();
17+
logger.d("Pre-emptive disconnect for clean state");
18+
await Future.delayed(const Duration(seconds: 1));
19+
} catch (_) {
20+
logger.d("No existing connection to disconnect");
21+
}
22+
1723
await scanResult.device.connect(autoConnect: false);
1824
BluetoothConnectionState connectionState =
1925
await scanResult.device.connectionState.first;
2026

2127
if (connectionState == BluetoothConnectionState.connected) {
22-
connected = true;
23-
24-
logger.d("Device connected");
28+
logger.d("Device connected successfully");
2529
toast.showToast('Device connected successfully.');
2630

27-
final writeState =
28-
WriteState(device: scanResult.device, manager: manager);
29-
final result = await writeState.process();
30-
try {
31-
await scanResult.device.disconnect();
32-
logger.d("Device disconnected after transfer");
33-
await Future.delayed(const Duration(seconds: 1));
34-
logger.d("Waited 1s after disconnect");
35-
} catch (e) {
36-
logger.e("Error during disconnect after transfer: $e");
37-
}
38-
return result;
31+
manager.connectedDevice = scanResult.device;
32+
33+
final writeState = WriteState(
34+
device: scanResult.device,
35+
manager: manager,
36+
);
37+
38+
return await writeState.process();
3939
} else {
4040
throw Exception("Failed to connect to the device");
4141
}
4242
} catch (e) {
43-
toast.showErrorToast('Failed to connect retrying...');
43+
toast.showErrorToast('Failed to connect. Retrying...');
4444
rethrow;
45-
} finally {
46-
if (!connected) {
47-
try {
48-
await scanResult.device.disconnect();
49-
logger.d("Device disconnected in finally block");
50-
await Future.delayed(const Duration(seconds: 1));
51-
logger.d("Waited 1s after disconnect (finally)");
52-
} catch (e) {
53-
logger.e("Error during disconnect in finally: $e");
54-
}
55-
}
5645
}
5746
}
5847
}

lib/bademagic_module/bluetooth/datagenerator.dart

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@ import 'package:badgemagic/bademagic_module/utils/data_to_bytearray_converter.da
33
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
44
import 'package:badgemagic/providers/badge_message_provider.dart';
55
import 'package:badgemagic/providers/imageprovider.dart';
6+
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
67
import 'package:get_it/get_it.dart';
78

89
class DataTransferManager {
910
final Data data;
10-
//make it singleton
1111

12-
DataTransferManager(this.data);
12+
BluetoothDevice? connectedDevice;
1313

1414
final BadgeMessageProvider badgeData = BadgeMessageProvider();
15-
DataToByteArrayConverter converter = DataToByteArrayConverter();
16-
FileHelper fileHelper = FileHelper();
17-
InlineImageProvider controllerData = GetIt.instance<InlineImageProvider>();
15+
final DataToByteArrayConverter converter = DataToByteArrayConverter();
16+
final FileHelper fileHelper = FileHelper();
17+
final InlineImageProvider controllerData =
18+
GetIt.instance<InlineImageProvider>();
19+
20+
DataTransferManager(this.data);
1821

1922
Future<List<List<int>>> generateDataChunk() async {
2023
return converter.convert(data);
2124
}
25+
26+
/// Helper to clear the currently connected device.
27+
void clearConnectedDevice() {
28+
connectedDevice = null;
29+
}
2230
}

lib/bademagic_module/bluetooth/scan_state.dart

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,74 +2,82 @@ import 'dart:async';
22
import 'package:badgemagic/bademagic_module/bluetooth/connect_state.dart';
33
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
44
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
5-
65
import 'base_ble_state.dart';
76

87
class ScanState extends NormalBleState {
98
final DataTransferManager manager;
9+
1010
ScanState({required this.manager});
1111

1212
@override
1313
Future<BleState?> processState() async {
14-
StreamSubscription<List<ScanResult>>? subscription;
15-
toast.showToast("Searching for device...");
14+
manager.clearConnectedDevice();
15+
await FlutterBluePlus.stopScan();
1616

17+
toast.showToast("Searching for device...");
1718
Completer<BleState?> nextStateCompleter = Completer();
18-
bool isCompleted = false;
19-
20-
ScanResult? foundDevice;
19+
StreamSubscription<List<ScanResult>>? subscription;
2120

21+
bool isCompleted = false;
2222
try {
2323
subscription = FlutterBluePlus.scanResults.listen(
2424
(results) async {
25-
if (!isCompleted) {
26-
if (results.isNotEmpty) {
27-
foundDevice = results.firstWhere(
28-
(result) => result.advertisementData.serviceUuids
29-
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb")),
30-
);
31-
if (foundDevice != null) {
32-
toast.showToast('Device found. Connecting...');
33-
isCompleted = true;
34-
nextStateCompleter.complete(ConnectState(
35-
scanResult: foundDevice!,
36-
manager: manager,
37-
));
38-
}
39-
}
25+
if (isCompleted || results.isEmpty) return;
26+
27+
try {
28+
final foundDevice = results.firstWhere(
29+
(r) => r.advertisementData.serviceUuids.contains(
30+
Guid("0000fee0-0000-1000-8000-00805f9b34fb"),
31+
),
32+
orElse: () => throw Exception("Matching device not found."),
33+
);
34+
35+
isCompleted = true;
36+
await FlutterBluePlus.stopScan();
37+
toast.showToast('Device found. Connecting...');
38+
nextStateCompleter.complete(
39+
ConnectState(scanResult: foundDevice, manager: manager),
40+
);
41+
} catch (_) {
42+
// Ignore and keep scanning
4043
}
4144
},
42-
onError: (e) async {
45+
onError: (e) {
4346
if (!isCompleted) {
4447
isCompleted = true;
48+
FlutterBluePlus.stopScan();
4549
logger.e("Scan error: $e");
4650
toast.showErrorToast('Scan error occurred.');
47-
nextStateCompleter.completeError(e);
51+
nextStateCompleter.completeError(
52+
Exception("Error during scanning: $e"),
53+
);
4854
}
4955
},
5056
);
5157

5258
await FlutterBluePlus.startScan(
5359
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
54-
removeIfGone: Duration(seconds: 5),
60+
removeIfGone: const Duration(seconds: 5),
5561
continuousUpdates: true,
56-
timeout: const Duration(seconds: 15), // Reduced scan timeout
62+
timeout: const Duration(seconds: 15),
5763
);
5864

5965
await Future.delayed(const Duration(seconds: 1));
6066

61-
// If no device is found after the scan timeout, complete with an error.
6267
if (!isCompleted) {
63-
toast.showToast('Device not found.');
68+
isCompleted = true;
69+
FlutterBluePlus.stopScan();
70+
toast.showErrorToast('Device not found.');
6471
nextStateCompleter.completeError(Exception('Device not found.'));
6572
}
6673

6774
return await nextStateCompleter.future;
6875
} catch (e) {
6976
logger.e("Exception during scanning: $e");
70-
throw Exception("please check the device is turned on and retry.");
77+
throw Exception("Please check if the device is turned on and retry.");
7178
} finally {
7279
await subscription?.cancel();
80+
await FlutterBluePlus.stopScan();
7381
}
7482
}
7583
}

lib/bademagic_module/bluetooth/write_state.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,19 @@ class WriteState extends NormalBleState {
2727
for (int attempt = 1; attempt <= 3; attempt++) {
2828
try {
2929
await characteristic.write(chunk, withoutResponse: false);
30+
logger.d("Chunk written successfully: $chunk");
3031
success = true;
3132
break;
3233
} catch (e) {
33-
logger.e("Write failed, retrying ($attempt/3): $e");
34+
logger.e("Write failed (attempt $attempt/3): $e");
3435
}
3536
}
3637
if (!success) {
3738
throw Exception("Failed to transfer data. Please try again.");
3839
}
40+
await Future.delayed(Duration(milliseconds: 50));
3941
}
42+
4043
logger.d("Characteristic written successfully");
4144
return CompletedState(
4245
isSuccess: true, message: "Data transferred successfully");

0 commit comments

Comments
 (0)