From 79a889b700695086ae866a2c5c6fce021eab08b7 Mon Sep 17 00:00:00 2001 From: Samruddhi Rahegaonkar Date: Tue, 8 Apr 2025 19:54:44 +0530 Subject: [PATCH] Added BrightnessLevel --- lib/main.dart | 11 ++ lib/providers/BadgeBrightnessProvider.dart | 91 +++++++++++ lib/providers/brightness_provider.dart | 1 + lib/services/BleBrightnessService.dart | 180 +++++++++++++++++++++ lib/view/homescreen.dart | 145 ++++++++++++++++- lib/virtualbadge/view/animated_badge.dart | 10 +- lib/virtualbadge/view/badge_paint.dart | 51 +++--- lib/virtualbadge/view/draw_badge.dart | 98 +++-------- 8 files changed, 488 insertions(+), 99 deletions(-) create mode 100644 lib/providers/BadgeBrightnessProvider.dart create mode 100644 lib/providers/brightness_provider.dart create mode 100644 lib/services/BleBrightnessService.dart diff --git a/lib/main.dart b/lib/main.dart index 5fe5aa3fd..faa0ceeb4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,7 @@ +import 'package:badgemagic/providers/BadgeBrightnessProvider.dart'; import 'package:badgemagic/providers/getitlocator.dart'; import 'package:badgemagic/providers/imageprovider.dart'; +import 'package:badgemagic/services/BleBrightnessService.dart'; import 'package:badgemagic/view/about_us_screen.dart'; import 'package:badgemagic/view/draw_badge_screen.dart'; import 'package:badgemagic/view/homescreen.dart'; @@ -8,16 +10,25 @@ import 'package:badgemagic/view/saved_clipart.dart'; import 'package:badgemagic/view/settings_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get_it/get_it.dart'; import 'package:provider/provider.dart'; import 'globals/globals.dart' as globals; void main() { + final getIt = GetIt.instance; + getIt.registerSingleton(BleBrightnessService()); + setupLocator(); WidgetsFlutterBinding.ensureInitialized(); runApp(MultiProvider( providers: [ ChangeNotifierProvider( create: (context) => getIt()), + ChangeNotifierProvider( + create: (_) => BadgeBrightnessProvider( + GetIt.instance(), + ), + ) ], child: const MyApp(), )); diff --git a/lib/providers/BadgeBrightnessProvider.dart b/lib/providers/BadgeBrightnessProvider.dart new file mode 100644 index 000000000..d2c2ea0be --- /dev/null +++ b/lib/providers/BadgeBrightnessProvider.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:badgemagic/services/BleBrightnessService.dart'; + +class BadgeBrightnessProvider extends ChangeNotifier { + final BleBrightnessService _bleBrightnessService; + double _brightness = 50.0; // Default brightness level + bool _isConnected = false; + String _errorMessage = ""; + bool _isBrightnessVisible = false; + + // Getters + double get brightness => _brightness; + bool get isConnected => _isConnected; + String get errorMessage => _errorMessage; + bool get isBrightnessVisible => _isBrightnessVisible; + + BadgeBrightnessProvider(this._bleBrightnessService) { + _initialize(); + } + + Future _initialize() async { + await _bleBrightnessService.initialize(); + + _bleBrightnessService.connectionStatus.listen((connected) { + _isConnected = connected; + notifyListeners(); + + if (connected) { + getCurrentBrightness(); + } + }); + + _bleBrightnessService.errorStream.listen((error) { + _errorMessage = error; + notifyListeners(); + }); + } + + void toggleBrightnessVisibility(bool value) { + _isBrightnessVisible = value; + notifyListeners(); + } + + Future connectToDevice(String deviceId) async { + bool result = await _bleBrightnessService.connectToDevice(deviceId); + if (result) { + _errorMessage = ""; + } + notifyListeners(); + return result; + } + + Future setBrightness(double brightnessLevel) async { + _brightness = brightnessLevel; + notifyListeners(); + + if (_isConnected) { + bool success = + await _bleBrightnessService.setBrightness(brightnessLevel.round()); + + if (!success && _errorMessage.isEmpty) { + _errorMessage = "Failed to update brightness"; + notifyListeners(); + } + + return success; + } + + // If not connected, return true since we updated the local state + return true; + } + + Future getCurrentBrightness() async { + final currentBrightness = + await _bleBrightnessService.getCurrentBrightness(); + if (currentBrightness != null) { + _brightness = currentBrightness.toDouble(); + notifyListeners(); + } + } + + Future disconnect() async { + await _bleBrightnessService.disconnect(); + } + + @override + void dispose() { + _bleBrightnessService.dispose(); + super.dispose(); + } +} diff --git a/lib/providers/brightness_provider.dart b/lib/providers/brightness_provider.dart new file mode 100644 index 000000000..613744d49 --- /dev/null +++ b/lib/providers/brightness_provider.dart @@ -0,0 +1 @@ +// TODO Implement this library. diff --git a/lib/services/BleBrightnessService.dart b/lib/services/BleBrightnessService.dart new file mode 100644 index 000000000..c51f96106 --- /dev/null +++ b/lib/services/BleBrightnessService.dart @@ -0,0 +1,180 @@ +import 'dart:async'; +import 'package:flutter_blue_plus/flutter_blue_plus.dart'; +import 'package:logger/logger.dart'; + +class BleBrightnessService { + final Logger logger = Logger(); + BluetoothDevice? connectedDevice; + BluetoothCharacteristic? brightnessCharacteristic; + + final String badgeServiceUuid = "0000fee0-0000-1000-8000-00805f9b34fb"; + final String brightnessCharacteristicUuid = + "0000fee1-0000-1000-8000-00805f9b34fb"; + + final _connectionStatus = StreamController.broadcast(); + Stream get connectionStatus => _connectionStatus.stream; + + final _errorStream = StreamController.broadcast(); + Stream get errorStream => _errorStream.stream; + + bool simulationMode = false; + int simulatedBrightness = 50; + + bool get isConnected => + simulationMode || + (connectedDevice != null && brightnessCharacteristic != null); + + Future initialize({bool enableSimulation = false}) async { + simulationMode = enableSimulation; + if (simulationMode) { + logger.i("Simulation mode enabled"); + _connectionStatus.add(true); + return; + } + + try { + await FlutterBluePlus.turnOn(); + if (!await FlutterBluePlus.isAvailable) { + _errorStream.add("Bluetooth is not available on this device"); + return; + } + } catch (e) { + logger.e("Error initializing Bluetooth: $e"); + _errorStream.add("Error initializing Bluetooth: $e"); + } + } + + Future connectToDevice(String deviceId) async { + if (simulationMode) { + logger.i("Simulated connection to device: $deviceId"); + _connectionStatus.add(true); + return true; + } + try { + List connectedDevices = FlutterBluePlus.connectedDevices; + for (var device in connectedDevices) { + if (device.remoteId.toString() == deviceId) { + return await _connectToDevice(device); + } + } + } catch (e) { + logger.e("Connection error: $e"); + _errorStream.add("Connection error: $e"); + } + return false; + } + + Future _connectToDevice(BluetoothDevice device) async { + if (simulationMode) return true; + try { + await device.connect(autoConnect: false); + connectedDevice = device; + _connectionStatus.add(true); + await _discoverServices(); + return true; + } catch (e) { + logger.e("Failed to connect: $e"); + _errorStream.add("Failed to connect: $e"); + return false; + } + } + + Future _discoverServices() async { + if (simulationMode) return; + if (connectedDevice == null) return; + try { + List services = + await connectedDevice!.discoverServices(); + for (var service in services) { + if (service.uuid.toString() == badgeServiceUuid) { + for (var characteristic in service.characteristics) { + if (characteristic.uuid.toString() == + brightnessCharacteristicUuid) { + brightnessCharacteristic = characteristic; + logger.i("Found brightness characteristic"); + break; + } + } + } + } + if (brightnessCharacteristic == null) { + _errorStream.add("Brightness characteristic not found"); + } + } catch (e) { + logger.e("Error discovering services: $e"); + _errorStream.add("Error discovering services: $e"); + } + } + + Future setBrightness(int brightnessLevel) async { + if (!isConnected) { + _errorStream.add("Not connected to device"); + return false; + } + final validBrightness = brightnessLevel.clamp(0, 100); + if (simulationMode) { + simulatedBrightness = validBrightness; + logger.i("Simulated brightness set to $validBrightness%"); + return true; + } + try { + int scaledBrightness = (validBrightness * 2.55).round(); + List payload = [0x01, scaledBrightness]; + await brightnessCharacteristic!.write(payload, withoutResponse: false); + logger.i("Brightness set to $validBrightness%"); + return true; + } catch (e) { + logger.e("Failed to set brightness: $e"); + _errorStream.add("Failed to set brightness: $e"); + return false; + } + } + + Future getCurrentBrightness() async { + if (!isConnected) { + _errorStream.add("Not connected to device"); + return null; + } + if (simulationMode) { + return simulatedBrightness; + } + try { + List value = await brightnessCharacteristic!.read(); + if (value.length > 1) { + return (value[1] / 2.55).round(); + } + return null; + } catch (e) { + logger.e("Failed to read brightness: $e"); + _errorStream.add("Failed to read brightness: $e"); + return null; + } + } + + Future disconnect() async { + if (simulationMode) { + logger.i("Simulated disconnection"); + _connectionStatus.add(false); + return; + } + if (connectedDevice != null) { + try { + await connectedDevice!.disconnect(); + logger.i("Disconnected from device"); + } catch (e) { + logger.e("Error disconnecting: $e"); + _errorStream.add("Error disconnecting: $e"); + } finally { + connectedDevice = null; + brightnessCharacteristic = null; + _connectionStatus.add(false); + } + } + } + + void dispose() { + disconnect(); + _connectionStatus.close(); + _errorStream.close(); + } +} diff --git a/lib/view/homescreen.dart b/lib/view/homescreen.dart index 0fdfe43d6..c4ebb2dc5 100644 --- a/lib/view/homescreen.dart +++ b/lib/view/homescreen.dart @@ -8,6 +8,7 @@ import 'package:badgemagic/badge_effect/invert_led_effect.dart'; import 'package:badgemagic/badge_effect/marquee_effect.dart'; import 'package:badgemagic/constants.dart'; import 'package:badgemagic/providers/animation_badge_provider.dart'; +import 'package:badgemagic/providers/BadgeBrightnessProvider.dart'; import 'package:badgemagic/providers/badge_message_provider.dart'; import 'package:badgemagic/providers/imageprovider.dart'; import 'package:badgemagic/providers/speed_dial_provider.dart'; @@ -49,12 +50,27 @@ class _HomeScreenState extends State bool isDialInteracting = false; String errorVal = ""; + // Use the provider for brightness management + late BadgeBrightnessProvider _brightnessProvider; + @override void initState() { inlineimagecontroller.addListener(handleTextChange); _setPortraitOrientation(); WidgetsBinding.instance.addPostFrameCallback((_) { inlineImageProvider.setContext(context); + + // Get the brightness provider from context + _brightnessProvider = + Provider.of(context, listen: false); + + // Connect to badge (using stored ID or default) + final String deviceId = + "YOUR_DEVICE_ID"; // Get from preferences in real app + _brightnessProvider.connectToDevice(deviceId).then((_) { + print("Connection status: ${_brightnessProvider.isConnected}"); + setState(() {}); + }); }); _startImageCaching(); speedDialProvider = SpeedDialProvider(animationProvider); @@ -186,6 +202,121 @@ class _HomeScreenState extends State ), ), ), + + // Use Consumer to listen for changes in the brightness provider + Consumer( + builder: (context, brightnessProvider, _) { + // Show connection status and brightness controls + return Column( + children: [ + // Connection status indicator - show errors if any + if (brightnessProvider.errorMessage.isNotEmpty) + Container( + margin: EdgeInsets.symmetric(horizontal: 15.w), + padding: EdgeInsets.all(8.w), + decoration: BoxDecoration( + color: Colors.red[100], + borderRadius: BorderRadius.circular(5.r), + ), + child: Text( + brightnessProvider.errorMessage, + style: TextStyle(color: Colors.red[900]), + ), + ), + + // Connection status indicator - show success if connected + if (brightnessProvider.isConnected && + brightnessProvider.errorMessage.isEmpty) + Container( + margin: EdgeInsets.symmetric(horizontal: 15.w), + padding: EdgeInsets.all(8.w), + decoration: BoxDecoration( + color: Colors.green[100], + borderRadius: BorderRadius.circular(5.r), + ), + child: Text( + "Badge Connected", + style: TextStyle(color: Colors.green[900]), + ), + ), + + // Toggle for brightness control + Container( + margin: EdgeInsets.symmetric(horizontal: 15.w), + child: SwitchListTile( + title: Text('Adjust Brightness'), + subtitle: Text( + 'Control LED intensity (works on virtual badge too)'), + value: brightnessProvider.isBrightnessVisible, + activeColor: colorPrimary, + onChanged: (bool value) { + brightnessProvider + .toggleBrightnessVisibility(value); + }, + ), + ), + + // Brightness Control Section (Shown/Hidden Based on Toggle) + Visibility( + visible: brightnessProvider.isBrightnessVisible, + child: Container( + margin: EdgeInsets.all(15.w), + padding: EdgeInsets.all(10.w), + decoration: BoxDecoration( + color: Colors.grey[200], + borderRadius: BorderRadius.circular(10.r), + ), + child: Column( + children: [ + Padding( + padding: + EdgeInsets.symmetric(vertical: 10.h), + child: Text( + 'Set Brightness Level', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold), + ), + ), + Slider( + value: brightnessProvider.brightness, + min: 0, + max: 100, + divisions: 100, + label: brightnessProvider.brightness + .round() + .toString(), + activeColor: colorPrimary, + onChanged: (double value) { + brightnessProvider.setBrightness(value); + }, + ), + Padding( + padding: + EdgeInsets.symmetric(vertical: 10.h), + child: Text( + "Brightness: ${brightnessProvider.brightness.round()}%", + style: TextStyle(fontSize: 16), + ), + ), + // Add a refresh button to get current brightness from device + TextButton.icon( + icon: Icon(Icons.refresh), + label: Text("Refresh from device"), + onPressed: () { + brightnessProvider + .getCurrentBrightness(); + }, + ), + ], + ), + ), + ), + ], + ); + }, + ), + Visibility( visible: isPrefixIconClicked, child: Container( @@ -293,6 +424,13 @@ class _HomeScreenState extends State children: [ GestureDetector( onTap: () { + // Get brightness value from provider + final brightnessProvider = + Provider.of( + context, + listen: false); + + // Include brightness in the transfer params badgeData.checkAndTransfer( inlineImageProvider.getController().text, animationProvider @@ -304,8 +442,11 @@ class _HomeScreenState extends State speedDialProvider.getOuterValue(), modeValueMap[animationProvider .getAnimationIndex()], - null, - false); + brightnessProvider.brightness.round() + as Map?, // Pass brightness value + brightnessProvider + .isConnected); // Pass connection status }, child: Container( padding: EdgeInsets.symmetric( diff --git a/lib/virtualbadge/view/animated_badge.dart b/lib/virtualbadge/view/animated_badge.dart index e28805e49..2a5d6b2b4 100644 --- a/lib/virtualbadge/view/animated_badge.dart +++ b/lib/virtualbadge/view/animated_badge.dart @@ -1,3 +1,4 @@ +import 'package:badgemagic/providers/BadgeBrightnessProvider.dart'; import 'package:badgemagic/providers/animation_badge_provider.dart'; import 'package:badgemagic/virtualbadge/view/badge_paint.dart'; import 'package:flutter/material.dart'; @@ -21,11 +22,16 @@ class _AnimationBadgeState extends State { @override Widget build(BuildContext context) { - final provider = context.watch(); + final animationProvider = context.watch(); + final brightnessProvider = context.watch(); + return AspectRatio( aspectRatio: 3.2, child: CustomPaint( - painter: BadgePaint(grid: provider.getPaintGrid()), + painter: BadgePaint( + grid: animationProvider.getPaintGrid(), + brightness: brightnessProvider.brightness, + ), ), ); } diff --git a/lib/virtualbadge/view/badge_paint.dart b/lib/virtualbadge/view/badge_paint.dart index 782ab0127..549ac1a28 100644 --- a/lib/virtualbadge/view/badge_paint.dart +++ b/lib/virtualbadge/view/badge_paint.dart @@ -1,27 +1,33 @@ import 'dart:math' as math; - import 'package:badgemagic/bademagic_module/utils/badge_utils.dart'; import 'package:flutter/material.dart'; class BadgePaint extends CustomPainter { - BadgeUtils badgeUtils = BadgeUtils(); + final BadgeUtils badgeUtils = BadgeUtils(); final List> grid; + final double brightness; // Brightness as a percentage (0–100) - BadgePaint({required this.grid}); + BadgePaint({ + required this.grid, + required this.brightness, + }); @override void paint(Canvas canvas, Size size) { // Padding for the rectangle - MapEntry badgeOffsetBackground = + final MapEntry badgeOffsetBackground = badgeUtils.getBadgeOffsetBackground(size); - double offsetHeightBadgeBackground = badgeOffsetBackground.key; - double offsetWidthBadgeBackground = badgeOffsetBackground.value; + final double offsetHeightBadgeBackground = badgeOffsetBackground.key; + final double offsetWidthBadgeBackground = badgeOffsetBackground.value; // Size of the rectangle - MapEntry badgeSize = badgeUtils.getBadgeSize( - offsetHeightBadgeBackground, offsetWidthBadgeBackground, size); - double badgeHeight = badgeSize.key; - double badgeWidth = badgeSize.value; + final MapEntry badgeSize = badgeUtils.getBadgeSize( + offsetHeightBadgeBackground, + offsetWidthBadgeBackground, + size, + ); + final double badgeHeight = badgeSize.key; + final double badgeWidth = badgeSize.value; // Draw the outer rectangle final Paint rectPaint = Paint() @@ -39,21 +45,28 @@ class BadgePaint extends CustomPainter { canvas.drawRRect(gridRect, rectPaint); - var cellSize = badgeWidth / grid[0].length; + final double cellSize = badgeWidth / grid[0].length; + + final MapEntry cellStartCoordinate = + badgeUtils.getCellStartCoordinate( + offsetWidthBadgeBackground, + offsetHeightBadgeBackground, + badgeWidth, + badgeHeight, + ); + final double cellStartX = cellStartCoordinate.key; + final double cellStartY = cellStartCoordinate.value; - MapEntry cellStartCoordinate = - badgeUtils.getCellStartCoordinate(offsetWidthBadgeBackground, - offsetHeightBadgeBackground, badgeWidth, badgeHeight); - double cellStartX = cellStartCoordinate.key; - double cellStartY = cellStartCoordinate.value; // Draw the cells for (int row = 0; row < grid.length; row++) { for (int col = 0; col < grid[row].length; col++) { - var cellStartRow = cellStartY + row * cellSize; - var cellStartCol = cellStartX + col * (cellSize * 0.93); + final double cellStartRow = cellStartY + row * cellSize; + final double cellStartCol = cellStartX + col * (cellSize * 0.93); final Paint paint = Paint() - ..color = grid[row][col] ? Colors.red : Colors.grey.shade900 + ..color = grid[row][col] + ? Colors.red.withOpacity(brightness / 100) + : Colors.grey.shade900 ..style = PaintingStyle.fill; final Rect cellRect = Rect.fromLTWH( diff --git a/lib/virtualbadge/view/draw_badge.dart b/lib/virtualbadge/view/draw_badge.dart index 811a48c05..cdaee111e 100644 --- a/lib/virtualbadge/view/draw_badge.dart +++ b/lib/virtualbadge/view/draw_badge.dart @@ -1,4 +1,5 @@ import 'package:badgemagic/bademagic_module/utils/badge_utils.dart'; +import 'package:badgemagic/providers/BadgeBrightnessProvider.dart'; import 'package:badgemagic/providers/draw_badge_provider.dart'; import 'package:badgemagic/virtualbadge/view/badge_paint.dart'; import 'package:flutter/material.dart'; @@ -19,13 +20,13 @@ class _BMBadgeState extends State { @override void initState() { + super.initState(); if (widget.providerInit != null) { widget.providerInit!(drawProvider); } if (widget.badgeGrid != null) { drawProvider.updateDrawViewGrid(widget.badgeGrid!); } - super.initState(); } static const int rows = 11; @@ -70,88 +71,33 @@ class _BMBadgeState extends State { drawProvider.setDrawViewGrid(row, col); } - setState(() { - // drawProvider.setDrawViewGrid(row, col); - }); + setState(() {}); } @override Widget build(BuildContext context) { var width = MediaQuery.of(context).size.width; Size size = Size(width, width / 3.2); - return ChangeNotifierProvider( - create: (context) => drawProvider, - child: GestureDetector( - onPanUpdate: _handlePanUpdate, - child: AspectRatio( - aspectRatio: 3.2, - child: Consumer( - builder: (context, value, child) => CustomPaint( - painter: BadgePaint(grid: value.getDrawViewGrid()), - size: size), + + return ChangeNotifierProvider.value( + value: drawProvider, + child: Consumer2( + builder: (context, drawProvider, brightnessProvider, child) { + return GestureDetector( + onPanUpdate: _handlePanUpdate, + child: AspectRatio( + aspectRatio: 3.2, + child: CustomPaint( + painter: BadgePaint( + grid: drawProvider.getDrawViewGrid(), + brightness: brightnessProvider.brightness, + ), + size: size, + ), ), - )), + ); + }, + ), ); } } - -// class AnimationBadgeROW extends LeafRenderObjectWidget { -// final DrawBadgeProvider provider; - -// const AnimationBadgeROW({super.key, required this.provider}); - -// @override -// RenderObject createRenderObject(BuildContext context) { -// final renderObject = BadgeRenderObject(provider: provider); -// provider.addListener(renderObject.onProviderUpdate); -// return renderObject; -// } - -// @override -// void updateRenderObject( -// BuildContext context, covariant BadgeRenderObject renderObject) { -// renderObject.provider = provider; -// } -// } - -// class BadgeRenderObject extends RenderBox with RenderObjectWithChildMixin { -// DrawBadgeProvider provider; - -// BadgeRenderObject({required this.provider}); - -// @override -// void performLayout() { -// var width = constraints.maxWidth; -// var height = constraints.maxHeight; - -// // Maintain aspect ratio but ensure it fits within the available height -// var desiredHeight = width / 3.2; -// if (desiredHeight > height) { -// desiredHeight = height; -// } - -// size = constraints.constrain(Size(width, desiredHeight)); -// } - -// @override -// void paint(PaintingContext context, Offset offset) { -// final Canvas canvas = context.canvas; -// BadgePaint(grid: provider.getDrawViewGrid()).paint(canvas, size); -// } - -// @override -// bool get alwaysNeedsCompositing => true; - -// void onProviderUpdate() { -// markNeedsPaint(); -// } - -// @override -// bool hitTest(BoxHitTestResult result, {required Offset position}) { -// if (size.contains(position)) { -// result.add(BoxHitTestEntry(this, position)); -// return true; -// } -// return false; -// } -// }