Skip to content
Merged
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
47 changes: 18 additions & 29 deletions lib/bademagic_module/bluetooth/connect_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,48 +11,37 @@ class ConnectState extends RetryBleState {

@override
Future<BleState?> processState() async {
bool connected = false;

try {
try {
await scanResult.device.disconnect();
logger.d("Pre-emptive disconnect for clean state");
await Future.delayed(const Duration(seconds: 1));
} catch (_) {
logger.d("No existing connection to disconnect");
}

await scanResult.device.connect(autoConnect: false);
BluetoothConnectionState connectionState =
await scanResult.device.connectionState.first;

if (connectionState == BluetoothConnectionState.connected) {
connected = true;

logger.d("Device connected");
logger.d("Device connected successfully");
toast.showToast('Device connected successfully.');

final writeState =
WriteState(device: scanResult.device, manager: manager);
final result = await writeState.process();
try {
await scanResult.device.disconnect();
logger.d("Device disconnected after transfer");
await Future.delayed(const Duration(seconds: 1));
logger.d("Waited 1s after disconnect");
} catch (e) {
logger.e("Error during disconnect after transfer: $e");
}
return result;
manager.connectedDevice = scanResult.device;

final writeState = WriteState(
device: scanResult.device,
manager: manager,
);

return await writeState.process();
} else {
throw Exception("Failed to connect to the device");
}
} catch (e) {
toast.showErrorToast('Failed to connect retrying...');
toast.showErrorToast('Failed to connect. Retrying...');
rethrow;
} finally {
if (!connected) {
try {
await scanResult.device.disconnect();
logger.d("Device disconnected in finally block");
await Future.delayed(const Duration(seconds: 1));
logger.d("Waited 1s after disconnect (finally)");
} catch (e) {
logger.e("Error during disconnect in finally: $e");
}
}
}
}
}
18 changes: 13 additions & 5 deletions lib/bademagic_module/bluetooth/datagenerator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,28 @@ import 'package:badgemagic/bademagic_module/utils/data_to_bytearray_converter.da
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
import 'package:badgemagic/providers/badge_message_provider.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get_it/get_it.dart';

class DataTransferManager {
final Data data;
//make it singleton

DataTransferManager(this.data);
BluetoothDevice? connectedDevice;

final BadgeMessageProvider badgeData = BadgeMessageProvider();
DataToByteArrayConverter converter = DataToByteArrayConverter();
FileHelper fileHelper = FileHelper();
InlineImageProvider controllerData = GetIt.instance<InlineImageProvider>();
final DataToByteArrayConverter converter = DataToByteArrayConverter();
final FileHelper fileHelper = FileHelper();
final InlineImageProvider controllerData =
GetIt.instance<InlineImageProvider>();

DataTransferManager(this.data);

Future<List<List<int>>> generateDataChunk() async {
return converter.convert(data);
}

/// Helper to clear the currently connected device.
void clearConnectedDevice() {
connectedDevice = null;
}
}
64 changes: 36 additions & 28 deletions lib/bademagic_module/bluetooth/scan_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,74 +2,82 @@ import 'dart:async';
import 'package:badgemagic/bademagic_module/bluetooth/connect_state.dart';
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';

import 'base_ble_state.dart';

class ScanState extends NormalBleState {
final DataTransferManager manager;

ScanState({required this.manager});

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

toast.showToast("Searching for device...");
Completer<BleState?> nextStateCompleter = Completer();
bool isCompleted = false;

ScanResult? foundDevice;
StreamSubscription<List<ScanResult>>? subscription;

bool isCompleted = false;
try {
subscription = FlutterBluePlus.scanResults.listen(
(results) async {
if (!isCompleted) {
if (results.isNotEmpty) {
foundDevice = results.firstWhere(
(result) => result.advertisementData.serviceUuids
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb")),
);
if (foundDevice != null) {
toast.showToast('Device found. Connecting...');
isCompleted = true;
nextStateCompleter.complete(ConnectState(
scanResult: foundDevice!,
manager: manager,
));
}
}
if (isCompleted || results.isEmpty) return;

try {
final foundDevice = results.firstWhere(
(r) => r.advertisementData.serviceUuids.contains(
Guid("0000fee0-0000-1000-8000-00805f9b34fb"),
),
orElse: () => throw Exception("Matching device not found."),
);

isCompleted = true;
await FlutterBluePlus.stopScan();
toast.showToast('Device found. Connecting...');
nextStateCompleter.complete(
ConnectState(scanResult: foundDevice, manager: manager),
);
} catch (_) {
// Ignore and keep scanning
}
},
onError: (e) async {
onError: (e) {
if (!isCompleted) {
isCompleted = true;
FlutterBluePlus.stopScan();
logger.e("Scan error: $e");
toast.showErrorToast('Scan error occurred.');
nextStateCompleter.completeError(e);
nextStateCompleter.completeError(
Exception("Error during scanning: $e"),
);
}
},
);

await FlutterBluePlus.startScan(
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
removeIfGone: Duration(seconds: 5),
removeIfGone: const Duration(seconds: 5),
continuousUpdates: true,
timeout: const Duration(seconds: 15), // Reduced scan timeout
timeout: const Duration(seconds: 15),
);

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

// If no device is found after the scan timeout, complete with an error.
if (!isCompleted) {
toast.showToast('Device not found.');
isCompleted = true;
FlutterBluePlus.stopScan();
toast.showErrorToast('Device not found.');
nextStateCompleter.completeError(Exception('Device not found.'));
}

return await nextStateCompleter.future;
} catch (e) {
logger.e("Exception during scanning: $e");
throw Exception("please check the device is turned on and retry.");
throw Exception("Please check if the device is turned on and retry.");
} finally {
await subscription?.cancel();
await FlutterBluePlus.stopScan();
}
}
}
5 changes: 4 additions & 1 deletion lib/bademagic_module/bluetooth/write_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,19 @@ class WriteState extends NormalBleState {
for (int attempt = 1; attempt <= 3; attempt++) {
try {
await characteristic.write(chunk, withoutResponse: false);
logger.d("Chunk written successfully: $chunk");
success = true;
break;
} catch (e) {
logger.e("Write failed, retrying ($attempt/3): $e");
logger.e("Write failed (attempt $attempt/3): $e");
}
}
if (!success) {
throw Exception("Failed to transfer data. Please try again.");
}
await Future.delayed(Duration(milliseconds: 50));
}

logger.d("Characteristic written successfully");
return CompletedState(
isSuccess: true, message: "Data transferred successfully");
Expand Down
Loading