Skip to content

Commit b4d3250

Browse files
Merge branch 'development' into streaming_feature
2 parents ee53df3 + 9b1e098 commit b4d3250

File tree

7 files changed

+156
-4
lines changed

7 files changed

+156
-4
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import 'dart:math';
2+
import 'package:badgemagic/badge_animation/animation_abstract.dart';
3+
4+
/// An animation that simulates a graphic equalizer with bouncing vertical bars.
5+
class EqualizerAnimation extends BadgeAnimation {
6+
// --- Animation Parameters ---
7+
/// The width of each vertical bar in pixels.
8+
static const int barWidth = 4;
9+
10+
/// The width of the gap between each bar.
11+
static const int gapWidth = 1;
12+
13+
/// The probability (0.0 to 1.0) that a bar will change its height on any given frame.
14+
static const double changeChance = 0.7;
15+
16+
/// The probability that instead of smoothing, a bar completely resets to a random height.
17+
static const double resetChance = 0.15;
18+
19+
final List<int> _barHeights = [];
20+
bool _initialized = false;
21+
final Random _rng = Random();
22+
23+
void _initialize(int badgeHeight, int badgeWidth) {
24+
if (_initialized) return;
25+
_initialized = true;
26+
27+
final int numberOfBars = (badgeWidth + gapWidth) ~/ (barWidth + gapWidth);
28+
for (int i = 0; i < numberOfBars; i++) {
29+
_barHeights.add(_rng.nextInt(badgeHeight) + 1);
30+
}
31+
}
32+
33+
@override
34+
void processAnimation(
35+
int badgeHeight,
36+
int badgeWidth,
37+
int animationIndex,
38+
List<List<bool>> processGrid,
39+
List<List<bool>> canvas,
40+
) {
41+
_initialize(badgeHeight, badgeWidth);
42+
43+
for (int y = 0; y < badgeHeight; y++) {
44+
for (int x = 0; x < badgeWidth; x++) {
45+
canvas[y][x] = false;
46+
}
47+
}
48+
49+
final int numberOfBars = (badgeWidth + gapWidth) ~/ (barWidth + gapWidth);
50+
51+
for (int i = 0; i < numberOfBars; i++) {
52+
if (_rng.nextDouble() < changeChance) {
53+
if (_rng.nextDouble() < resetChance) {
54+
// Hard reset → instant jump
55+
_barHeights[i] = _rng.nextInt(badgeHeight) + 1;
56+
} else {
57+
// This will do Smooth transition toward a random target
58+
double target = _rng.nextInt(badgeHeight).toDouble();
59+
_barHeights[i] = (_barHeights[i] * 0.7 + target * 0.3)
60+
.round()
61+
.clamp(1, badgeHeight);
62+
}
63+
}
64+
}
65+
66+
// this draws the bars
67+
for (int i = 0; i < numberOfBars; i++) {
68+
int barHeight = _barHeights[i];
69+
int startX = i * (barWidth + gapWidth);
70+
71+
for (int y = badgeHeight - 1; y >= badgeHeight - barHeight; y--) {
72+
for (int x = startX; x < startX + barWidth; x++) {
73+
if (y >= 0 && y < badgeHeight && x >= 0 && x < badgeWidth) {
74+
canvas[y][x] = true;
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}

lib/providers/animation_badge_provider.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import 'package:badgemagic/badge_animation/ani_diagonal.dart';
3434
import 'package:badgemagic/badge_animation/ani_emergency.dart';
3535
import 'package:badgemagic/badge_animation/ani_beating_hearts.dart';
3636
import 'package:badgemagic/badge_animation/ani_fireworks.dart';
37+
import 'package:badgemagic/badge_animation/ani_equalizer.dart'; // new import of EqualizerAnimation
3738

3839
Map<int, BadgeAnimation?> animationMap = {
3940
0: LeftAnimation(),
@@ -56,6 +57,7 @@ Map<int, BadgeAnimation?> animationMap = {
5657
17: EmergencyAnimation(), // Emergency
5758
18: BeatingHeartsAnimation(), // Beating Hearts
5859
19: FireworksAnimation(), // Fireworks
60+
20: EqualizerAnimation(), // Digital Rain
5961
};
6062

6163
Map<int, BadgeEffect> effectMap = {
@@ -85,8 +87,8 @@ class AnimationBadgeProvider extends ChangeNotifier {
8587
// Helper: returns true if a special animation (custom) is selected
8688
bool isSpecialAnimationSelected() {
8789
int idx = getAnimationIndex() ?? 0;
88-
// Add all special animation indices here (including Fireworks at 19):
89-
return [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19].contains(idx);
90+
// Add all special animation indices here (including Equalizer at 20):
91+
return [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].contains(idx);
9092
}
9193

9294
// Call this to reset to text animation (LeftAnimation)
@@ -98,7 +100,8 @@ class AnimationBadgeProvider extends ChangeNotifier {
98100
void calculateDuration(int speed) {
99101
int idx = getAnimationIndex() ?? 0;
100102
int newSpeed;
101-
if (idx == 9 || idx == 10 || idx == 11 || idx == 12) {
103+
if (idx == 9 || idx == 10 || idx == 11 || idx == 12 || idx == 20) {
104+
//added EqualizerAnimation
102105
// Use slower mapping for custom animations
103106
// (aniSpeedStrategy already uses the slower mapping if you want, or you can hardcode)
104107
newSpeed = aniSpeedStrategy(speed - 1); // keep as is, or adjust if needed
@@ -292,6 +295,8 @@ class AnimationBadgeProvider extends ChangeNotifier {
292295
await transferBeatingHeartsAnimation(badgeData, selectedSpeed);
293296
} else if (aniIndex == 19) {
294297
await transferFireworksAnimation(badgeData, selectedSpeed);
298+
} else if (aniIndex == 20) {
299+
await transferEqualizerAnimation(badgeData, selectedSpeed);
295300
} else {
296301
await badgeData.checkAndTransfer(
297302
inlineImageProvider.getController().text,

lib/providers/badge_message_provider.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import 'package:badgemagic/badge_animation/ani_emergency.dart';
2424
import 'package:badgemagic/badge_animation/ani_beating_hearts.dart';
2525
import 'package:badgemagic/badge_animation/ani_fireworks.dart';
2626
import 'package:flutter/foundation.dart';
27+
import 'package:badgemagic/badge_animation/ani_equalizer.dart'; // Import the new EqualizerAnimation
2728

2829
Map<int, Mode> modeValueMap = {
2930
0: Mode.left,
@@ -1092,3 +1093,56 @@ void _drawDestroyEffect(
10921093
}
10931094
}
10941095
}
1096+
1097+
/// Transfers the Equalizer animation to the badge hardware.
1098+
Future<void> transferEqualizerAnimation(
1099+
BadgeMessageProvider badgeDataProvider, int speedLevel) async {
1100+
final adapterState = await FlutterBluePlus.adapterState.first;
1101+
if (adapterState != BluetoothAdapterState.on) {
1102+
ToastUtils().showErrorToast('Please turn on Bluetooth');
1103+
return;
1104+
}
1105+
1106+
const int badgeHeight = 11;
1107+
const int badgeWidth = 44;
1108+
const int hardwareFrameCount = 8; // The badge can store up to 8 frames
1109+
final Speed selectedSpeed = Speed.eight;
1110+
final logger = Logger();
1111+
1112+
logger.i('Starting Equalizer animation transfer...');
1113+
1114+
List<Message> equalizerFrames = [];
1115+
1116+
// Create the animation object *before* the loop because it's stateful.
1117+
final equalizerAnimation = EqualizerAnimation();
1118+
1119+
for (int i = 0; i < hardwareFrameCount; i++) {
1120+
List<List<bool>> frameBitmap = List.generate(
1121+
badgeHeight, (_) => List.generate(badgeWidth, (_) => false));
1122+
1123+
List<List<bool>> processGrid = List.generate(
1124+
badgeHeight, (_) => List.generate(badgeWidth, (_) => false));
1125+
1126+
equalizerAnimation.processAnimation(
1127+
badgeHeight, badgeWidth, i, processGrid, frameBitmap);
1128+
1129+
// Convert the boolean bitmap to a hex string
1130+
List<List<int>> intBitmap = boolToIntBitmap(frameBitmap);
1131+
List<String> hexList = Converters.convertBitmapToLEDHex(intBitmap, false);
1132+
1133+
logger.i('📊 Equalizer Frame $i hex: ${hexList.join(",")}');
1134+
1135+
equalizerFrames.add(Message(
1136+
text: hexList,
1137+
mode: Mode.fixed, // Each frame is sent as a fixed image
1138+
speed: selectedSpeed,
1139+
flash: false,
1140+
marquee: false,
1141+
));
1142+
}
1143+
1144+
Data data = Data(messages: equalizerFrames);
1145+
DataTransferManager manager = DataTransferManager(data);
1146+
await badgeDataProvider.transferData(manager);
1147+
logger.i('💡 Equalizer animation transfer completed successfully!');
1148+
}

lib/view/widgets/transitiontab.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ class _TransitionTabState extends State<TransitionTab> {
106106
animationName: 'Fireworks',
107107
index: 19,
108108
),
109+
AniContainer(
110+
animationName: 'Equalizer',
111+
index: 20, // This MUST match the index in your animationMap
112+
icon: Icons.equalizer,
113+
)
109114
],
110115
),
111116
),

macos/Runner/DebugProfile.entitlements

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<true/>
77
<key>com.apple.security.cs.allow-jit</key>
88
<true/>
9+
<key>com.apple.security.device.bluetooth</key>
10+
<true/>
911
<key>com.apple.security.network.server</key>
1012
<true/>
1113
</dict>

macos/Runner/Release.entitlements

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@
44
<dict>
55
<key>com.apple.security.app-sandbox</key>
66
<true/>
7+
<key>com.apple.security.cs.allow-jit</key>
8+
<true/>
9+
<key>com.apple.security.device.bluetooth</key>
10+
<true/>
11+
<key>com.apple.security.network.server</key>
12+
<true/>
713
</dict>
814
</plist>

pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -838,4 +838,4 @@ packages:
838838
version: "3.1.3"
839839
sdks:
840840
dart: ">=3.8.0 <4.0.0"
841-
flutter: ">=3.32.4"
841+
flutter: ">=3.35.1"

0 commit comments

Comments
 (0)