Skip to content

Commit 6f2d625

Browse files
Merge branch 'development' into issue#1268
2 parents 6f6c123 + 197e0ce commit 6f2d625

19 files changed

+375
-236
lines changed
-34.5 KB
Loading
-70 KB
Loading
118 KB
Loading

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");

lib/bademagic_module/models/messages.dart

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,25 @@ class Message {
77
final bool marquee;
88
final Speed speed;
99
final Mode mode;
10+
final int? animationIndex; // 👈 NEW
1011

1112
Message({
1213
required this.text,
1314
this.flash = false,
1415
this.marquee = false,
15-
this.speed = Speed.one, // Default speed
16-
this.mode = Mode.left, // Default mode
16+
this.speed = Speed.one,
17+
this.mode = Mode.left,
18+
this.animationIndex, // 👈 NEW
1719
});
1820

1921
// Convert Message object to JSON
2022
Map<String, dynamic> toJson() => {
2123
'text': text,
2224
'flash': flash,
2325
'marquee': marquee,
24-
'speed': speed.hexValue, // Use hexValue for serialization
25-
'mode': mode.hexValue, // Use hexValue for serialization
26+
'speed': speed.hexValue,
27+
'mode': mode.hexValue,
28+
if (animationIndex != null) 'animationIndex': animationIndex, // 👈 NEW
2629
};
2730

2831
// Convert JSON to Message object
@@ -52,10 +55,9 @@ class Message {
5255
text: List<String>.from(textList),
5356
flash: (json['flash'] as bool?) ?? false,
5457
marquee: (json['marquee'] as bool?) ?? false,
55-
speed: Speed.fromHex(
56-
json['speed'] as String), // Using helper method for safety
57-
mode: Mode.fromHex(
58-
json['mode'] as String), // Using helper method for safety
58+
speed: Speed.fromHex(json['speed'] as String),
59+
mode: Mode.fromHex(json['mode'] as String),
60+
animationIndex: json['animationIndex'] as int?, // 👈 NEW
5961
);
6062
}
6163
}

lib/bademagic_module/models/mode.dart

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,27 @@ enum Mode {
44
up('0x02'),
55
down('0x03'),
66
fixed('0x04'),
7-
snowflake('0x05'),
8-
picture('0x06'),
9-
animation('0x07'),
7+
animation('0x05'),
8+
snowflake('0x06'),
9+
picture('0x07'),
1010
laser('0x08');
1111

1212
final String hexValue;
1313
const Mode(this.hexValue);
1414

15-
//method to get the integer value of the mode
1615
static int getIntValue(Mode mode) {
17-
String hexValue = mode.hexValue.substring(3, 4);
18-
int intValue = int.parse(hexValue, radix: 10);
19-
return intValue;
16+
return int.parse(mode.hexValue.substring(2), radix: 16);
2017
}
2118

22-
// Helper method to safely parse hex value
2319
static Mode fromHex(String hexValue) {
2420
return Mode.values.firstWhere(
2521
(mode) => mode.hexValue == hexValue,
26-
orElse: () => Mode.left, // Default to Mode.left if no match
22+
orElse: () => Mode.left,
2723
);
2824
}
25+
26+
static Mode fromInt(int value) {
27+
final hex = value.toRadixString(16).padLeft(2, '0');
28+
return fromHex('0x$hex');
29+
}
2930
}

lib/badge_animation/ani_animation.dart

Lines changed: 13 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,61 +4,26 @@ class AniAnimation extends BadgeAnimation {
44
@override
55
void processAnimation(int badgeHeight, int badgeWidth, int animationIndex,
66
List<List<bool>> processGrid, List<List<bool>> canvas) {
7-
int newWidth = processGrid[0].length;
8-
int newHeight = processGrid.length;
9-
int verticalOffset = (badgeHeight - newHeight) ~/ 2;
10-
int displayWidth = newWidth > badgeWidth ? badgeWidth : newWidth;
11-
int horizontalOffset = (badgeWidth - displayWidth) ~/ 2;
12-
var totalAnimationLength = badgeWidth;
13-
int frame = animationIndex % totalAnimationLength;
14-
var firstHalf = frame < badgeWidth ~/ 2;
15-
var secondHalf = frame >= badgeWidth ~/ 2;
16-
7+
int newGridHeight = processGrid.length;
8+
int newGridWidth = processGrid[0].length;
179
for (int i = 0; i < badgeHeight; i++) {
1810
for (int j = 0; j < badgeWidth; j++) {
19-
bool lineShow = false;
20-
bool bitmapShowcenter = false;
21-
bool bitmapShowOut = false;
22-
23-
int sourceRow = i - verticalOffset;
24-
int sourceCol = j - horizontalOffset;
25-
26-
bool isWithinNewGrid = sourceRow >= 0 &&
27-
sourceRow < newHeight &&
28-
sourceCol >= 0 &&
29-
sourceCol < displayWidth;
30-
31-
int leftCenterCol = badgeWidth ~/ 2 - 1;
32-
int rightCenterCol = badgeWidth ~/ 2;
33-
34-
int maxDistance = leftCenterCol;
35-
36-
int currentAnimationIndex = animationIndex % (maxDistance + 1);
11+
// Calculate the total number of frames that fit the badge width
12+
int framesCount = (newGridWidth / badgeWidth).ceil();
3713

38-
int leftColPos = leftCenterCol - currentAnimationIndex;
39-
int rightColPos = rightCenterCol + currentAnimationIndex;
14+
// Determine the current frame based on the animation value
15+
int currentcountFrame = animationIndex ~/ badgeWidth % framesCount;
4016

41-
if (leftColPos < 0) leftColPos += badgeWidth;
42-
if (rightColPos >= badgeWidth) rightColPos -= badgeWidth;
17+
// Calculate the starting column for the current frame in newGrid
18+
int startCol = currentcountFrame * badgeWidth;
4319

44-
if (j == leftColPos || j == rightColPos) {
45-
lineShow = true;
46-
} else {
47-
lineShow = false;
48-
}
20+
bool isNewGridCell = i < newGridHeight && (startCol + j) < newGridWidth;
4921

50-
if (firstHalf) {
51-
if (isWithinNewGrid && j > leftColPos && j < rightColPos) {
52-
bitmapShowcenter = processGrid[sourceRow][sourceCol];
53-
}
54-
}
55-
if (secondHalf) {
56-
if (isWithinNewGrid && (j < leftColPos || j > rightColPos)) {
57-
bitmapShowOut = processGrid[sourceRow][sourceCol];
58-
}
59-
}
22+
// Update the grid based on the current frame's data
23+
bool animationCondition =
24+
(isNewGridCell && processGrid[i][startCol + j]);
6025

61-
canvas[i][j] = (lineShow || bitmapShowOut || bitmapShowcenter);
26+
canvas[i][j] = animationCondition;
6227
}
6328
}
6429
}

0 commit comments

Comments
 (0)