Skip to content

Commit 5dbdbc0

Browse files
feat: Add Support for Renaming (Alias) BLE Devices in App
fix: fixed the issues according to review fix: format code to match Dart standards fix: added check for duplicate checking fix: show succesfully connected as alias name
1 parent f35b827 commit 5dbdbc0

File tree

8 files changed

+263
-71
lines changed

8 files changed

+263
-71
lines changed

lib/bademagic_module/bluetooth/connect_state.dart

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import 'package:badgemagic/bademagic_module/bluetooth/datagenerator.dart';
22
import 'package:badgemagic/bademagic_module/bluetooth/write_state.dart';
33
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
4+
import 'package:badgemagic/providers/BadgeAliasProvider.dart';
5+
import 'package:get_it/get_it.dart';
46
import 'base_ble_state.dart';
57

68
class ConnectState extends RetryBleState {
79
final ScanResult scanResult;
810
final DataTransferManager manager;
11+
final String displayName;
912

10-
ConnectState({required this.manager, required this.scanResult});
13+
ConnectState({
14+
required this.manager,
15+
required this.scanResult,
16+
required this.displayName,
17+
});
1118

1219
@override
1320
Future<BleState?> processState() async {
@@ -21,15 +28,35 @@ class ConnectState extends RetryBleState {
2128
if (connectionState == BluetoothConnectionState.connected) {
2229
connected = true;
2330

24-
logger.d("Device connected");
25-
toast.showToast('Device connected successfully.');
31+
String alias = displayName;
32+
final aliasProvider = GetIt.I<BadgeAliasProvider>();
33+
final maybeAlias = aliasProvider.getAlias(displayName);
34+
if (maybeAlias != null && maybeAlias.trim().isNotEmpty) {
35+
alias = maybeAlias;
36+
}
2637

27-
return WriteState(device: scanResult.device, manager: manager);
38+
logger.d("Device '$displayName' connected");
39+
toast.showToast('Connected successfully to "$alias".');
40+
41+
final writeState =
42+
WriteState(device: scanResult.device, manager: manager);
43+
final result = await writeState.process();
44+
45+
try {
46+
await scanResult.device.disconnect();
47+
logger.d("Device disconnected after transfer");
48+
await Future.delayed(const Duration(seconds: 1));
49+
logger.d("Waited 1s after disconnect");
50+
} catch (e) {
51+
logger.e("Error during disconnect after transfer: $e");
52+
}
53+
54+
return result;
2855
} else {
2956
throw Exception("Failed to connect to the device");
3057
}
3158
} catch (e) {
32-
toast.showErrorToast('Failed to connect retrying...');
59+
toast.showErrorToast('Failed to connect to "$displayName", retrying...');
3360
rethrow;
3461
} finally {
3562
if (!connected) {

lib/bademagic_module/bluetooth/scan_state.dart

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,21 @@ 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:badgemagic/providers/BadgeScanProvider.dart';
5+
import 'package:badgemagic/providers/BadgeAliasProvider.dart';
56
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
6-
77
import 'base_ble_state.dart';
88

99
class ScanState extends NormalBleState {
1010
final DataTransferManager manager;
1111
final BadgeScanMode mode;
1212
final List<String> allowedNames;
13+
final BadgeAliasProvider aliasProvider;
1314

1415
ScanState({
1516
required this.manager,
1617
required this.mode,
1718
required this.allowedNames,
19+
required this.aliasProvider,
1820
});
1921

2022
@override
@@ -24,6 +26,14 @@ class ScanState extends NormalBleState {
2426

2527
final Completer<BleState?> nextStateCompleter = Completer();
2628
bool isCompleted = false;
29+
bool stopScanCalled = false;
30+
31+
void stopScanSafely() {
32+
if (!stopScanCalled) {
33+
stopScanCalled = true;
34+
FlutterBluePlus.stopScan();
35+
}
36+
}
2737

2838
try {
2939
subscription = FlutterBluePlus.scanResults.listen(
@@ -42,21 +52,44 @@ class ScanState extends NormalBleState {
4252
.contains(Guid("0000fee0-0000-1000-8000-00805f9b34fb"));
4353

4454
final deviceName = result.device.name.trim().toLowerCase();
45-
final matchesName = mode == BadgeScanMode.any ||
55+
56+
final matchesDirectName = mode == BadgeScanMode.any ||
4657
normalizedAllowedNames.contains(deviceName);
4758

48-
return matchesUuid && matchesName;
59+
final aliasMatch = normalizedAllowedNames.any((realName) {
60+
final alias =
61+
aliasProvider.getAlias(realName)?.trim().toLowerCase();
62+
return alias == deviceName;
63+
});
64+
65+
return matchesUuid && (matchesDirectName || aliasMatch);
4966
},
5067
orElse: () => throw Exception("Matching device not found."),
5168
);
5269

5370
isCompleted = true;
54-
FlutterBluePlus.stopScan();
71+
stopScanSafely();
72+
5573
toast.showToast('Device found. Connecting...');
5674

75+
final foundName = foundDevice.device.name.trim();
76+
String? resolvedAlias;
77+
78+
for (final real in allowedNames) {
79+
final alias = aliasProvider.getAlias(real)?.trim();
80+
if (alias != null &&
81+
alias.toLowerCase() == foundName.toLowerCase()) {
82+
resolvedAlias = alias;
83+
break;
84+
}
85+
}
86+
87+
final displayName = resolvedAlias ?? foundName;
88+
5789
nextStateCompleter.complete(ConnectState(
5890
scanResult: foundDevice,
5991
manager: manager,
92+
displayName: displayName,
6093
));
6194
} catch (e) {
6295
logger.w("No matching device found in this batch: $e");
@@ -65,6 +98,7 @@ class ScanState extends NormalBleState {
6598
onError: (e) async {
6699
if (!isCompleted) {
67100
isCompleted = true;
101+
stopScanSafely(); // ✅ Guarded again
68102
logger.e("Scan error: $e");
69103
toast.showErrorToast('Scan error occurred.');
70104
nextStateCompleter.completeError(Exception("Scan error: $e"));
@@ -76,23 +110,25 @@ class ScanState extends NormalBleState {
76110
withServices: [Guid("0000fee0-0000-1000-8000-00805f9b34fb")],
77111
removeIfGone: Duration(seconds: 5),
78112
continuousUpdates: true,
79-
timeout: const Duration(seconds: 15), // Reduced scan timeout
113+
timeout: const Duration(seconds: 15),
80114
);
81115

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

84-
// If no device is found after the scan timeout, complete with an error.
85118
if (!isCompleted) {
119+
isCompleted = true;
120+
stopScanSafely();
86121
toast.showToast('Device not found.');
87122
nextStateCompleter.completeError(Exception('Device not found.'));
88123
}
89124

90125
return await nextStateCompleter.future;
91126
} catch (e) {
92127
logger.e("Exception during scanning: $e");
93-
throw Exception("please check the device is turned on and retry.");
128+
throw Exception("Please check if the device is turned on and retry.");
94129
} finally {
95130
await subscription?.cancel();
131+
stopScanSafely();
96132
}
97133
}
98134
}

lib/main.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:badgemagic/providers/BadgeAliasProvider.dart';
12
import 'package:badgemagic/providers/BadgeScanProvider.dart';
23
import 'package:badgemagic/providers/getitlocator.dart';
34
import 'package:badgemagic/providers/imageprovider.dart';
@@ -25,6 +26,7 @@ void main() {
2526
ChangeNotifierProvider<InlineImageProvider>(
2627
create: (context) => getIt<InlineImageProvider>()),
2728
ChangeNotifierProvider(create: (_) => BadgeScanProvider()),
29+
ChangeNotifierProvider(create: (_) => BadgeAliasProvider()),
2830
],
2931
child: const MyApp(),
3032
));

lib/providers/BadgeAliasProvider.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import 'package:flutter/material.dart';
2+
3+
class BadgeAliasProvider with ChangeNotifier {
4+
final Map<String, String> _aliases = {};
5+
6+
String? getAlias(String deviceId) => _aliases[deviceId];
7+
8+
void setAlias(String deviceId, String alias) {
9+
_aliases[deviceId] = alias;
10+
notifyListeners();
11+
}
12+
13+
void removeAlias(String deviceId) {
14+
_aliases.remove(deviceId);
15+
notifyListeners();
16+
}
17+
18+
void clearAll() {
19+
_aliases.clear();
20+
notifyListeners();
21+
}
22+
23+
Map<String, String> get allAliases => Map.unmodifiable(_aliases);
24+
}

lib/providers/badge_message_provider.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:badgemagic/bademagic_module/models/speed.dart';
99
import 'package:badgemagic/bademagic_module/utils/converters.dart';
1010
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
1111
import 'package:badgemagic/bademagic_module/utils/toast_utils.dart';
12+
import 'package:badgemagic/providers/BadgeAliasProvider.dart';
1213
import 'package:badgemagic/providers/BadgeScanProvider.dart';
1314
import 'package:badgemagic/providers/imageprovider.dart';
1415
import 'package:flutter/widgets.dart';
@@ -100,10 +101,11 @@ class BadgeMessageProvider {
100101
) async {
101102
final scanProvider = Provider.of<BadgeScanProvider>(context, listen: false);
102103

103-
final BleState? initialState = ScanState(
104+
final BleState initialState = ScanState(
104105
manager: manager,
105106
mode: scanProvider.mode,
106107
allowedNames: scanProvider.badgeNames,
108+
aliasProvider: context.read<BadgeAliasProvider>(),
107109
);
108110

109111
BleState? state = initialState;

0 commit comments

Comments
 (0)