diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 31ae953..15b3e6c 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,38 +1,7 @@ { "permissions": { "allow": [ - "Bash(magick Icon-App-1024x1024@1x.png -background white -alpha remove -alpha off Icon-App-1024x1024@1x.png)", - "Bash(convert Icon-App-1024x1024@1x.png -background white -alpha remove -alpha off Icon-App-1024x1024@1x-noalpha.png)", - "Bash(sips -s format jpeg Icon-App-1024x1024@1x.png --out /tmp/icon_flattened.jpg)", - "Bash(sips -s format png /tmp/icon_flattened.jpg --out Icon-App-1024x1024@1x.png)", - "Bash(flutter analyze lib/services/ble/)", - "Bash(flutter analyze lib/models/location_trail.dart lib/providers/map_provider.dart lib/widgets/map/location_trail_layer.dart lib/widgets/map/trail_controls.dart lib/widgets/map/compass/compass_filters.dart)", - "Bash(flutter analyze lib/screens/map_tab.dart)", - "Bash(flutter gen-l10n)", - "Bash(flutter analyze lib/screens/contacts_tab.dart)", - "Bash(flutter analyze lib/widgets/permission_request_dialog.dart lib/main.dart)", - "Bash(flutter analyze lib/screens/settings_screen.dart)", - "Bash(flutter analyze lib/services/location_tracking_service.dart)", - "Bash(flutter analyze lib/main.dart lib/screens/home_screen.dart)", - "Bash(flutter analyze lib/main.dart)", - "Bash(flutter analyze lib/providers/app_provider.dart lib/services/meshcore_ble_service.dart)", - "Bash(flutter analyze lib/services/meshcore_ble_service.dart)", - "Bash(flutter analyze lib/services/meshcore_ble_service.dart lib/services/location_tracking_service.dart)", - "Bash(flutter analyze lib/widgets/map/trail_controls.dart lib/screens/map_tab.dart)", - "Bash(flutter analyze lib/screens/device_config_screen.dart)", - "Bash(flutter analyze lib/services/ble/ble_response_handler.dart)", - "Bash(flutter analyze)", - "Bash(flutter analyze lib/models/contact.dart)", - "Read(//Users/dz0ny/meshcore-sar/MeshCore/**)", - "Read(//Users/dz0ny/meshcore-sar/**)", - "Bash(flutter analyze lib/models/sent_message_tracker.dart lib/models/message.dart lib/services/ble/ble_response_handler.dart)", - "Bash(flutter pub get)", - "Bash(flutter analyze lib/models/contact.dart lib/providers/app_provider.dart lib/widgets/contacts/contact_tile.dart lib/widgets/map/drawing_toolbar.dart)", - "Bash(flutter analyze lib/services/locale_preferences.dart)", - "Bash(flutter analyze lib/services/ble/ble_response_handler.dart lib/models/sent_message_tracker.dart)", - "Bash(flutter analyze lib/utils/message_extensions.dart)", - "Bash(flutter analyze lib/l10n/)", - "Bash(flutter analyze lib/services/ble/ble_response_handler.dart lib/utils/message_extensions.dart)" + "Bash(flutter analyze lib)" ], "deny": [], "ask": [] diff --git a/integration_test/app_screenshots_test.dart b/integration_test/app_screenshots_test.dart deleted file mode 100644 index e8fb275..0000000 --- a/integration_test/app_screenshots_test.dart +++ /dev/null @@ -1,217 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:provider/provider.dart'; - -import 'package:meshcore_sar_app/main.dart'; -import 'package:meshcore_sar_app/providers/connection_provider.dart'; -import 'package:meshcore_sar_app/providers/contacts_provider.dart'; -import 'package:meshcore_sar_app/providers/messages_provider.dart'; -import 'package:meshcore_sar_app/providers/drawing_provider.dart'; -import 'package:meshcore_sar_app/providers/map_provider.dart'; - -import 'helpers/screenshot_helper.dart'; -import 'helpers/mock_data.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - group('App Screenshots', () { - late ScreenshotHelper screenshotHelper; - - testWidgets('Capture all app screens with mock data', (tester) async { - // Initialize screenshot helper - final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - screenshotHelper = ScreenshotHelper(binding); - - // Pump the app - await tester.pumpWidget(const MeshCoreSarApp()); - await tester.pumpAndSettle(); - - // =================================================================== - // 1. DISCONNECTED STATE - Home Screen - // =================================================================== - await screenshotHelper.takeScreenshot( - tester, - 'home_disconnected', - wait: const Duration(seconds: 1), - ); - - // =================================================================== - // 2. SIMULATED CONNECTED STATE (inject mock data into providers) - // =================================================================== - // Note: Since we can't actually connect to a BLE device in tests, - // we'll need to manually inject mock data into the providers. - // This requires accessing the providers through the context. - - final context = tester.element(find.byType(MaterialApp)); - final contactsProvider = context.read(); - final messagesProvider = context.read(); - - // Inject mock contacts - final mockContacts = MockData.getMockContacts(); - for (final contact in mockContacts) { - contactsProvider.addOrUpdateContact(contact); - } - - // Inject mock messages - final mockMessages = MockData.getMockMessages(); - for (final message in mockMessages) { - messagesProvider.receiveMessage(message); - } - - // Inject mock SAR markers - final mockSarMarkers = MockData.getMockSarMarkers(); - for (final marker in mockSarMarkers) { - messagesProvider.addSarMarker(marker); - } - - await tester.pumpAndSettle(); - - // =================================================================== - // 3. MESSAGES TAB WITH DATA - // =================================================================== - // The app should default to Messages tab (index 0) - await screenshotHelper.takeScreenshot( - tester, - 'messages_list_with_sar_markers', - ); - - // Try to find and tap a SAR marker to show detail - final sarMarkerFinder = find.byWidgetPredicate( - (widget) => widget.runtimeType.toString().contains('SarMarker'), - ); - if (sarMarkerFinder.hasFound) { - await tester.tapAndSettle(sarMarkerFinder.first); - await screenshotHelper.takeScreenshot( - tester, - 'messages_sar_marker_detail', - ); - // Go back - await tester.pageBack(); - await tester.pumpAndSettle(); - } - - // =================================================================== - // 4. CONTACTS TAB - // =================================================================== - // Find and tap the Contacts tab - final contactsTab = find.text('Contacts'); - if (contactsTab.hasFound) { - await tester.tapAndSettle(contactsTab); - } else { - // Try icon-based navigation - final tabBar = find.byType(TabBar); - if (tabBar.hasFound) { - final contactsIcon = find.descendant( - of: tabBar, - matching: find.byIcon(Icons.contacts), - ); - await tester.tapAndSettle(contactsIcon); - } - } - - await screenshotHelper.takeScreenshot( - tester, - 'contacts_list_with_teams', - ); - - // Try to find and tap a contact to show detail - final contactListTile = find.byType(ListTile).first; - if (contactListTile.hasFound) { - await tester.tapAndSettle(contactListTile); - await screenshotHelper.takeScreenshot( - tester, - 'contacts_detail_dialog', - ); - // Close dialog (tap outside or back button) - await tester.pageBack(); - await tester.pumpAndSettle(); - } - - // =================================================================== - // 5. MAP TAB - // =================================================================== - // Find and tap the Map tab - final mapTab = find.text('Map'); - if (mapTab.hasFound) { - await tester.tapAndSettle(mapTab); - } else { - // Try icon-based navigation - final tabBar = find.byType(TabBar); - if (tabBar.hasFound) { - final mapIcon = find.descendant( - of: tabBar, - matching: find.byIcon(Icons.map), - ); - await tester.tapAndSettle(mapIcon); - } - } - - // Wait for map to load - await tester.pumpAndSettle(const Duration(seconds: 2)); - await screenshotHelper.takeScreenshot( - tester, - 'map_with_markers_and_sar', - ); - - // Try to find and open the map legend - final legendFinder = find.byWidgetPredicate( - (widget) => widget.runtimeType.toString().contains('Legend'), - ); - if (legendFinder.hasFound) { - await tester.tapAndSettle(legendFinder.first); - await screenshotHelper.takeScreenshot( - tester, - 'map_legend_expanded', - ); - } - - // =================================================================== - // 6. SETTINGS SCREEN (via menu) - // =================================================================== - // Go back to Messages tab - final messagesTab = find.text('Messages'); - if (messagesTab.hasFound) { - await tester.tapAndSettle(messagesTab); - } - - // Find and tap the menu button - final menuButton = find.byIcon(Icons.more_vert); - if (menuButton.hasFound) { - await tester.tapAndSettle(menuButton); - - // Find and tap Settings in the popup menu - final settingsMenuItem = find.text('Settings'); - if (settingsMenuItem.hasFound) { - await tester.tapAndSettle(settingsMenuItem); - await tester.pumpAndSettle(const Duration(seconds: 1)); - - await screenshotHelper.takeScreenshot( - tester, - 'settings_screen', - ); - - // Go back - await tester.pageBack(); - await tester.pumpAndSettle(); - } - } - - // =================================================================== - // 7. SIMULATED DEVICE CONNECTION DIALOG - // =================================================================== - // NOTE: This is challenging without actual BLE, but we can try to - // trigger the connection dialog - // For now, we'll skip this as it requires disconnecting first - - // =================================================================== - // SUMMARY - // =================================================================== - print('\nโœ… Screenshot capture complete!'); - print('๐Ÿ“ธ Total screenshots taken: ${screenshotHelper.screenshotCount}'); - print('\nScreenshots are saved in the default integration test output directory.'); - print('To view them, check your flutter drive output folder.\n'); - }); - }); -} diff --git a/integration_test/helpers/mock_data.dart b/integration_test/helpers/mock_data.dart deleted file mode 100644 index 476fedd..0000000 --- a/integration_test/helpers/mock_data.dart +++ /dev/null @@ -1,230 +0,0 @@ -import 'package:meshcore_sar_app/models/contact.dart'; -import 'package:meshcore_sar_app/models/message.dart'; -import 'package:meshcore_sar_app/models/sar_marker.dart'; - -/// Mock data for integration tests and screenshots -class MockData { - /// Generate mock contacts with predictable data - static List getMockContacts() { - return [ - Contact( - publicKey: '0x1111111111111111111111111111111111111111111111111111111111111111', - name: 'Alpha Team Lead', - contactType: ContactType.chat, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 2)), - lastLocation: const ContactLocation( - latitude: 46.0569, - longitude: 14.5058, - altitude: 295.0, - ), - batteryMillivolts: 3850, - hopCount: 1, - rssi: -45, - ), - Contact( - publicKey: '0x2222222222222222222222222222222222222222222222222222222222222222', - name: 'Bravo Scout', - contactType: ContactType.chat, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 5)), - lastLocation: const ContactLocation( - latitude: 46.0589, - longitude: 14.5078, - altitude: 310.0, - ), - batteryMillivolts: 3700, - hopCount: 2, - rssi: -68, - ), - Contact( - publicKey: '0x3333333333333333333333333333333333333333333333333333333333333333', - name: 'Charlie Base', - contactType: ContactType.chat, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 1)), - lastLocation: const ContactLocation( - latitude: 46.0549, - longitude: 14.5038, - altitude: 285.0, - ), - batteryMillivolts: 4100, - hopCount: 0, - rssi: -35, - ), - Contact( - publicKey: '0x4444444444444444444444444444444444444444444444444444444444444444', - name: 'Delta Medic', - contactType: ContactType.chat, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 8)), - lastLocation: const ContactLocation( - latitude: 46.0609, - longitude: 14.5098, - altitude: 320.0, - ), - batteryMillivolts: 3600, - hopCount: 3, - rssi: -75, - ), - Contact( - publicKey: '0x5555555555555555555555555555555555555555555555555555555555555555', - name: 'Mountain Repeater 1', - contactType: ContactType.repeater, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 1)), - lastLocation: const ContactLocation( - latitude: 46.0650, - longitude: 14.5150, - altitude: 450.0, - ), - batteryMillivolts: 4150, - hopCount: 0, - rssi: -40, - ), - Contact( - publicKey: '0x6666666666666666666666666666666666666666666666666666666666666666', - name: 'SAR Command Room', - contactType: ContactType.room, - lastAdvertisement: DateTime.now().subtract(const Duration(minutes: 30)), - hopCount: 1, - rssi: -50, - ), - ]; - } - - /// Generate mock messages with SAR markers - static List getMockMessages() { - final now = DateTime.now(); - return [ - Message( - id: 'msg1', - sender: '0x1111111111111111111111111111111111111111111111111111111111111111', - senderName: 'Alpha Team Lead', - content: 'Team Alpha in position, beginning sweep of sector 3', - timestamp: now.subtract(const Duration(minutes: 15)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg2', - sender: '0x2222222222222222222222222222222222222222222222222222222222222222', - senderName: 'Bravo Scout', - content: 'S:๐Ÿง‘:46.0589,14.5078:Found injured hiker near trail marker 7', - timestamp: now.subtract(const Duration(minutes: 12)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg3', - sender: 'self', - senderName: 'You', - content: 'Copy that Bravo, sending medic to your location', - timestamp: now.subtract(const Duration(minutes: 11)), - isSent: true, - isPublicChannel: false, - ), - Message( - id: 'msg4', - sender: '0x4444444444444444444444444444444444444444444444444444444444444444', - senderName: 'Delta Medic', - content: 'En route to Bravo position, ETA 5 minutes', - timestamp: now.subtract(const Duration(minutes: 10)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg5', - sender: '0x3333333333333333333333333333333333333333333333333333333333333333', - senderName: 'Charlie Base', - content: 'S:๐Ÿ•๏ธ:46.0549,14.5038:Staging area established, supplies available', - timestamp: now.subtract(const Duration(minutes: 8)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg6', - sender: '0x1111111111111111111111111111111111111111111111111111111111111111', - senderName: 'Alpha Team Lead', - content: 'S:๐Ÿ”ฅ:46.0620,14.5120:Small campfire spotted in sector 4, monitoring', - timestamp: now.subtract(const Duration(minutes: 5)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg7', - sender: '0x2222222222222222222222222222222222222222222222222222222222222222', - senderName: 'Bravo Scout', - content: 'Patient stabilized, waiting for extraction', - timestamp: now.subtract(const Duration(minutes: 3)), - isSent: false, - isPublicChannel: false, - ), - Message( - id: 'msg8', - sender: 'self', - senderName: 'You', - content: 'All teams: Weather window closing in 2 hours, prepare to RTB', - timestamp: now.subtract(const Duration(minutes: 1)), - isSent: true, - isPublicChannel: true, - ), - ]; - } - - /// Generate mock SAR markers from messages - static List getMockSarMarkers() { - final now = DateTime.now(); - return [ - SarMarker( - id: 'sar1', - type: SarMarkerType.foundPerson, - latitude: 46.0589, - longitude: 14.5078, - message: 'Found injured hiker near trail marker 7', - timestamp: now.subtract(const Duration(minutes: 12)), - sender: '0x2222222222222222222222222222222222222222222222222222222222222222', - senderName: 'Bravo Scout', - ), - SarMarker( - id: 'sar2', - type: SarMarkerType.stagingArea, - latitude: 46.0549, - longitude: 14.5038, - message: 'Staging area established, supplies available', - timestamp: now.subtract(const Duration(minutes: 8)), - sender: '0x3333333333333333333333333333333333333333333333333333333333333333', - senderName: 'Charlie Base', - ), - SarMarker( - id: 'sar3', - type: SarMarkerType.fireLocation, - latitude: 46.0620, - longitude: 14.5120, - message: 'Small campfire spotted in sector 4, monitoring', - timestamp: now.subtract(const Duration(minutes: 5)), - sender: '0x1111111111111111111111111111111111111111111111111111111111111111', - senderName: 'Alpha Team Lead', - ), - ]; - } - - /// Mock device info for connection status - static Map getMockDeviceInfo() { - return { - 'deviceName': 'MeshCore-SAR-DEMO', - 'firmwareVersion': '2.1.0', - 'hardwareVersion': 'v3', - 'publicKey': '0xAABBCCDDEEFF00112233445566778899AABBCCDDEEFF00112233445566778899', - 'batteryMillivolts': 3950, - 'storageUsed': 1024 * 512, // 512 KB - 'storageTotal': 1024 * 1024 * 4, // 4 MB - }; - } - - /// Mock radio parameters - static Map getMockRadioParams() { - return { - 'frequency': 915.0, - 'bandwidth': 125.0, - 'spreadingFactor': 9, - 'codingRate': 7, - 'txPower': 20, - }; - } -} diff --git a/integration_test/helpers/screenshot_helper.dart b/integration_test/helpers/screenshot_helper.dart deleted file mode 100644 index 3409c03..0000000 --- a/integration_test/helpers/screenshot_helper.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -/// Helper class for taking screenshots during integration tests -class ScreenshotHelper { - final IntegrationTestWidgetsFlutterBinding binding; - final String outputDir; - int _screenshotCounter = 0; - - ScreenshotHelper(this.binding, {this.outputDir = 'screenshots'}); - - /// Take a screenshot with automatic numbering and description - Future takeScreenshot( - WidgetTester tester, - String description, { - Duration? wait, - }) async { - // Wait for UI to settle - await tester.pumpAndSettle(wait ?? const Duration(milliseconds: 500)); - - // Add extra delay for animations - await Future.delayed(const Duration(milliseconds: 300)); - - // Increment counter - _screenshotCounter++; - - // Format filename: 01_description.png - final filename = - '${_screenshotCounter.toString().padLeft(2, '0')}_${_sanitizeFilename(description)}.png'; - - if (kDebugMode) { - print('๐Ÿ“ธ Taking screenshot: $filename'); - } - - // Take screenshot - await binding.takeScreenshot(filename); - } - - /// Sanitize filename by removing special characters - String _sanitizeFilename(String input) { - return input - .toLowerCase() - .replaceAll(RegExp(r'[^\w\s-]'), '') - .replaceAll(RegExp(r'[\s_]+'), '_') - .replaceAll(RegExp(r'^-+|-+$'), ''); - } - - /// Reset counter (useful for multiple test runs) - void resetCounter() { - _screenshotCounter = 0; - } - - /// Get current screenshot count - int get screenshotCount => _screenshotCounter; - - /// Create output directory if it doesn't exist - static Future ensureOutputDir(String path) async { - final dir = Directory(path); - if (!await dir.exists()) { - await dir.create(recursive: true); - } - } -} - -/// Extension methods for easier screenshot taking -extension ScreenshotTestExtension on WidgetTester { - /// Wait for a specific widget to appear - Future waitFor( - Finder finder, { - Duration timeout = const Duration(seconds: 10), - }) async { - final end = DateTime.now().add(timeout); - while (DateTime.now().isBefore(end)) { - await pump(); - if (finder.evaluate().isNotEmpty) { - return; - } - await Future.delayed(const Duration(milliseconds: 100)); - } - throw Exception('Widget not found: $finder'); - } - - /// Tap and wait for navigation - Future tapAndSettle(Finder finder, {Duration? settleDuration}) async { - await tap(finder); - await pumpAndSettle(settleDuration ?? const Duration(milliseconds: 500)); - } - - /// Scroll until widget is visible - Future scrollUntilVisible( - Finder finder, - Finder scrollable, { - double delta = 100, - int maxScrolls = 50, - }) async { - int scrollCount = 0; - while (finder.evaluate().isEmpty && scrollCount < maxScrolls) { - await drag(scrollable, Offset(0, -delta)); - await pump(const Duration(milliseconds: 100)); - scrollCount++; - } - if (finder.evaluate().isEmpty) { - throw Exception('Could not scroll to widget: $finder'); - } - } - - /// Fill text field and dismiss keyboard - Future enterTextAndDismiss(Finder finder, String text) async { - await enterText(finder, text); - await pump(); - await testTextInput.receiveAction(TextInputAction.done); - await pumpAndSettle(); - } -} diff --git a/lib/providers/connection_provider.dart b/lib/providers/connection_provider.dart index 21b5c7c..6663d00 100644 --- a/lib/providers/connection_provider.dart +++ b/lib/providers/connection_provider.dart @@ -146,7 +146,7 @@ class ConnectionProvider with ChangeNotifier { void _initializeBleService() { _bleService.onConnectionStateChanged = (isConnected) { - print('๐Ÿ”” [Provider] Connection state callback fired: $isConnected'); + debugPrint('๐Ÿ”” [Provider] Connection state callback fired: $isConnected'); _deviceInfo = _deviceInfo.copyWith( connectionState: isConnected ? ConnectionState.connected @@ -155,37 +155,37 @@ class ConnectionProvider with ChangeNotifier { : ConnectionState.disconnected), lastUpdate: DateTime.now(), ); - print( + debugPrint( ' Updated deviceInfo.connectionState: ${_deviceInfo.connectionState}', ); - print(' Updated deviceInfo.isConnected: ${_deviceInfo.isConnected}'); - print(' isReconnecting: ${_bleService.isReconnecting}'); + debugPrint(' Updated deviceInfo.isConnected: ${_deviceInfo.isConnected}'); + debugPrint(' isReconnecting: ${_bleService.isReconnecting}'); notifyListeners(); - print(' Notified listeners'); + debugPrint(' Notified listeners'); }; _bleService.onReconnectionAttempt = (attemptNumber, maxAttempts) { - print('๐Ÿ”„ [Provider] Reconnection attempt $attemptNumber/$maxAttempts'); + debugPrint('๐Ÿ”„ [Provider] Reconnection attempt $attemptNumber/$maxAttempts'); // Notify UI to update reconnection status display notifyListeners(); }; _bleService.onError = (error, {int? errorCode}) { - print('โš ๏ธ [Provider] BLE error received: $error'); - print(' Error code: ${errorCode ?? "none"}'); - print(' Current connection state: ${_deviceInfo.connectionState}'); + debugPrint('โš ๏ธ [Provider] BLE error received: $error'); + debugPrint(' Error code: ${errorCode ?? "none"}'); + debugPrint(' Current connection state: ${_deviceInfo.connectionState}'); _error = error; // Only set connection state to error if we're not already connected // Data parsing errors after connection shouldn't disconnect us if (_deviceInfo.connectionState != ConnectionState.connected) { - print(' Setting connection state to error'); + debugPrint(' Setting connection state to error'); _deviceInfo = _deviceInfo.copyWith( connectionState: ConnectionState.error, ); } else { - print( + debugPrint( ' Keeping connection state as connected (ignoring data parsing error)', ); } @@ -194,10 +194,10 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onContactNotFound = (contactPublicKey) async { - print('๐Ÿ”ง [Provider] Contact not found error detected - initiating auto-recovery'); + debugPrint('๐Ÿ”ง [Provider] Contact not found error detected - initiating auto-recovery'); if (contactPublicKey == null) { - print(' โš ๏ธ No contact public key available for recovery'); + debugPrint(' โš ๏ธ No contact public key available for recovery'); return; } @@ -206,12 +206,12 @@ class ConnectionProvider with ChangeNotifier { final pendingOp = _pendingSendOperations[operationId]; if (pendingOp == null || pendingOp.contact == null) { - print(' โš ๏ธ No pending operation found for recovery: $operationId'); + debugPrint(' โš ๏ธ No pending operation found for recovery: $operationId'); return; } - print(' ๐Ÿ“‹ Found pending operation for: ${pendingOp.contact!.advName}'); - print(' ๐Ÿ“ค Step 1: Adding contact to radio...'); + debugPrint(' ๐Ÿ“‹ Found pending operation for: ${pendingOp.contact!.advName}'); + debugPrint(' ๐Ÿ“ค Step 1: Adding contact to radio...'); try { // Step 1: Add the contact to the radio @@ -220,8 +220,8 @@ class ConnectionProvider with ChangeNotifier { // Small delay to ensure contact is added before retrying await Future.delayed(const Duration(milliseconds: 300)); - print(' โœ… Contact added successfully'); - print(' ๐Ÿ”„ Step 2: Retrying message send...'); + debugPrint(' โœ… Contact added successfully'); + debugPrint(' ๐Ÿ”„ Step 2: Retrying message send...'); // Step 2: Retry the send operation await _bleService.sendTextMessage( @@ -230,12 +230,12 @@ class ConnectionProvider with ChangeNotifier { attempt: pendingOp.retryAttempt, ); - print(' โœ… Auto-recovery completed - message resent'); + debugPrint(' โœ… Auto-recovery completed - message resent'); // Clear pending operation after successful recovery _pendingSendOperations.remove(operationId); } catch (e) { - print(' โŒ Auto-recovery failed: $e'); + debugPrint(' โŒ Auto-recovery failed: $e'); _error = 'Auto-recovery failed: $e'; notifyListeners(); @@ -269,12 +269,12 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onBinaryResponse = (publicKeyPrefix, tag, responseData) { - print('๐Ÿ“ฅ [Provider] Binary response received'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Binary response received'); + debugPrint( ' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}', ); - print(' Tag: $tag'); - print(' Response data: ${responseData.length} bytes'); + debugPrint(' Tag: $tag'); + debugPrint(' Response data: ${responseData.length} bytes'); // Mark ping as successful if this was a ping request // Binary responses can also be telemetry responses (newer firmware) _pingTracker.markPingSuccessful(publicKeyPrefix); @@ -282,12 +282,12 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onNoMoreMessages = () { - print('๐Ÿ“ฅ [Provider] Received NoMoreMessages signal'); + debugPrint('๐Ÿ“ฅ [Provider] Received NoMoreMessages signal'); _noMoreMessages = true; }; _bleService.onMessageWaiting = () { - print( + debugPrint( '๐Ÿ“ฅ [Provider] PUSH_CODE_MSG_WAITING received - auto-fetching messages via event', ); // Automatically fetch messages when push notification received @@ -297,11 +297,11 @@ class ConnectionProvider with ChangeNotifier { _bleService .onLoginSuccess = (publicKeyPrefix, permissions, isAdmin, tag) async { - print('๐Ÿ“ฅ [Provider] Login successful to room'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Login successful to room'); + debugPrint( ' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}', ); - print(' Permissions: $permissions, Admin: $isAdmin, Tag: $tag'); + debugPrint(' Permissions: $permissions, Admin: $isAdmin, Tag: $tag'); // Update room login state via helper await _roomLoginManager.handleLoginSuccess( @@ -316,8 +316,8 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onLoginFail = (publicKeyPrefix) { - print('๐Ÿ“ฅ [Provider] Login failed to room'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Login failed to room'); + debugPrint( ' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}', ); @@ -329,11 +329,11 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onAdvertReceived = (publicKey) { - print('๐Ÿ“ฅ [Provider] Advert received from node'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Advert received from node'); + debugPrint( ' Public key: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...', ); - print( + debugPrint( ' Note: Waiting for PUSH_CODE_NEW_ADVERT (0x8A) with full contact details', ); // The companion radio will automatically send PUSH_CODE_NEW_ADVERT if manual_add_contacts=0 @@ -341,11 +341,11 @@ class ConnectionProvider with ChangeNotifier { }; _bleService.onPathUpdated = (publicKey) { - print('๐Ÿ“ฅ [Provider] Path updated for contact'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Path updated for contact'); + debugPrint( ' Public key: ${publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}...', ); - print( + debugPrint( ' Note: Mesh network discovered a new/better routing path to this contact', ); // Forward the callback to ContactsProvider to trigger contact sync @@ -354,7 +354,7 @@ class ConnectionProvider with ChangeNotifier { _bleService .onMessageSent = (expectedAckTag, suggestedTimeoutMs, isFloodMode) { - print( + debugPrint( '๐Ÿ“ฅ [Provider] Message sent - ACK tag: $expectedAckTag, timeout: ${suggestedTimeoutMs}ms', ); @@ -362,7 +362,7 @@ class ConnectionProvider with ChangeNotifier { final messageId = _messageDeliveryTracker.popPendingMessageId(); if (messageId != null) { - print(' Matched with message ID: $messageId'); + debugPrint(' Matched with message ID: $messageId'); // Store the ACK tag to message ID mapping for delivery confirmation _messageDeliveryTracker.mapAckTagToMessageId(expectedAckTag, messageId); @@ -370,45 +370,45 @@ class ConnectionProvider with ChangeNotifier { // Notify callback with message ID onMessageSent?.call(messageId, expectedAckTag, suggestedTimeoutMs); } else { - print( + debugPrint( 'โš ๏ธ [Provider] SENT response received but no pending message IDs', ); } }; _bleService.onMessageDelivered = (ackCode, roundTripTimeMs) { - print( + debugPrint( '๐Ÿ“ฅ [Provider] Message delivered - ACK code: $ackCode, RTT: ${roundTripTimeMs}ms', ); onMessageDelivered?.call(ackCode, roundTripTimeMs); }; _bleService.onMessageEchoDetected = (messageId, echoCount, snrRaw, rssiDbm) { - print( + debugPrint( '๐Ÿ”Š [Provider] Echo detected - Message: $messageId, Count: $echoCount', ); onMessageEchoDetected?.call(messageId, echoCount, snrRaw, rssiDbm); }; _bleService.onStatusResponse = (publicKeyPrefix, statusData) { - print('๐Ÿ“ฅ [Provider] Status response received from node'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Status response received from node'); + debugPrint( ' Public key prefix: ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}', ); - print(' Status data: ${statusData.length} bytes'); + debugPrint(' Status data: ${statusData.length} bytes'); // Forward the callback to whoever needs it (e.g., ContactsProvider) onStatusResponse?.call(publicKeyPrefix, statusData); }; _bleService.onDeviceInfoReceived = (deviceInfo) { - print('๐Ÿ“ฅ [Provider] Received DeviceInfo:'); - print(' Firmware Version: ${deviceInfo['firmwareVersion']}'); - print(' Max Contacts: ${deviceInfo['maxContacts']}'); - print(' Max Channels: ${deviceInfo['maxChannels']}'); - print(' BLE PIN: ${deviceInfo['blePin']}'); - print(' Build Date: ${deviceInfo['firmwareBuildDate']}'); - print(' Model: ${deviceInfo['manufacturerModel']}'); - print(' Version: ${deviceInfo['semanticVersion']}'); + debugPrint('๐Ÿ“ฅ [Provider] Received DeviceInfo:'); + debugPrint(' Firmware Version: ${deviceInfo['firmwareVersion']}'); + debugPrint(' Max Contacts: ${deviceInfo['maxContacts']}'); + debugPrint(' Max Channels: ${deviceInfo['maxChannels']}'); + debugPrint(' BLE PIN: ${deviceInfo['blePin']}'); + debugPrint(' Build Date: ${deviceInfo['firmwareBuildDate']}'); + debugPrint(' Model: ${deviceInfo['manufacturerModel']}'); + debugPrint(' Version: ${deviceInfo['semanticVersion']}'); _deviceInfo = _deviceInfo.copyWith( firmwareVersion: deviceInfo['firmwareVersion'] as int?, @@ -420,21 +420,21 @@ class ConnectionProvider with ChangeNotifier { semanticVersion: deviceInfo['semanticVersion'] as String?, ); notifyListeners(); - print('โœ… [Provider] Device info updated with DeviceInfo'); + debugPrint('โœ… [Provider] Device info updated with DeviceInfo'); }; _bleService.onSelfInfoReceived = (selfInfo) { - print('๐Ÿ“ฅ [Provider] Received SelfInfo:'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Received SelfInfo:'); + debugPrint( ' TX Power: ${selfInfo['txPower']} / ${selfInfo['maxTxPower']} dBm', ); - print( + debugPrint( ' Radio: freq=${selfInfo['radioFreq']}, bw=${selfInfo['radioBw']}, sf=${selfInfo['radioSf']}, cr=${selfInfo['radioCr']}', ); - print( + debugPrint( ' Position: ${selfInfo['advLat'] / 1000000.0}, ${selfInfo['advLon'] / 1000000.0}', ); - print(' Self Name: ${selfInfo['selfName']}'); + debugPrint(' Self Name: ${selfInfo['selfName']}'); _deviceInfo = _deviceInfo.copyWith( deviceType: selfInfo['deviceType'] as int?, @@ -451,24 +451,24 @@ class ConnectionProvider with ChangeNotifier { selfName: selfInfo['selfName'] as String?, ); notifyListeners(); - print('โœ… [Provider] Device info updated with SelfInfo'); + debugPrint('โœ… [Provider] Device info updated with SelfInfo'); }; // Activity indicators _bleService.onBatteryAndStorage = (millivolts, usedKb, totalKb) { - print('๐Ÿ“ฅ [Provider] Received BatteryAndStorage:'); - print( + debugPrint('๐Ÿ“ฅ [Provider] Received BatteryAndStorage:'); + debugPrint( ' Battery: ${millivolts}mV (${(millivolts / 1000.0).toStringAsFixed(2)}V)', ); if (usedKb != null) { - print(' Storage Used: ${usedKb}KB'); + debugPrint(' Storage Used: ${usedKb}KB'); } if (totalKb != null) { - print(' Storage Total: ${totalKb}KB'); + debugPrint(' Storage Total: ${totalKb}KB'); if (totalKb > 0 && usedKb != null) { final usedPercent = (usedKb / totalKb) * 100.0; - print(' Storage Usage: ${usedPercent.toStringAsFixed(1)}%'); + debugPrint(' Storage Usage: ${usedPercent.toStringAsFixed(1)}%'); } } @@ -479,7 +479,7 @@ class ConnectionProvider with ChangeNotifier { lastUpdate: DateTime.now(), ); notifyListeners(); - print('โœ… [Provider] Device info updated with BatteryAndStorage'); + debugPrint('โœ… [Provider] Device info updated with BatteryAndStorage'); }; _bleService.onRxActivity = () { _rxActivity = true; @@ -516,24 +516,24 @@ class ConnectionProvider with ChangeNotifier { /// Start scanning for MeshCore devices Future startScan() async { - print('๐Ÿ” [Provider] startScan() called'); + debugPrint('๐Ÿ” [Provider] startScan() called'); _isScanning = true; _scannedDevices.clear(); _error = null; notifyListeners(); - print('โœ… [Provider] Scan state initialized, notifying listeners'); + debugPrint('โœ… [Provider] Scan state initialized, notifying listeners'); try { await for (final scanResult in _bleService.scanForDevices( timeout: const Duration(seconds: 10), )) { - print('๐Ÿ“ฑ [Provider] Scan result received from scan stream'); + debugPrint('๐Ÿ“ฑ [Provider] Scan result received from scan stream'); final device = scanResult.device; final rssi = scanResult.rssi; if (!_scannedDevices.any((d) => d.device.remoteId == device.remoteId)) { _scannedDevices.add(ScannedDevice(device: device, rssi: rssi)); - print( + debugPrint( 'โœ… [Provider] Added device to list: ${device.platformName} (RSSI: $rssi dBm), total: ${_scannedDevices.length}', ); notifyListeners(); @@ -544,22 +544,22 @@ class ConnectionProvider with ChangeNotifier { ); if (index != -1 && _scannedDevices[index].rssi != rssi) { _scannedDevices[index] = ScannedDevice(device: device, rssi: rssi); - print( + debugPrint( ' ๐Ÿ”„ [Provider] Updated RSSI for ${device.platformName}: $rssi dBm', ); notifyListeners(); } else { - print( + debugPrint( ' โญ๏ธ [Provider] Device already in list with same RSSI, skipping', ); } } } } catch (e) { - print('โŒ [Provider] Scan error: $e'); + debugPrint('โŒ [Provider] Scan error: $e'); _error = 'Scan error: $e'; } finally { - print('๐Ÿ [Provider] Scan completed'); + debugPrint('๐Ÿ [Provider] Scan completed'); _isScanning = false; notifyListeners(); } @@ -574,7 +574,7 @@ class ConnectionProvider with ChangeNotifier { /// Connect to a device Future connect(BluetoothDevice device) async { - print('๐Ÿ”ต [Provider] connect() called for device: ${device.platformName}'); + debugPrint('๐Ÿ”ต [Provider] connect() called for device: ${device.platformName}'); _deviceInfo = _deviceInfo.copyWith( deviceId: device.remoteId.toString(), @@ -584,16 +584,16 @@ class ConnectionProvider with ChangeNotifier { connectionState: ConnectionState.connecting, ); _error = null; - print('โœ… [Provider] Device info updated to connecting state'); + debugPrint('โœ… [Provider] Device info updated to connecting state'); notifyListeners(); - print('๐Ÿ”ต [Provider] Calling BLE service connect()...'); + debugPrint('๐Ÿ”ต [Provider] Calling BLE service connect()...'); final success = await _bleService.connect(device); if (success) { - print('โœ… [Provider] BLE service connect() returned success'); + debugPrint('โœ… [Provider] BLE service connect() returned success'); } else { - print('โŒ [Provider] BLE service connect() returned failure'); + debugPrint('โŒ [Provider] BLE service connect() returned failure'); _deviceInfo = _deviceInfo.copyWith( connectionState: ConnectionState.error, ); @@ -622,7 +622,7 @@ class ConnectionProvider with ChangeNotifier { /// Cancel ongoing reconnection attempts /// This is useful when the user wants to manually disconnect during reconnection void cancelReconnection() { - print('๐Ÿ”ด [Provider] User requested cancellation of reconnection'); + debugPrint('๐Ÿ”ด [Provider] User requested cancellation of reconnection'); disconnect(); } @@ -705,19 +705,19 @@ class ConnectionProvider with ChangeNotifier { // Log path status and retry info if (contact != null) { if (retryAttempt > 0) { - print('๐Ÿ”„ [ConnectionProvider] Sending message to ${contact.advName} (retry $retryAttempt/3)'); + debugPrint('๐Ÿ”„ [ConnectionProvider] Sending message to ${contact.advName} (retry $retryAttempt/3)'); } else { - print('๐Ÿ“ค [ConnectionProvider] Sending message to ${contact.advName}'); + debugPrint('๐Ÿ“ค [ConnectionProvider] Sending message to ${contact.advName}'); } - print(' Type: ${contact.type.displayName}'); - print(' Path status: ${contact.pathDescription}'); + debugPrint(' Type: ${contact.type.displayName}'); + debugPrint(' Path status: ${contact.pathDescription}'); if (contact.hasPath) { - print(' โœ… Using learned path (${contact.outPathLen} bytes)'); + debugPrint(' โœ… Using learned path (${contact.outPathLen} bytes)'); } else { - print(' โš ๏ธ No path available - will use flood mode'); + debugPrint(' โš ๏ธ No path available - will use flood mode'); } } else if (retryAttempt > 0) { - print('๐Ÿ”„ [ConnectionProvider] Sending message (retry $retryAttempt/3)'); + debugPrint('๐Ÿ”„ [ConnectionProvider] Sending message (retry $retryAttempt/3)'); } // Track pending operation for auto-recovery (if contact not found in radio) @@ -730,7 +730,7 @@ class ConnectionProvider with ChangeNotifier { contact: contact, retryAttempt: retryAttempt, ); - print(' ๐Ÿ“ Tracked pending operation for auto-recovery: $operationId'); + debugPrint(' ๐Ÿ“ Tracked pending operation for auto-recovery: $operationId'); } // IMPORTANT: Track pending message BEFORE sending to avoid race condition @@ -738,7 +738,7 @@ class ConnectionProvider with ChangeNotifier { // the callback will fire before we add the message ID to the queue. if (messageId != null) { _messageDeliveryTracker.trackPendingMessage(messageId); - print(' Added message ID to pending queue BEFORE sending: $messageId'); + debugPrint(' Added message ID to pending queue BEFORE sending: $messageId'); } // Send the message with retry attempt info @@ -796,9 +796,9 @@ class ConnectionProvider with ChangeNotifier { // Channel messages are ephemeral (not persisted) - mark as "sent" immediately // They don't have ACK/TAG mechanism like direct messages if (messageId != null) { - print('โœ… [ConnectionProvider] Channel message sent successfully'); - print(' Message ID: $messageId'); - print(' onMessageSent callback exists: ${onMessageSent != null}'); + debugPrint('โœ… [ConnectionProvider] Channel message sent successfully'); + debugPrint(' Message ID: $messageId'); + debugPrint(' onMessageSent callback exists: ${onMessageSent != null}'); // Track for echo detection // The BLE handler will capture the packet via LOG_RX_DATA and associate it @@ -812,9 +812,9 @@ class ConnectionProvider with ChangeNotifier { // Use a dummy ACK tag (0) and timeout (0) for channel messages // This will trigger the callback to mark the message as "sent" - print(' Calling onMessageSent callback...'); + debugPrint(' Calling onMessageSent callback...'); onMessageSent?.call(messageId, 0, 0); - print(' onMessageSent callback completed'); + debugPrint(' onMessageSent callback completed'); } } catch (e) { _error = 'Failed to send channel message: $e'; @@ -902,7 +902,7 @@ class ConnectionProvider with ChangeNotifier { // First attempt timed out - retry with flooding if first was direct if (firstAttemptDirect) { - print( + debugPrint( 'โš ๏ธ [Provider] Ping timeout on direct attempt, retrying with flooding...', ); onRetryWithFlooding?.call(); @@ -1253,8 +1253,8 @@ class ConnectionProvider with ChangeNotifier { try { _isSyncingMessages = true; - print('๐Ÿ”„ [Provider] Starting message sync loop...'); - print(' Initial _noMoreMessages state: $_noMoreMessages'); + debugPrint('๐Ÿ”„ [Provider] Starting message sync loop...'); + debugPrint(' Initial _noMoreMessages state: $_noMoreMessages'); // Keep syncing until we get NoMoreMessages response // The device will send ContactMsgRecv or ChannelMsgRecv responses @@ -1263,13 +1263,13 @@ class ConnectionProvider with ChangeNotifier { // Safety limit // Check flag BEFORE sending (not after) if (_noMoreMessages) { - print( + debugPrint( 'โœ… [Provider] Message sync complete - NoMoreMessages flag set after $count requests', ); break; } - print( + debugPrint( '๐Ÿ“ค [Provider] Sync iteration ${i + 1}: Sending CMD_SYNC_NEXT_MESSAGE', ); @@ -1290,21 +1290,21 @@ class ConnectionProvider with ChangeNotifier { // Small delay to allow response to be processed await Future.delayed(const Duration(milliseconds: 150)); - print(' After iteration ${i + 1}: _noMoreMessages=$_noMoreMessages'); + debugPrint(' After iteration ${i + 1}: _noMoreMessages=$_noMoreMessages'); } if (!_noMoreMessages && count >= 100) { - print( + debugPrint( 'โš ๏ธ [Provider] Message sync stopped - reached safety limit of 100 requests without NoMoreMessages', ); } - print( + debugPrint( '๐Ÿ [Provider] Message sync finished: sent $count sync requests, _noMoreMessages=$_noMoreMessages', ); return count; } catch (e) { - print('โŒ [Provider] Failed to sync messages: $e'); + debugPrint('โŒ [Provider] Failed to sync messages: $e'); _error = 'Failed to sync messages: $e'; notifyListeners(); return count; @@ -1321,10 +1321,10 @@ class ConnectionProvider with ChangeNotifier { /// Example usage: /// ```dart /// connectionProvider.onLoginSuccess = (pkPrefix, perms, isAdmin, tag) { - /// print('Successfully logged in to room!'); + /// debugPrint('Successfully logged in to room!'); /// }; /// connectionProvider.onLoginFail = (pkPrefix) { - /// print('Login failed - incorrect password'); + /// debugPrint('Login failed - incorrect password'); /// }; /// await connectionProvider.loginToRoom( /// roomPublicKey: contact.publicKey, @@ -1374,7 +1374,7 @@ class ConnectionProvider with ChangeNotifier { /// Example usage: /// ```dart /// connectionProvider.onStatusResponse = (publicKeyPrefix, statusData) { - /// print('Status from node: ${utf8.decode(statusData)}'); + /// debugPrint('Status from node: ${utf8.decode(statusData)}'); /// }; /// await connectionProvider.requestStatus(repeaterContact.publicKey); /// ``` diff --git a/lib/providers/contacts_provider.dart b/lib/providers/contacts_provider.dart index 6add3a9..13a7a0e 100644 --- a/lib/providers/contacts_provider.dart +++ b/lib/providers/contacts_provider.dart @@ -23,7 +23,7 @@ class ContactsProvider with ChangeNotifier { if (_isInitialized) return; try { - print('๐Ÿ“ฆ [ContactsProvider] Loading persisted contacts...'); + debugPrint('๐Ÿ“ฆ [ContactsProvider] Loading persisted contacts...'); final storedContacts = await _storageService.loadContacts( excludePublicKey: devicePublicKey, ); @@ -39,14 +39,14 @@ class ContactsProvider with ChangeNotifier { } _isInitialized = true; - print('โœ… [ContactsProvider] Loaded ${storedContacts.length} persisted contacts'); + debugPrint('โœ… [ContactsProvider] Loaded ${storedContacts.length} persisted contacts'); // Ensure public channel exists after loading _ensurePublicChannelExists(); notifyListeners(); } catch (e) { - print('โŒ [ContactsProvider] Error initializing: $e'); + debugPrint('โŒ [ContactsProvider] Error initializing: $e'); _isInitialized = true; // Mark as initialized even on error _ensurePublicChannelExists(); } @@ -84,7 +84,7 @@ class ContactsProvider with ChangeNotifier { .toList(); await _storageService.saveContacts(contactsToSave); } catch (e) { - print('โŒ [ContactsProvider] Error persisting contacts: $e'); + debugPrint('โŒ [ContactsProvider] Error persisting contacts: $e'); } } @@ -129,7 +129,7 @@ class ContactsProvider with ChangeNotifier { void addOrUpdateContact(Contact contact, {Uint8List? devicePublicKey}) { // Don't add contacts that match our device's public key if (devicePublicKey != null && _publicKeysMatch(contact.publicKey, devicePublicKey)) { - print('โ„น๏ธ [ContactsProvider] Ignoring contact with device\'s own public key: ${contact.advName}'); + debugPrint('โ„น๏ธ [ContactsProvider] Ignoring contact with device\'s own public key: ${contact.advName}'); return; } @@ -182,14 +182,14 @@ class ContactsProvider with ChangeNotifier { for (final contact in contacts) { // Don't add contacts that match our device's public key if (devicePublicKey != null && _publicKeysMatch(contact.publicKey, devicePublicKey)) { - print('โ„น๏ธ [ContactsProvider] Ignoring contact with device\'s own public key: ${contact.advName}'); + debugPrint('โ„น๏ธ [ContactsProvider] Ignoring contact with device\'s own public key: ${contact.advName}'); excluded++; continue; } _contacts[contact.publicKeyHex] = contact; } if (excluded > 0) { - print('โ„น๏ธ [ContactsProvider] Excluded $excluded contact(s) matching device public key'); + debugPrint('โ„น๏ธ [ContactsProvider] Excluded $excluded contact(s) matching device public key'); } _persistContacts(); notifyListeners(); @@ -197,38 +197,38 @@ class ContactsProvider with ChangeNotifier { /// Update contact telemetry void updateTelemetry(Uint8List publicKeyPrefix, Uint8List lppData) { - print('๐Ÿ“Š [ContactsProvider] updateTelemetry() called'); - print(' Public key prefix (hex): ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); - print(' LPP data size: ${lppData.length} bytes'); + debugPrint('๐Ÿ“Š [ContactsProvider] updateTelemetry() called'); + debugPrint(' Public key prefix (hex): ${publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint(' LPP data size: ${lppData.length} bytes'); // Find contact by public key prefix final contact = _findContactByPrefix(publicKeyPrefix); if (contact == null) { - print(' โŒ Contact not found for this prefix'); + debugPrint(' โŒ Contact not found for this prefix'); return; } - print(' โœ… Found contact: ${contact.advName}'); - print(' Old telemetry timestamp: ${contact.telemetry?.timestamp}'); + debugPrint(' โœ… Found contact: ${contact.advName}'); + debugPrint(' Old telemetry timestamp: ${contact.telemetry?.timestamp}'); try { // Parse Cayenne LPP data final telemetry = CayenneLppParser.parse(lppData); - print(' โœ… Parsed new telemetry'); - print(' New telemetry timestamp: ${telemetry.timestamp}'); + debugPrint(' โœ… Parsed new telemetry'); + debugPrint(' New telemetry timestamp: ${telemetry.timestamp}'); // Update contact with new telemetry final updatedContact = contact.copyWith(telemetry: telemetry); _contacts[contact.publicKeyHex] = updatedContact; - print(' โœ… Updated contact in map'); + debugPrint(' โœ… Updated contact in map'); _persistContacts(); - print(' โœ… Persisted contacts to storage'); + debugPrint(' โœ… Persisted contacts to storage'); notifyListeners(); - print(' โœ… Notified listeners - UI should update'); + debugPrint(' โœ… Notified listeners - UI should update'); } catch (e) { - print(' โŒ Failed to parse telemetry: $e'); + debugPrint(' โŒ Failed to parse telemetry: $e'); debugPrint('Failed to parse telemetry: $e'); } } diff --git a/lib/providers/messages_provider.dart b/lib/providers/messages_provider.dart index 514bd15..cf6c21f 100644 --- a/lib/providers/messages_provider.dart +++ b/lib/providers/messages_provider.dart @@ -88,7 +88,7 @@ class MessagesProvider with ChangeNotifier { if (_isInitialized) return; try { - print('๐Ÿ“ฆ [MessagesProvider] Loading persisted messages...'); + debugPrint('๐Ÿ“ฆ [MessagesProvider] Loading persisted messages...'); final storedMessages = await _storageService.loadMessages(); // Add stored messages with enhancement to ensure SAR detection @@ -108,10 +108,10 @@ class MessagesProvider with ChangeNotifier { } _isInitialized = true; - print('โœ… [MessagesProvider] Loaded ${storedMessages.length} persisted messages'); + debugPrint('โœ… [MessagesProvider] Loaded ${storedMessages.length} persisted messages'); notifyListeners(); } catch (e) { - print('โŒ [MessagesProvider] Error initializing: $e'); + debugPrint('โŒ [MessagesProvider] Error initializing: $e'); _isInitialized = true; // Mark as initialized even on error } } @@ -149,9 +149,9 @@ class MessagesProvider with ChangeNotifier { // Debug: Check if message is SAR if (message.text.startsWith('S:')) { - print('๐Ÿ” [MessagesProvider] Processing SAR message: ${message.text}'); - print(' isSarMarker: ${finalMessage.isSarMarker}'); - print(' sarMarkerType: ${finalMessage.sarMarkerType}'); + debugPrint('๐Ÿ” [MessagesProvider] Processing SAR message: ${message.text}'); + debugPrint(' isSarMarker: ${finalMessage.isSarMarker}'); + debugPrint(' sarMarkerType: ${finalMessage.sarMarkerType}'); } // Check for duplicates before adding @@ -160,8 +160,8 @@ class MessagesProvider with ChangeNotifier { // - Multiple paths in the network // - Syncing messages from device queue if (_isDuplicate(finalMessage)) { - print('โš ๏ธ [MessagesProvider] Duplicate message detected, skipping: ${finalMessage.id}'); - print(' Text: ${finalMessage.text.substring(0, finalMessage.text.length > 50 ? 50 : finalMessage.text.length)}...'); + debugPrint('โš ๏ธ [MessagesProvider] Duplicate message detected, skipping: ${finalMessage.id}'); + debugPrint(' Text: ${finalMessage.text.substring(0, finalMessage.text.length > 50 ? 50 : finalMessage.text.length)}...'); return; // Skip duplicate } @@ -263,7 +263,7 @@ class MessagesProvider with ChangeNotifier { } } - print('๐Ÿ“ฅ [MessagesProvider] Added $addedCount messages, skipped $duplicateCount duplicates'); + debugPrint('๐Ÿ“ฅ [MessagesProvider] Added $addedCount messages, skipped $duplicateCount duplicates'); // Persist to storage asynchronously _persistMessages(); @@ -280,9 +280,9 @@ class MessagesProvider with ChangeNotifier { // Get sender name from message final senderName = message.senderName ?? message.senderKeyShort ?? 'Unknown'; - print('๐Ÿ”” [MessagesProvider] Triggering SAR notification for ${marker.type.displayName}'); - print(' Sender: $senderName'); - print(' Coordinates: $coords'); + debugPrint('๐Ÿ”” [MessagesProvider] Triggering SAR notification for ${marker.type.displayName}'); + debugPrint(' Sender: $senderName'); + debugPrint(' Coordinates: $coords'); await _notificationService.showSarNotification( type: marker.type, @@ -292,7 +292,7 @@ class MessagesProvider with ChangeNotifier { localizations: _localizations, ); } catch (e) { - print('โŒ [MessagesProvider] Error triggering SAR notification: $e'); + debugPrint('โŒ [MessagesProvider] Error triggering SAR notification: $e'); } } @@ -301,7 +301,7 @@ class MessagesProvider with ChangeNotifier { try { await _storageService.saveMessages(_messages); } catch (e) { - print('โŒ [MessagesProvider] Error persisting messages: $e'); + debugPrint('โŒ [MessagesProvider] Error persisting messages: $e'); } } @@ -409,7 +409,7 @@ class MessagesProvider with ChangeNotifier { _pendingSentMessages.remove(message.expectedAckTag); } - print('๐Ÿ—‘๏ธ [MessagesProvider] Message $messageId deleted'); + debugPrint('๐Ÿ—‘๏ธ [MessagesProvider] Message $messageId deleted'); _persistMessages(); notifyListeners(); @@ -495,18 +495,18 @@ class MessagesProvider with ChangeNotifier { /// Add a sent message with initial status void addSentMessage(Message message, {Contact? contact}) { - print('๐Ÿ“ [MessagesProvider] addSentMessage called'); - print(' Message ID: ${message.id}'); - print(' Message type: ${message.messageType}'); - print(' Initial status: ${message.deliveryStatus}'); - print(' Message text preview: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); + debugPrint('๐Ÿ“ [MessagesProvider] addSentMessage called'); + debugPrint(' Message ID: ${message.id}'); + debugPrint(' Message type: ${message.messageType}'); + debugPrint(' Initial status: ${message.deliveryStatus}'); + debugPrint(' Message text preview: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); // Always enhance message with SAR parser to detect SAR markers final enhancedMessage = SarMessageParser.enhanceMessage(message); // Check for duplicates (shouldn't happen for sent messages, but be safe) if (_isDuplicate(enhancedMessage)) { - print('โš ๏ธ [MessagesProvider] Duplicate sent message detected, skipping: ${enhancedMessage.id}'); + debugPrint('โš ๏ธ [MessagesProvider] Duplicate sent message detected, skipping: ${enhancedMessage.id}'); return; } @@ -516,13 +516,13 @@ class MessagesProvider with ChangeNotifier { isRead: true, // Sent messages are always marked as read ); _messages.add(sendingMessage); - print(' โœ… Message added to list at index ${_messages.length - 1}'); - print(' Total messages in list: ${_messages.length}'); + debugPrint(' โœ… Message added to list at index ${_messages.length - 1}'); + debugPrint(' Total messages in list: ${_messages.length}'); // Store contact mapping for retry logic if (contact != null) { _messageContactMap[message.id] = contact; - print(' โœ… Stored contact mapping for retry logic'); + debugPrint(' โœ… Stored contact mapping for retry logic'); } // If it's a SAR marker message, extract and store the marker @@ -535,25 +535,25 @@ class MessagesProvider with ChangeNotifier { _persistMessages(); notifyListeners(); - print(' โœ… notifyListeners() called - UI should update'); + debugPrint(' โœ… notifyListeners() called - UI should update'); } /// Update message status to sent with ACK tag void markMessageSent(String messageId, int expectedAckTag, int suggestedTimeoutMs) { - print('๐Ÿ“ค [MessagesProvider] markMessageSent called'); - print(' Message ID: $messageId'); - print(' Expected ACK tag: $expectedAckTag (0x${expectedAckTag.toRadixString(16).padLeft(8, '0')})'); - print(' Timeout: ${suggestedTimeoutMs}ms'); - print(' Current pending ACKs before adding: ${_pendingSentMessages.keys.toList()}'); + debugPrint('๐Ÿ“ค [MessagesProvider] markMessageSent called'); + debugPrint(' Message ID: $messageId'); + debugPrint(' Expected ACK tag: $expectedAckTag (0x${expectedAckTag.toRadixString(16).padLeft(8, '0')})'); + debugPrint(' Timeout: ${suggestedTimeoutMs}ms'); + debugPrint(' Current pending ACKs before adding: ${_pendingSentMessages.keys.toList()}'); final index = _messages.indexWhere((m) => m.id == messageId); - print(' Message index in list: $index'); + debugPrint(' Message index in list: $index'); if (index != -1) { final message = _messages[index]; - print(' Current status: ${message.deliveryStatus}'); - print(' Message type: ${message.messageType}'); - print(' Message text preview: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); + debugPrint(' Current status: ${message.deliveryStatus}'); + debugPrint(' Message type: ${message.messageType}'); + debugPrint(' Message text preview: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); final updatedMessage = message.copyWith( deliveryStatus: MessageDeliveryStatus.sent, @@ -566,55 +566,55 @@ class MessagesProvider with ChangeNotifier { if (expectedAckTag > 0 && suggestedTimeoutMs > 0) { // Track by ACK tag for matching with delivery confirmation _pendingSentMessages[expectedAckTag] = updatedMessage; - print(' โœ… Added to pending messages map with ACK: $expectedAckTag'); - print(' Total pending messages: ${_pendingSentMessages.length}'); - print(' Pending ACKs after adding: ${_pendingSentMessages.keys.toList()}'); + debugPrint(' โœ… Added to pending messages map with ACK: $expectedAckTag'); + debugPrint(' Total pending messages: ${_pendingSentMessages.length}'); + debugPrint(' Pending ACKs after adding: ${_pendingSentMessages.keys.toList()}'); // Start timeout timer _timeoutTimers[expectedAckTag] = Timer( Duration(milliseconds: suggestedTimeoutMs), () { - print('โฑ๏ธ [MessagesProvider] Timeout for message $messageId (ACK $expectedAckTag)'); + debugPrint('โฑ๏ธ [MessagesProvider] Timeout for message $messageId (ACK $expectedAckTag)'); if (_pendingSentMessages.containsKey(expectedAckTag)) { markMessageFailed(messageId); } }, ); - print('โฑ๏ธ [MessagesProvider] Started ${suggestedTimeoutMs}ms timeout timer for message $messageId (ACK $expectedAckTag)'); + debugPrint('โฑ๏ธ [MessagesProvider] Started ${suggestedTimeoutMs}ms timeout timer for message $messageId (ACK $expectedAckTag)'); } else { - print(' โ„น๏ธ Channel message (no ACK tracking) - marked as sent immediately'); + debugPrint(' โ„น๏ธ Channel message (no ACK tracking) - marked as sent immediately'); } - print(' Calling notifyListeners() to update UI with "sent" status'); + debugPrint(' Calling notifyListeners() to update UI with "sent" status'); _persistMessages(); notifyListeners(); - print(' โœ… markMessageSent completed successfully'); + debugPrint(' โœ… markMessageSent completed successfully'); } else { - print('โš ๏ธ [MessagesProvider] Message not found in list: $messageId'); - print(' Total messages in list: ${_messages.length}'); - print(' Recent messages:'); + debugPrint('โš ๏ธ [MessagesProvider] Message not found in list: $messageId'); + debugPrint(' Total messages in list: ${_messages.length}'); + debugPrint(' Recent messages:'); for (final m in _messages.take(5)) { - print(' - ID: ${m.id}, Status: ${m.deliveryStatus}'); + debugPrint(' - ID: ${m.id}, Status: ${m.deliveryStatus}'); } } } /// Handle echo detection for public channel messages void handleMessageEcho(String messageId, int echoCount, int snrRaw, int rssiDbm) { - print('๐Ÿ”Š [MessagesProvider] handleMessageEcho called'); - print(' Message ID: $messageId'); - print(' Echo count: $echoCount'); - print(' SNR: ${(snrRaw.toSigned(8) / 4.0).toStringAsFixed(2)} dB'); - print(' RSSI: ${rssiDbm.toSigned(8)} dBm'); + debugPrint('๐Ÿ”Š [MessagesProvider] handleMessageEcho called'); + debugPrint(' Message ID: $messageId'); + debugPrint(' Echo count: $echoCount'); + debugPrint(' SNR: ${(snrRaw.toSigned(8) / 4.0).toStringAsFixed(2)} dB'); + debugPrint(' RSSI: ${rssiDbm.toSigned(8)} dBm'); // Find the message final index = _messages.indexWhere((m) => m.id == messageId); if (index != -1) { final message = _messages[index]; - print(' โœ… Found message: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); + debugPrint(' โœ… Found message: ${message.text.substring(0, message.text.length > 30 ? 30 : message.text.length)}...'); // Update echo count final updatedMessage = message.copyWith( @@ -623,28 +623,28 @@ class MessagesProvider with ChangeNotifier { ); _messages[index] = updatedMessage; - print(' Updated echo count to: $echoCount'); + debugPrint(' Updated echo count to: $echoCount'); _persistMessages(); notifyListeners(); - print(' โœ… Echo update complete, UI notified'); + debugPrint(' โœ… Echo update complete, UI notified'); } else { - print(' โš ๏ธ Message not found in messages list'); + debugPrint(' โš ๏ธ Message not found in messages list'); } } /// Update message status to delivered with RTT void markMessageDelivered(int ackCode, int roundTripTimeMs) { - print('๐Ÿ” [MessagesProvider] markMessageDelivered called with ACK: $ackCode, RTT: ${roundTripTimeMs}ms'); - print(' Current pending messages: ${_pendingSentMessages.keys.toList()}'); - print(' Total messages in list: ${_messages.length}'); - print(' Looking for ACK: $ackCode'); + debugPrint('๐Ÿ” [MessagesProvider] markMessageDelivered called with ACK: $ackCode, RTT: ${roundTripTimeMs}ms'); + debugPrint(' Current pending messages: ${_pendingSentMessages.keys.toList()}'); + debugPrint(' Total messages in list: ${_messages.length}'); + debugPrint(' Looking for ACK: $ackCode'); // Find message by ACK code final message = _pendingSentMessages[ackCode]; if (message != null) { - print(' โœ… Found message in pending map: ${message.id}'); + debugPrint(' โœ… Found message in pending map: ${message.id}'); final index = _messages.indexWhere((m) => m.id == message.id); - print(' Message index in list: $index'); + debugPrint(' Message index in list: $index'); if (index != -1) { final updatedMessage = message.copyWith( @@ -664,42 +664,42 @@ class MessagesProvider with ChangeNotifier { // Clear retry tracking on successful delivery _retryManager.clearRetry(message.id); - print('โœ… [MessagesProvider] Message ${message.id} delivered in ${roundTripTimeMs}ms (ACK $ackCode)'); - print(' Updated status to: ${updatedMessage.deliveryStatus}'); - print(' Calling notifyListeners() to update UI'); + debugPrint('โœ… [MessagesProvider] Message ${message.id} delivered in ${roundTripTimeMs}ms (ACK $ackCode)'); + debugPrint(' Updated status to: ${updatedMessage.deliveryStatus}'); + debugPrint(' Calling notifyListeners() to update UI'); _persistMessages(); notifyListeners(); - print(' โœ… notifyListeners() called successfully'); + debugPrint(' โœ… notifyListeners() called successfully'); } else { - print('โš ๏ธ [MessagesProvider] Message not found in messages list (index=-1)'); - print(' This should never happen - message was in pending map but not in messages list'); + debugPrint('โš ๏ธ [MessagesProvider] Message not found in messages list (index=-1)'); + debugPrint(' This should never happen - message was in pending map but not in messages list'); } } else { - print('โš ๏ธ [MessagesProvider] No pending message found for ACK code: $ackCode'); - print(' Pending ACK codes: ${_pendingSentMessages.keys.toList()}'); - print(' This means either:'); - print(' 1. markMessageSent() was never called for this message (ACK tag not stored)'); - print(' 2. The ACK code from PUSH_CODE_SEND_CONFIRMED doesn\'t match the expected ACK tag from RESP_CODE_SENT'); - print(' 3. The message was already delivered or timed out'); - print(' Searching all messages for debugging...'); + debugPrint('โš ๏ธ [MessagesProvider] No pending message found for ACK code: $ackCode'); + debugPrint(' Pending ACK codes: ${_pendingSentMessages.keys.toList()}'); + debugPrint(' This means either:'); + debugPrint(' 1. markMessageSent() was never called for this message (ACK tag not stored)'); + debugPrint(' 2. The ACK code from PUSH_CODE_SEND_CONFIRMED doesn\'t match the expected ACK tag from RESP_CODE_SENT'); + debugPrint(' 3. The message was already delivered or timed out'); + debugPrint(' Searching all messages for debugging...'); // Debug: Search for any message with this ACK tag final matchingMessages = _messages.where((m) => m.expectedAckTag == ackCode).toList(); if (matchingMessages.isNotEmpty) { - print(' โš ๏ธ Found ${matchingMessages.length} message(s) with matching ACK tag but NOT in pending map:'); + debugPrint(' โš ๏ธ Found ${matchingMessages.length} message(s) with matching ACK tag but NOT in pending map:'); for (final m in matchingMessages) { - print(' - Message ID: ${m.id}, Status: ${m.deliveryStatus}, ACK: ${m.expectedAckTag}'); + debugPrint(' - Message ID: ${m.id}, Status: ${m.deliveryStatus}, ACK: ${m.expectedAckTag}'); } - print(' This indicates the message was sent but never added to _pendingSentMessages map'); - print(' Likely cause: markMessageSent() was not called with correct message ID'); + debugPrint(' This indicates the message was sent but never added to _pendingSentMessages map'); + debugPrint(' Likely cause: markMessageSent() was not called with correct message ID'); } else { - print(' No messages found with ACK tag $ackCode'); - print(' Recent sent messages:'); + debugPrint(' No messages found with ACK tag $ackCode'); + debugPrint(' Recent sent messages:'); final sentMessages = _messages.where((m) => m.isSentMessage).take(5).toList(); for (final m in sentMessages) { - print(' - ID: ${m.id}, Status: ${m.deliveryStatus}, ACK: ${m.expectedAckTag}'); + debugPrint(' - ID: ${m.id}, Status: ${m.deliveryStatus}, ACK: ${m.expectedAckTag}'); } } } @@ -709,17 +709,17 @@ class MessagesProvider with ChangeNotifier { void markMessageFailed(String messageId) { final index = _messages.indexWhere((m) => m.id == messageId); if (index == -1) { - print('โš ๏ธ [MessagesProvider] markMessageFailed: Message not found: $messageId'); + debugPrint('โš ๏ธ [MessagesProvider] markMessageFailed: Message not found: $messageId'); return; } final message = _messages[index]; final contact = _messageContactMap[messageId]; - print('โŒ [MessagesProvider] Message $messageId timeout/failed'); - print(' Retry attempt: ${message.retryAttempt}'); - print(' Contact has path: ${contact?.hasPath ?? false}'); - print(' Used flood fallback: ${message.usedFloodFallback}'); + debugPrint('โŒ [MessagesProvider] Message $messageId timeout/failed'); + debugPrint(' Retry attempt: ${message.retryAttempt}'); + debugPrint(' Contact has path: ${contact?.hasPath ?? false}'); + debugPrint(' Used flood fallback: ${message.usedFloodFallback}'); // Decision tree for retry/flood/fail if (contact != null && _retryManager.canRetry(message, contact)) { @@ -739,8 +739,8 @@ class MessagesProvider with ChangeNotifier { final nextAttempt = message.retryAttempt + 1; final timeout = _retryManager.getTimeoutForAttempt(message.retryAttempt); - print('๐Ÿ”„ [MessagesProvider] Scheduling retry $nextAttempt/3 for message $messageId'); - print(' Timeout: ${timeout}ms'); + debugPrint('๐Ÿ”„ [MessagesProvider] Scheduling retry $nextAttempt/3 for message $messageId'); + debugPrint(' Timeout: ${timeout}ms'); // Update message with new retry attempt final index = _messages.indexWhere((m) => m.id == messageId); @@ -765,7 +765,7 @@ class MessagesProvider with ChangeNotifier { // Schedule actual retry after delay Timer(Duration(milliseconds: timeout), () async { - print('โฐ [MessagesProvider] Executing retry $nextAttempt for message $messageId'); + debugPrint('โฐ [MessagesProvider] Executing retry $nextAttempt for message $messageId'); if (sendMessageCallback != null) { await sendMessageCallback!( contactPublicKey: contact.publicKey, @@ -775,7 +775,7 @@ class MessagesProvider with ChangeNotifier { retryAttempt: nextAttempt, ); } else { - print('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot retry'); + debugPrint('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot retry'); } }); @@ -785,7 +785,7 @@ class MessagesProvider with ChangeNotifier { /// Send message with flood mode as last resort Future _sendWithFloodMode(String messageId, Message message, Contact contact) async { - print('๐ŸŒŠ [MessagesProvider] Trying flood mode for message $messageId'); + debugPrint('๐ŸŒŠ [MessagesProvider] Trying flood mode for message $messageId'); final index = _messages.indexWhere((m) => m.id == messageId); if (index != -1) { @@ -813,7 +813,7 @@ class MessagesProvider with ChangeNotifier { retryAttempt: 0, // Reset attempt for flood ); } else { - print('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot send flood'); + debugPrint('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot send flood'); } _persistMessages(); @@ -822,7 +822,7 @@ class MessagesProvider with ChangeNotifier { /// Mark message as permanently failed void _markAsPermanentlyFailed(String messageId, Message message) { - print('โŒ [MessagesProvider] Message $messageId permanently failed'); + debugPrint('โŒ [MessagesProvider] Message $messageId permanently failed'); final index = _messages.indexWhere((m) => m.id == messageId); if (index != -1) { @@ -849,7 +849,7 @@ class MessagesProvider with ChangeNotifier { Future resendMessage(String messageId) async { final index = _messages.indexWhere((m) => m.id == messageId); if (index == -1) { - print('โš ๏ธ [MessagesProvider] resendMessage: Message not found: $messageId'); + debugPrint('โš ๏ธ [MessagesProvider] resendMessage: Message not found: $messageId'); return; } @@ -857,11 +857,11 @@ class MessagesProvider with ChangeNotifier { final contact = _messageContactMap[messageId]; if (contact == null) { - print('โš ๏ธ [MessagesProvider] Cannot resend: Contact not found for message $messageId'); + debugPrint('โš ๏ธ [MessagesProvider] Cannot resend: Contact not found for message $messageId'); return; } - print('๐Ÿ” [MessagesProvider] Resending message $messageId'); + debugPrint('๐Ÿ” [MessagesProvider] Resending message $messageId'); // Reset retry state _messages[index] = message.copyWith( @@ -886,7 +886,7 @@ class MessagesProvider with ChangeNotifier { retryAttempt: 0, ); } else { - print('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot resend'); + debugPrint('โš ๏ธ [MessagesProvider] sendMessageCallback not set, cannot resend'); } _persistMessages(); diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart index 37758c7..7db40b2 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home_screen.dart @@ -174,7 +174,7 @@ class _HomeScreenState extends State ), ).timeout(const Duration(seconds: 5)); } catch (e) { - print('โŒ Failed to get GPS position: $e'); + debugPrint('โŒ Failed to get GPS position: $e'); if (context.mounted) { ToastLogger.error( context, @@ -206,7 +206,7 @@ class _HomeScreenState extends State ); } } catch (e) { - print('โŒ Failed to advertise device: $e'); + debugPrint('โŒ Failed to advertise device: $e'); if (context.mounted) { ToastLogger.error( context, @@ -442,38 +442,38 @@ class _HomeScreenState extends State ).colorScheme.onSurfaceVariant, ), onTap: () async { - print( + debugPrint( '๐Ÿ”ต [UI] User tapped device: ${device.platformName}', ); // Get app provider reference before popping dialog final appProvider = context.read(); - print('๐Ÿ”ต [UI] Closing dialog...'); + debugPrint('๐Ÿ”ต [UI] Closing dialog...'); Navigator.pop(context); - print('๐Ÿ”ต [UI] Calling provider.connect()...'); + debugPrint('๐Ÿ”ต [UI] Calling provider.connect()...'); final success = await provider.connect(device); - print( + debugPrint( success ? 'โœ… [UI] provider.connect() returned success' : 'โŒ [UI] provider.connect() returned failure', ); if (success && provider.deviceInfo.isConnected) { - print( + debugPrint( 'โœ… [UI] Device is connected, initializing app provider...', ); await appProvider.initialize(); - print('โœ… [UI] App provider initialized'); + debugPrint('โœ… [UI] App provider initialized'); } else { - print( + debugPrint( 'โŒ [UI] Device not connected after connect() call', ); - print( + debugPrint( ' Connection state: ${provider.deviceInfo.connectionState}', ); - print(' Error: ${provider.error}'); + debugPrint(' Error: ${provider.error}'); } }, ), @@ -824,7 +824,7 @@ class _HomeScreenState extends State } } catch (e) { // Fallback if anything fails - print('Haptic feedback error: $e'); + debugPrint('Haptic feedback error: $e'); await HapticFeedback.vibrate(); } _advertiseDevice(context); diff --git a/lib/screens/map_management_screen.dart b/lib/screens/map_management_screen.dart index 5f50bc9..c22fed7 100644 --- a/lib/screens/map_management_screen.dart +++ b/lib/screens/map_management_screen.dart @@ -277,7 +277,7 @@ class _MapManagementScreenState extends State { minZoom: _minZoom, maxZoom: _maxZoom, onProgress: (progress) { - print('UI received progress update: $progress%'); + debugPrint('UI received progress update: $progress%'); if (!mounted) return; setState(() { _downloadProgress = progress; diff --git a/lib/screens/messages_tab.dart b/lib/screens/messages_tab.dart index 2d5f16b..02a7e8a 100644 --- a/lib/screens/messages_tab.dart +++ b/lib/screens/messages_tab.dart @@ -254,7 +254,7 @@ class _MessagesTabState extends State { } try { - print('๐Ÿ”„ [MessagesTab] Manual refresh triggered - syncing messages'); + debugPrint('๐Ÿ”„ [MessagesTab] Manual refresh triggered - syncing messages'); final messageCount = await connectionProvider.syncAllMessages(); if (!mounted) return; if (messageCount > 0) { @@ -263,7 +263,7 @@ class _MessagesTabState extends State { ToastLogger.info(context, 'No new messages'); } } catch (e) { - print('โŒ [MessagesTab] Sync error: $e'); + debugPrint('โŒ [MessagesTab] Sync error: $e'); if (!mounted) return; ToastLogger.error(context, 'Sync failed: $e'); } diff --git a/lib/services/background_location_service.dart b/lib/services/background_location_service.dart index 3982a28..a4c46f4 100644 --- a/lib/services/background_location_service.dart +++ b/lib/services/background_location_service.dart @@ -32,12 +32,12 @@ class BackgroundLocationService { /// additional platform-specific configuration is required. Future startTracking({double distanceThreshold = 10.0}) async { if (!_isInitialized || _bleService == null) { - print('โš ๏ธ [BackgroundLocation] Service not initialized or BLE service null'); + debugPrint('โš ๏ธ [BackgroundLocation] Service not initialized or BLE service null'); return false; } if (!_bleService!.isConnected) { - print('โš ๏ธ [BackgroundLocation] BLE not connected'); + debugPrint('โš ๏ธ [BackgroundLocation] BLE not connected'); return false; } @@ -46,13 +46,13 @@ class BackgroundLocationService { if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { - print('โš ๏ธ [BackgroundLocation] Location permission denied'); + debugPrint('โš ๏ธ [BackgroundLocation] Location permission denied'); return false; } } if (permission == LocationPermission.deniedForever) { - print('โš ๏ธ [BackgroundLocation] Location permission permanently denied'); + debugPrint('โš ๏ธ [BackgroundLocation] Location permission permanently denied'); return false; } @@ -70,7 +70,7 @@ class BackgroundLocationService { distanceFilter: distanceThreshold.toInt(), ), ).listen((Position position) async { - print('๐Ÿ“ [BackgroundLocation] New position: ${position.latitude}, ${position.longitude}'); + debugPrint('๐Ÿ“ [BackgroundLocation] New position: ${position.latitude}, ${position.longitude}'); // Calculate distance from last position if (lastPosition != null) { @@ -81,7 +81,7 @@ class BackgroundLocationService { position.longitude, ); - print(' Distance moved: ${distance.toStringAsFixed(1)}m (threshold: ${distanceThreshold}m)'); + debugPrint(' Distance moved: ${distance.toStringAsFixed(1)}m (threshold: ${distanceThreshold}m)'); // Skip if haven't moved enough if (distance < distanceThreshold) { @@ -99,41 +99,41 @@ class BackgroundLocationService { // Update device's advertised location if (_bleService != null && _bleService!.isConnected) { try { - print('๐Ÿ“ค [BackgroundLocation] Updating device location...'); + debugPrint('๐Ÿ“ค [BackgroundLocation] Updating device location...'); await _bleService!.setAdvertLatLon( latitude: position.latitude, longitude: position.longitude, ); // Send advertisement to mesh network - print('๐Ÿ“ก [BackgroundLocation] Broadcasting self advertisement...'); + debugPrint('๐Ÿ“ก [BackgroundLocation] Broadcasting self advertisement...'); await _bleService!.sendSelfAdvert(floodMode: true); - print('โœ… [BackgroundLocation] Location update sent successfully'); + debugPrint('โœ… [BackgroundLocation] Location update sent successfully'); } catch (e) { - print('โŒ [BackgroundLocation] Failed to send location update: $e'); + debugPrint('โŒ [BackgroundLocation] Failed to send location update: $e'); } } else { - print('โš ๏ธ [BackgroundLocation] BLE disconnected, cannot send update'); + debugPrint('โš ๏ธ [BackgroundLocation] BLE disconnected, cannot send update'); } }); - print('โœ… [BackgroundLocation] Tracking started with ${distanceThreshold}m threshold'); + debugPrint('โœ… [BackgroundLocation] Tracking started with ${distanceThreshold}m threshold'); return true; } catch (e) { - print('โŒ [BackgroundLocation] Failed to start tracking: $e'); + debugPrint('โŒ [BackgroundLocation] Failed to start tracking: $e'); return false; } } /// Stop location tracking Future stopTracking() async { - print('๐Ÿ›‘ [BackgroundLocation] Stopping tracking'); + debugPrint('๐Ÿ›‘ [BackgroundLocation] Stopping tracking'); await _positionSubscription?.cancel(); _positionSubscription = null; final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_prefKeyEnabled, false); - print('โœ… [BackgroundLocation] Tracking stopped'); + debugPrint('โœ… [BackgroundLocation] Tracking stopped'); } /// Update the distance threshold for location updates @@ -141,7 +141,7 @@ class BackgroundLocationService { Future updateDistanceThreshold(double distance) async { final prefs = await SharedPreferences.getInstance(); await prefs.setDouble(_prefKeyDistance, distance); - print('๐Ÿ“ [BackgroundLocation] Distance threshold updated to ${distance}m'); + debugPrint('๐Ÿ“ [BackgroundLocation] Distance threshold updated to ${distance}m'); // Restart tracking if currently enabled final isEnabled = prefs.getBool(_prefKeyEnabled) ?? false; diff --git a/lib/services/ble/ble_command_sender.dart b/lib/services/ble/ble_command_sender.dart index c55276b..cc62b37 100644 --- a/lib/services/ble/ble_command_sender.dart +++ b/lib/services/ble/ble_command_sender.dart @@ -128,9 +128,9 @@ class BleCommandSender { ? '0x${commandCode.toRadixString(16).padLeft(2, '0').toUpperCase()}' : 'N/A'; - print('๐Ÿ“ค [TX] Sending command: $opcodeName ($opcodeHex)'); - print(' Data size: ${data.length} bytes'); - print(' Hex: ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); + debugPrint('๐Ÿ“ค [TX] Sending command: $opcodeName ($opcodeHex)'); + debugPrint(' Data size: ${data.length} bytes'); + debugPrint(' Hex: ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); // Check if the characteristic supports write without response final supportsWriteWithoutResponse = _rxCharacteristic!.properties.writeWithoutResponse; @@ -151,9 +151,9 @@ class BleCommandSender { _txPacketCount++; onTxActivity?.call(); - print('โœ… [TX] Command sent successfully'); + debugPrint('โœ… [TX] Command sent successfully'); } catch (e) { - print('โŒ [TX] Write error: $e'); + debugPrint('โŒ [TX] Write error: $e'); onError?.call('Write error: $e'); rethrow; } diff --git a/lib/services/ble/ble_connection_manager.dart b/lib/services/ble/ble_connection_manager.dart index 57909ff..55cd626 100644 --- a/lib/services/ble/ble_connection_manager.dart +++ b/lib/services/ble/ble_connection_manager.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import '../meshcore_constants.dart'; @@ -60,42 +61,42 @@ class BleConnectionManager { Duration timeout = const Duration(seconds: 10), }) async* { try { - print('๐Ÿ” [BLE] Starting scan for MeshCore devices...'); - print(' Service UUID: ${MeshCoreConstants.bleServiceUuid}'); - print(' Timeout: ${timeout.inSeconds}s'); + debugPrint('๐Ÿ” [BLE] Starting scan for MeshCore devices...'); + debugPrint(' Service UUID: ${MeshCoreConstants.bleServiceUuid}'); + debugPrint(' Timeout: ${timeout.inSeconds}s'); await FlutterBluePlus.startScan( timeout: timeout, withServices: [Guid(MeshCoreConstants.bleServiceUuid)], ); - print('โœ… [BLE] Scan started successfully'); + debugPrint('โœ… [BLE] Scan started successfully'); int deviceCount = 0; await for (final scanResult in FlutterBluePlus.scanResults) { - print( + debugPrint( '๐Ÿ“ก [BLE] Scan results batch received: ${scanResult.length} results', ); for (final result in scanResult) { - print( + debugPrint( ' Device: ${result.device.platformName} (${result.device.remoteId})', ); - print(' RSSI: ${result.rssi}'); - print(' Service UUIDs: ${result.advertisementData.serviceUuids}'); + debugPrint(' RSSI: ${result.rssi}'); + debugPrint(' Service UUIDs: ${result.advertisementData.serviceUuids}'); if (result.advertisementData.serviceUuids.contains( Guid(MeshCoreConstants.bleServiceUuid), )) { deviceCount++; - print(' โœ… MeshCore device found! Total: $deviceCount'); + debugPrint(' โœ… MeshCore device found! Total: $deviceCount'); yield result; } else { - print(' โŒ Not a MeshCore device (service UUID mismatch)'); + debugPrint(' โŒ Not a MeshCore device (service UUID mismatch)'); } } } - print('๐Ÿ [BLE] Scan completed. Found $deviceCount MeshCore devices'); + debugPrint('๐Ÿ [BLE] Scan completed. Found $deviceCount MeshCore devices'); } catch (e) { - print('โŒ [BLE] Scan error: $e'); + debugPrint('โŒ [BLE] Scan error: $e'); onError?.call('Scan error: $e'); } } @@ -103,35 +104,35 @@ class BleConnectionManager { /// Connect to a MeshCore device Future connect(BluetoothDevice device) async { try { - print( + debugPrint( '๐Ÿ”ต [BLE] Starting connection to device: ${device.platformName} (${device.remoteId})', ); _device = device; // Connect to device - print('๐Ÿ”ต [BLE] Calling device.connect() with 15s timeout...'); + debugPrint('๐Ÿ”ต [BLE] Calling device.connect() with 15s timeout...'); await device.connect( license: License.free, timeout: const Duration(seconds: 15), mtu: 512, ); - print('โœ… [BLE] Device connected successfully'); + debugPrint('โœ… [BLE] Device connected successfully'); // Discover services - print('๐Ÿ”ต [BLE] Discovering services...'); + debugPrint('๐Ÿ”ต [BLE] Discovering services...'); final services = await device.discoverServices(); - print('โœ… [BLE] Found ${services.length} services'); + debugPrint('โœ… [BLE] Found ${services.length} services'); // Log all discovered services for debugging for (final service in services) { - print(' ๐Ÿ“‹ Service: ${service.uuid}'); + debugPrint(' ๐Ÿ“‹ Service: ${service.uuid}'); for (final char in service.characteristics) { - print(' - Characteristic: ${char.uuid}'); + debugPrint(' - Characteristic: ${char.uuid}'); } } // Find MeshCore service - print( + debugPrint( '๐Ÿ”ต [BLE] Looking for MeshCore service: ${MeshCoreConstants.bleServiceUuid}', ); BluetoothService? meshCoreService; @@ -139,51 +140,51 @@ class BleConnectionManager { if (service.uuid.toString().toLowerCase() == MeshCoreConstants.bleServiceUuid.toLowerCase()) { meshCoreService = service; - print('โœ… [BLE] Found MeshCore service'); + debugPrint('โœ… [BLE] Found MeshCore service'); break; } } if (meshCoreService == null) { - print('โŒ [BLE] MeshCore service not found!'); + debugPrint('โŒ [BLE] MeshCore service not found!'); throw Exception('MeshCore service not found'); } // Find RX and TX characteristics - print('๐Ÿ”ต [BLE] Looking for RX and TX characteristics...'); - print(' RX UUID: ${MeshCoreConstants.bleCharacteristicRxUuid}'); - print(' TX UUID: ${MeshCoreConstants.bleCharacteristicTxUuid}'); + debugPrint('๐Ÿ”ต [BLE] Looking for RX and TX characteristics...'); + debugPrint(' RX UUID: ${MeshCoreConstants.bleCharacteristicRxUuid}'); + debugPrint(' TX UUID: ${MeshCoreConstants.bleCharacteristicTxUuid}'); for (final characteristic in meshCoreService.characteristics) { final uuid = characteristic.uuid.toString().toLowerCase(); - print(' ๐Ÿ“‹ Checking characteristic: $uuid'); + debugPrint(' ๐Ÿ“‹ Checking characteristic: $uuid'); if (uuid == MeshCoreConstants.bleCharacteristicRxUuid.toLowerCase()) { _rxCharacteristic = characteristic; - print(' โœ… Found RX characteristic'); + debugPrint(' โœ… Found RX characteristic'); } else if (uuid == MeshCoreConstants.bleCharacteristicTxUuid.toLowerCase()) { _txCharacteristic = characteristic; - print(' โœ… Found TX characteristic'); + debugPrint(' โœ… Found TX characteristic'); } } if (_rxCharacteristic == null || _txCharacteristic == null) { - print('โŒ [BLE] Required characteristics not found!'); - print(' RX found: ${_rxCharacteristic != null}'); - print(' TX found: ${_txCharacteristic != null}'); + debugPrint('โŒ [BLE] Required characteristics not found!'); + debugPrint(' RX found: ${_rxCharacteristic != null}'); + debugPrint(' TX found: ${_txCharacteristic != null}'); throw Exception('Required characteristics not found'); } // Enable notifications on TX characteristic - print('๐Ÿ”ต [BLE] Enabling notifications on TX characteristic...'); + debugPrint('๐Ÿ”ต [BLE] Enabling notifications on TX characteristic...'); await _txCharacteristic!.setNotifyValue(true); - print('โœ… [BLE] Notifications enabled'); + debugPrint('โœ… [BLE] Notifications enabled'); _isConnected = true; _reconnectionAttempt = 0; // Reset reconnection counter on successful connection - print('๐Ÿ”ต [BLE] Notifying connection state change: connected'); + debugPrint('๐Ÿ”ต [BLE] Notifying connection state change: connected'); onConnectionStateChanged?.call(true); // Monitor connection state for automatic reconnection @@ -192,11 +193,11 @@ class BleConnectionManager { // Start RSSI monitoring _startRssiMonitoring(); - print('โœ…โœ…โœ… [BLE] Connection completed successfully!'); + debugPrint('โœ…โœ…โœ… [BLE] Connection completed successfully!'); return true; } catch (e) { - print('โŒโŒโŒ [BLE] Connection failed: $e'); - print('Stack trace: ${StackTrace.current}'); + debugPrint('โŒโŒโŒ [BLE] Connection failed: $e'); + debugPrint('Stack trace: ${StackTrace.current}'); onError?.call('Connection error: $e'); _isConnected = false; onConnectionStateChanged?.call(false); @@ -207,7 +208,7 @@ class BleConnectionManager { /// Disconnect from device Future disconnect() async { try { - print('๐Ÿ”ด [BLE] Disconnect requested by user'); + debugPrint('๐Ÿ”ด [BLE] Disconnect requested by user'); // Disable reconnection before disconnecting _reconnectionEnabled = false; _cancelReconnection(); @@ -226,7 +227,7 @@ class BleConnectionManager { /// Setup connection monitoring for automatic reconnection void _setupConnectionMonitoring() { - print( + debugPrint( '๐Ÿ”ต [BLE] Setting up connection monitoring for device: ${_device?.platformName}', ); @@ -235,20 +236,20 @@ class BleConnectionManager { // Monitor connection state changes _connectionStateSubscription = _device?.connectionState.listen((state) { - print('๐Ÿ”” [BLE] Connection state changed: $state'); + debugPrint('๐Ÿ”” [BLE] Connection state changed: $state'); if (state == BluetoothConnectionState.disconnected) { - print('โš ๏ธ [BLE] Device disconnected unexpectedly!'); + debugPrint('โš ๏ธ [BLE] Device disconnected unexpectedly!'); _isConnected = false; onConnectionStateChanged?.call(false); // Attempt automatic reconnection if enabled if (_reconnectionEnabled && !_isReconnecting) { - print('๐Ÿ”„ [BLE] Starting automatic reconnection...'); + debugPrint('๐Ÿ”„ [BLE] Starting automatic reconnection...'); _attemptReconnection(); } } else if (state == BluetoothConnectionState.connected) { - print('โœ… [BLE] Device connected'); + debugPrint('โœ… [BLE] Device connected'); _isConnected = true; _reconnectionAttempt = 0; _isReconnecting = false; @@ -266,13 +267,13 @@ class BleConnectionManager { _isReconnecting = true; _reconnectionAttempt++; - print( + debugPrint( '๐Ÿ”„ [BLE] Reconnection attempt $_reconnectionAttempt of $_maxReconnectionAttempts', ); onReconnectionAttempt?.call(_reconnectionAttempt, _maxReconnectionAttempts); if (_reconnectionAttempt > _maxReconnectionAttempts) { - print( + debugPrint( 'โŒ [BLE] Max reconnection attempts reached after ~15 minutes. Giving up.', ); _isReconnecting = false; @@ -289,30 +290,30 @@ class BleConnectionManager { ); final delayMs = _reconnectionDelaysMs[delayIndex]; - print( + debugPrint( '๐Ÿ”„ [BLE] Waiting ${(delayMs / 1000).toStringAsFixed(0)}s before reconnection attempt $_reconnectionAttempt...', ); // Wait before attempting reconnection _reconnectionTimer = Timer(Duration(milliseconds: delayMs), () async { if (!_reconnectionEnabled) { - print('๐Ÿ”„ [BLE] Reconnection cancelled by user'); + debugPrint('๐Ÿ”„ [BLE] Reconnection cancelled by user'); _isReconnecting = false; return; } try { - print('๐Ÿ”„ [BLE] Attempting to reconnect...'); + debugPrint('๐Ÿ”„ [BLE] Attempting to reconnect...'); // Try to reconnect final success = await connect(_device!); if (success) { - print('โœ… [BLE] Reconnection successful!'); + debugPrint('โœ… [BLE] Reconnection successful!'); _isReconnecting = false; _reconnectionAttempt = 0; } else { - print('โŒ [BLE] Reconnection attempt $_reconnectionAttempt failed'); + debugPrint('โŒ [BLE] Reconnection attempt $_reconnectionAttempt failed'); _isReconnecting = false; // Try again if we haven't reached max attempts @@ -325,7 +326,7 @@ class BleConnectionManager { } } } catch (e) { - print('โŒ [BLE] Reconnection attempt $_reconnectionAttempt error: $e'); + debugPrint('โŒ [BLE] Reconnection attempt $_reconnectionAttempt error: $e'); _isReconnecting = false; // Try again if we haven't reached max attempts @@ -342,7 +343,7 @@ class BleConnectionManager { /// Cancel ongoing reconnection attempts void _cancelReconnection() { - print('๐Ÿ”ด [BLE] Cancelling reconnection attempts'); + debugPrint('๐Ÿ”ด [BLE] Cancelling reconnection attempts'); _reconnectionTimer?.cancel(); _reconnectionTimer = null; _isReconnecting = false; @@ -353,13 +354,13 @@ class BleConnectionManager { /// Enable automatic reconnection (useful after user manually disconnects) void enableReconnection() { - print('๐Ÿ”ต [BLE] Re-enabling automatic reconnection'); + debugPrint('๐Ÿ”ต [BLE] Re-enabling automatic reconnection'); _reconnectionEnabled = true; } /// Start monitoring RSSI in the background void _startRssiMonitoring() { - print('๐Ÿ“ก [BLE] Starting RSSI monitoring (every 5 seconds)'); + debugPrint('๐Ÿ“ก [BLE] Starting RSSI monitoring (every 5 seconds)'); _stopRssiMonitoring(); // Cancel any existing timer _rssiTimer = Timer.periodic(const Duration(seconds: 5), (timer) async { @@ -371,7 +372,7 @@ class BleConnectionManager { onRssiUpdate?.call(rssi); } } catch (e) { - print('โš ๏ธ [BLE] Failed to read RSSI: $e'); + debugPrint('โš ๏ธ [BLE] Failed to read RSSI: $e'); } } }); @@ -382,12 +383,12 @@ class BleConnectionManager { _rssiTimer?.cancel(); _rssiTimer = null; _lastRssi = null; - print('๐Ÿ“ก [BLE] RSSI monitoring stopped'); + debugPrint('๐Ÿ“ก [BLE] RSSI monitoring stopped'); } /// Dispose resources void dispose() { - print('๐Ÿ”ด [BLE] Disposing BLE connection manager'); + debugPrint('๐Ÿ”ด [BLE] Disposing BLE connection manager'); _cancelReconnection(); _stopRssiMonitoring(); _device = null; diff --git a/lib/services/ble/ble_response_handler.dart b/lib/services/ble/ble_response_handler.dart index 53e60fc..0f86aa8 100644 --- a/lib/services/ble/ble_response_handler.dart +++ b/lib/services/ble/ble_response_handler.dart @@ -93,7 +93,7 @@ class BleResponseHandler { _txSubscription = txCharacteristic.lastValueStream.listen( _onDataReceived, onError: (error) { - print('โŒ [BLE] TX notification error: $error'); + debugPrint('โŒ [BLE] TX notification error: $error'); onError?.call('TX notification error: $error'); }, ); @@ -104,7 +104,7 @@ class BleResponseHandler { try { // Handle empty data if (data.isEmpty) { - print('โš ๏ธ [RX] Empty data received, ignoring'); + debugPrint('โš ๏ธ [RX] Empty data received, ignoring'); return; } @@ -121,124 +121,124 @@ class BleResponseHandler { final opcodeName = MeshCoreOpcodeNames.getOpcodeName(responseCode, isTx: false); final opcodeHex = '0x${responseCode.toRadixString(16).padLeft(2, '0').toUpperCase()}'; - print('๐Ÿ“ฅ [RX] Received: $opcodeName ($opcodeHex)'); - print(' Data size: ${data.length} bytes'); - print(' Hex: ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); - print(' Payload: ${reader.remainingBytesCount} bytes'); + debugPrint('๐Ÿ“ฅ [RX] Received: $opcodeName ($opcodeHex)'); + debugPrint(' Data size: ${data.length} bytes'); + debugPrint(' Hex: ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); + debugPrint(' Payload: ${reader.remainingBytesCount} bytes'); // Log RX packet (before processing so we capture everything) _logPacket(dataBytes, PacketDirection.rx, responseCode: responseCode); switch (responseCode) { case MeshCoreConstants.respContactsStart: - print(' โ†’ Handling ContactsStart'); + debugPrint(' โ†’ Handling ContactsStart'); _handleContactsStart(reader); break; case MeshCoreConstants.respContact: - print(' โ†’ Handling Contact'); + debugPrint(' โ†’ Handling Contact'); _handleContact(reader); break; case MeshCoreConstants.respEndOfContacts: - print(' โ†’ Handling EndOfContacts'); + debugPrint(' โ†’ Handling EndOfContacts'); _handleEndOfContacts(reader); break; case MeshCoreConstants.respSent: - print(' โ†’ Handling Sent confirmation'); + debugPrint(' โ†’ Handling Sent confirmation'); _handleSentConfirmation(reader); break; case MeshCoreConstants.respContactMsgRecv: - print(' โ†’ Handling ContactMessage'); + debugPrint(' โ†’ Handling ContactMessage'); _handleContactMessage(reader); break; case MeshCoreConstants.respChannelMsgRecv: - print(' โ†’ Handling ChannelMessage'); + debugPrint(' โ†’ Handling ChannelMessage'); _handleChannelMessage(reader); break; case MeshCoreConstants.pushTelemetryResponse: - print(' โ†’ Handling TelemetryResponse'); + debugPrint(' โ†’ Handling TelemetryResponse'); _handleTelemetryResponse(reader); break; case MeshCoreConstants.pushBinaryResponse: - print(' โ†’ Handling BinaryResponse'); + debugPrint(' โ†’ Handling BinaryResponse'); _handleBinaryResponse(reader); break; case MeshCoreConstants.respDeviceInfo: - print(' โ†’ Handling DeviceInfo'); + debugPrint(' โ†’ Handling DeviceInfo'); _handleDeviceInfo(reader); break; case MeshCoreConstants.respSelfInfo: - print(' โ†’ Handling SelfInfo'); + debugPrint(' โ†’ Handling SelfInfo'); _handleSelfInfo(reader); break; case MeshCoreConstants.pushAdvert: - print(' โ†’ Handling Advert push'); + debugPrint(' โ†’ Handling Advert push'); _handleAdvert(reader); break; case MeshCoreConstants.pushPathUpdated: - print(' โ†’ Handling PathUpdated push'); + debugPrint(' โ†’ Handling PathUpdated push'); _handlePathUpdated(reader); break; case MeshCoreConstants.pushLogRxData: - print(' โ†’ Handling LogRxData push'); + debugPrint(' โ†’ Handling LogRxData push'); _handleLogRxData(reader); break; case MeshCoreConstants.pushNewAdvert: - print(' โ†’ Handling NewAdvert push'); + debugPrint(' โ†’ Handling NewAdvert push'); _handleNewAdvert(reader); break; case MeshCoreConstants.pushSendConfirmed: - print(' โ†’ Handling SendConfirmed push'); + debugPrint(' โ†’ Handling SendConfirmed push'); _handleSendConfirmed(reader); break; case MeshCoreConstants.pushMsgWaiting: - print(' โ†’ Handling MsgWaiting push'); + debugPrint(' โ†’ Handling MsgWaiting push'); _handleMsgWaiting(reader); break; case MeshCoreConstants.pushLoginSuccess: - print(' โ†’ Handling LoginSuccess push'); + debugPrint(' โ†’ Handling LoginSuccess push'); _handleLoginSuccess(reader); break; case MeshCoreConstants.pushLoginFail: - print(' โ†’ Handling LoginFail push'); + debugPrint(' โ†’ Handling LoginFail push'); _handleLoginFail(reader); break; case MeshCoreConstants.pushStatusResponse: - print(' โ†’ Handling StatusResponse push'); + debugPrint(' โ†’ Handling StatusResponse push'); _handleStatusResponse(reader); break; case MeshCoreConstants.respCurrTime: - print(' โ†’ Handling CurrentTime'); + debugPrint(' โ†’ Handling CurrentTime'); _handleCurrentTime(reader); break; case MeshCoreConstants.respBatteryVoltage: - print(' โ†’ Handling BatteryAndStorage'); + debugPrint(' โ†’ Handling BatteryAndStorage'); _handleBatteryAndStorage(reader); break; case MeshCoreConstants.respChannelInfo: - print(' โ†’ Handling ChannelInfo'); + debugPrint(' โ†’ Handling ChannelInfo'); _handleChannelInfo(reader); break; case MeshCoreConstants.respNoMoreMessages: - print(' โ†’ Response: No More Messages'); + debugPrint(' โ†’ Response: No More Messages'); onNoMoreMessages?.call(); break; case MeshCoreConstants.respOk: - print(' โ†’ Response: OK'); + debugPrint(' โ†’ Response: OK'); // Complete any pending ACK command _commandQueue?.completeCommand(MeshCoreConstants.respOk, null); break; case MeshCoreConstants.respErr: - print(' โ†’ Response: ERROR'); + debugPrint(' โ†’ Response: ERROR'); _handleError(reader); break; default: - print(' โš ๏ธ Unknown response code: $responseCode'); + debugPrint(' โš ๏ธ Unknown response code: $responseCode'); break; } - print('โœ… [BLE] Data parsed successfully'); + debugPrint('โœ… [BLE] Data parsed successfully'); } catch (e, stackTrace) { - print('โŒ [BLE] Data parsing error: $e'); - print(' Stack trace: $stackTrace'); + debugPrint('โŒ [BLE] Data parsing error: $e'); + debugPrint(' Stack trace: $stackTrace'); onError?.call('Data parsing error: $e'); } } @@ -253,12 +253,12 @@ class BleResponseHandler { void _handleContact(BufferReader reader) { try { final contact = FrameParser.parseContact(reader); - print(' โœ… [Contact] Parsed successfully: ${contact.advName}'); - print(' outPathLen: ${contact.outPathLen} (${contact.pathDescription})'); + debugPrint(' โœ… [Contact] Parsed successfully: ${contact.advName}'); + debugPrint(' outPathLen: ${contact.outPathLen} (${contact.pathDescription})'); _pendingContacts.add(contact); onContactReceived?.call(contact); } catch (e) { - print(' โŒ [Contact] Parsing error: $e'); + debugPrint(' โŒ [Contact] Parsing error: $e'); onError?.call('Contact parsing error: $e'); } } @@ -274,7 +274,7 @@ class BleResponseHandler { try { final result = FrameParser.parseSentConfirmation(reader); if (result.isNotEmpty) { - print(' โœ… [Sent] Message sent successfully'); + debugPrint(' โœ… [Sent] Message sent successfully'); // Complete any pending command waiting for sent confirmation _commandQueue?.completeCommand>( @@ -289,7 +289,7 @@ class BleResponseHandler { ); } } catch (e) { - print(' โŒ [Sent] Parsing error: $e'); + debugPrint(' โŒ [Sent] Parsing error: $e'); } } @@ -297,10 +297,10 @@ class BleResponseHandler { void _handleContactMessage(BufferReader reader) { try { final message = FrameParser.parseContactMessage(reader); - print(' โœ… [ContactMessage] Parsed successfully'); + debugPrint(' โœ… [ContactMessage] Parsed successfully'); onMessageReceived?.call(message); } catch (e) { - print(' โŒ [ContactMessage] Parsing error: $e'); + debugPrint(' โŒ [ContactMessage] Parsing error: $e'); onError?.call('Contact message parsing error: $e'); } } @@ -309,10 +309,10 @@ class BleResponseHandler { void _handleChannelMessage(BufferReader reader) { try { final message = FrameParser.parseChannelMessage(reader); - print(' โœ… [ChannelMessage] Parsed successfully'); + debugPrint(' โœ… [ChannelMessage] Parsed successfully'); onMessageReceived?.call(message); } catch (e) { - print(' โŒ [ChannelMessage] Parsing error: $e'); + debugPrint(' โŒ [ChannelMessage] Parsing error: $e'); onError?.call('Channel message parsing error: $e'); } } @@ -321,13 +321,13 @@ class BleResponseHandler { void _handleTelemetryResponse(BufferReader reader) { try { final result = FrameParser.parseTelemetryResponse(reader); - print(' โœ… [Telemetry] Parsed successfully'); + debugPrint(' โœ… [Telemetry] Parsed successfully'); onTelemetryReceived?.call( result['publicKeyPrefix'] as Uint8List, result['lppSensorData'] as Uint8List, ); } catch (e) { - print(' โŒ [Telemetry] Parsing error: $e'); + debugPrint(' โŒ [Telemetry] Parsing error: $e'); onError?.call('Telemetry parsing error: $e'); } } @@ -336,14 +336,14 @@ class BleResponseHandler { void _handleBinaryResponse(BufferReader reader) { try { final result = FrameParser.parseBinaryResponse(reader); - print(' โœ… [BinaryResponse] Parsed successfully'); + debugPrint(' โœ… [BinaryResponse] Parsed successfully'); onBinaryResponse?.call( result['publicKeyPrefix'] as Uint8List, result['tag'] as int, result['responseData'] as Uint8List, ); } catch (e) { - print(' โŒ [BinaryResponse] Parsing error: $e'); + debugPrint(' โŒ [BinaryResponse] Parsing error: $e'); onError?.call('Binary response parsing error: $e'); } } @@ -360,9 +360,9 @@ class BleResponseHandler { ); onDeviceInfoReceived?.call(info); - print(' โœ… [DeviceInfo] Parsed successfully'); + debugPrint(' โœ… [DeviceInfo] Parsed successfully'); } catch (e) { - print(' โŒ [DeviceInfo] Parsing error: $e'); + debugPrint(' โŒ [DeviceInfo] Parsing error: $e'); onError?.call('DeviceInfo parsing error: $e'); } } @@ -381,9 +381,9 @@ class BleResponseHandler { onSelfInfoReceived?.call(info); } - print(' โœ… [SelfInfo] Parsed successfully'); + debugPrint(' โœ… [SelfInfo] Parsed successfully'); } catch (e) { - print(' โŒ [SelfInfo] Parsing error: $e'); + debugPrint(' โŒ [SelfInfo] Parsing error: $e'); } } @@ -394,9 +394,9 @@ class BleResponseHandler { if (publicKey != null) { onAdvertReceived?.call(publicKey); } - print(' โœ… [Advert] Parsed successfully'); + debugPrint(' โœ… [Advert] Parsed successfully'); } catch (e) { - print(' โŒ [Advert] Parsing error: $e'); + debugPrint(' โŒ [Advert] Parsing error: $e'); } } @@ -407,37 +407,37 @@ class BleResponseHandler { if (publicKey != null) { onPathUpdated?.call(publicKey); } - print(' โœ… [PathUpdated] Parsed successfully'); + debugPrint(' โœ… [PathUpdated] Parsed successfully'); } catch (e) { - print(' โŒ [PathUpdated] Parsing error: $e'); + debugPrint(' โŒ [PathUpdated] Parsing error: $e'); } } /// Handle LogRxData push - includes extensive decoding logic void _handleLogRxData(BufferReader reader) { try { - print(' [LogRxData] Parsing log rx data from over-the-air packet...'); + debugPrint(' [LogRxData] Parsing log rx data from over-the-air packet...'); final data = reader.readRemainingBytes(); if (data.length < 2) { - print(' โš ๏ธ [LogRxData] Insufficient data'); + debugPrint(' โš ๏ธ [LogRxData] Insufficient data'); return; } final snrRaw = data[0]; final snrDb = (snrRaw.toSigned(8)) / 4.0; - print(' SNR: ${snrDb.toStringAsFixed(2)} dB'); + debugPrint(' SNR: ${snrDb.toStringAsFixed(2)} dB'); final rssiDbm = data[1].toSigned(8); - print(' RSSI: $rssiDbm dBm'); + debugPrint(' RSSI: $rssiDbm dBm'); if (data.length <= 2) { - print(' โš ๏ธ [LogRxData] No raw packet data'); + debugPrint(' โš ๏ธ [LogRxData] No raw packet data'); return; } final rawPacketData = data.sublist(2); - print(' Raw packet data: ${rawPacketData.length} bytes'); + debugPrint(' Raw packet data: ${rawPacketData.length} bytes'); // Decode packet header and path for display if (rawPacketData.length >= 2) { @@ -445,31 +445,31 @@ class BleResponseHandler { final payloadType = (header >> 2) & 0x0F; final pathLen = rawPacketData[1]; - print(' Packet type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); + debugPrint(' Packet type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); if (pathLen > 0 && rawPacketData.length >= 2 + pathLen) { final path = rawPacketData.sublist(2, 2 + pathLen); final pathStr = path.map((b) => '0x${b.toRadixString(16).padLeft(2, '0')}').join(' โ†’ '); - print(' Path ($pathLen hops): $pathStr'); + debugPrint(' Path ($pathLen hops): $pathStr'); // Highlight multi-hop packets if (pathLen > 1) { - print(' ๐Ÿ”„ MULTI-HOP PACKET! Original sender: 0x${path[0].toRadixString(16).padLeft(2, '0')}'); + debugPrint(' ๐Ÿ”„ MULTI-HOP PACKET! Original sender: 0x${path[0].toRadixString(16).padLeft(2, '0')}'); } // Check if our node hash is in the path if (_ourNodeHash != null && path.contains(_ourNodeHash!)) { - print(' โœ…โœ…โœ… ECHO DETECTED! Path contains our hash (0x${_ourNodeHash!.toRadixString(16).padLeft(2, '0')}) โœ…โœ…โœ…'); + debugPrint(' โœ…โœ…โœ… ECHO DETECTED! Path contains our hash (0x${_ourNodeHash!.toRadixString(16).padLeft(2, '0')}) โœ…โœ…โœ…'); if (path[0] == _ourNodeHash) { - print(' ๐Ÿ‘‰ WE are the original sender!'); + debugPrint(' ๐Ÿ‘‰ WE are the original sender!'); } else { - print(' ๐Ÿ‘‰ Original sender: 0x${path[0].toRadixString(16).padLeft(2, '0')}, WE sent it to the network'); + debugPrint(' ๐Ÿ‘‰ Original sender: 0x${path[0].toRadixString(16).padLeft(2, '0')}, WE sent it to the network'); } } else { - print(' โ„น๏ธ Does NOT contain our hash (not our message)'); + debugPrint(' โ„น๏ธ Does NOT contain our hash (not our message)'); } } else { - print(' Path length: $pathLen'); + debugPrint(' Path length: $pathLen'); } } @@ -507,9 +507,9 @@ class BleResponseHandler { } } - print(' โœ… [LogRxData] Parsed successfully'); + debugPrint(' โœ… [LogRxData] Parsed successfully'); } catch (e) { - print(' โŒ [LogRxData] Parsing error: $e'); + debugPrint(' โŒ [LogRxData] Parsing error: $e'); } } @@ -543,26 +543,26 @@ class BleResponseHandler { /// Check if received packet is an echo of a sent message void _checkForEcho(Uint8List rawPacket, int snrRaw, int rssiDbm) { try { - print(' ๐Ÿ” [Echo] _checkForEcho called, packet size: ${rawPacket.length} bytes'); + debugPrint(' ๐Ÿ” [Echo] _checkForEcho called, packet size: ${rawPacket.length} bytes'); // Need at least header + path_len if (rawPacket.length < 2) { - print(' โš ๏ธ [Echo] Packet too short'); + debugPrint(' โš ๏ธ [Echo] Packet too short'); return; } final header = rawPacket[0]; final payloadType = (header >> 2) & 0x0F; - print(' ๐Ÿ” [Echo] Payload type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); + debugPrint(' ๐Ÿ” [Echo] Payload type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); if (payloadType != 0x05) { - print(' โš ๏ธ [Echo] Not GRP_TXT, ignoring'); + debugPrint(' โš ๏ธ [Echo] Not GRP_TXT, ignoring'); return; // Only track GRP_TXT } final pathLen = rawPacket[1]; - print(' ๐Ÿ” [Echo] Path length: $pathLen'); + debugPrint(' ๐Ÿ” [Echo] Path length: $pathLen'); if (pathLen == 0 || rawPacket.length < 2 + pathLen) { - print(' โš ๏ธ [Echo] Invalid path length'); + debugPrint(' โš ๏ธ [Echo] Invalid path length'); return; } @@ -592,23 +592,23 @@ class BleResponseHandler { tracker.echoCount++; tracker.echoTimestamps.add(DateTime.now()); - print(' ๐Ÿ”Š [Echo] New echo detected!'); - print(' Message: ${tracker.messageId}'); - print(' Path: $pathSignature'); - print(' Total echoes: ${tracker.echoCount}'); - print(' Unique paths: ${tracker.uniqueEchoPaths.length}'); + debugPrint(' ๐Ÿ”Š [Echo] New echo detected!'); + debugPrint(' Message: ${tracker.messageId}'); + debugPrint(' Path: $pathSignature'); + debugPrint(' Total echoes: ${tracker.echoCount}'); + debugPrint(' Unique paths: ${tracker.uniqueEchoPaths.length}'); // Notify callback onMessageEchoDetected?.call(tracker.messageId, tracker.echoCount, snrRaw, rssiDbm); } else { - print(' โ™ป๏ธ [Echo] Duplicate path (already counted): $pathSignature'); + debugPrint(' โ™ป๏ธ [Echo] Duplicate path (already counted): $pathSignature'); } } // Cleanup expired trackers _cleanupExpiredTrackers(); } catch (e) { - print(' โš ๏ธ [Echo] Error checking for echo: $e'); + debugPrint(' โš ๏ธ [Echo] Error checking for echo: $e'); } } @@ -631,15 +631,15 @@ class BleResponseHandler { // Store by message ID temporarily _sentMessageTrackers[messageId] = tracker; - print(' ๐Ÿ“ค [Echo] Tracking message $messageId (will match any GRP_TXT within 10000ms)'); - print(' ๐Ÿ“Š [Echo] Total trackers: ${_sentMessageTrackers.length}'); + debugPrint(' ๐Ÿ“ค [Echo] Tracking message $messageId (will match any GRP_TXT within 10000ms)'); + debugPrint(' ๐Ÿ“Š [Echo] Total trackers: ${_sentMessageTrackers.length}'); // Cleanup if too many trackers if (_sentMessageTrackers.length > _maxTrackers) { _cleanupOldestTrackers(); } } catch (e) { - print(' โš ๏ธ [Echo] Error tracking sent message: $e'); + debugPrint(' โš ๏ธ [Echo] Error tracking sent message: $e'); } } @@ -649,8 +649,8 @@ class BleResponseHandler { /// Set our node hash for packet identification void setOurNodeHash(int nodeHash) { _ourNodeHash = nodeHash; - print(' ๐Ÿ”‘ [Echo] Our node hash set to: 0x${nodeHash.toRadixString(16).padLeft(2, '0')}'); - print(' โ„น๏ธ [Echo] Will track packets containing our hash in the path'); + debugPrint(' ๐Ÿ”‘ [Echo] Our node hash set to: 0x${nodeHash.toRadixString(16).padLeft(2, '0')}'); + debugPrint(' โ„น๏ธ [Echo] Will track packets containing our hash in the path'); } /// Associate a captured packet with a sent message @@ -666,27 +666,27 @@ class BleResponseHandler { /// [3+] = rest of path + encrypted payload void _associatePacketWithSentMessage(Uint8List rawPacket) { try { - print(' ๐Ÿ” [Echo] _associatePacketWithSentMessage called, packet size: ${rawPacket.length}'); + debugPrint(' ๐Ÿ” [Echo] _associatePacketWithSentMessage called, packet size: ${rawPacket.length}'); // Need at least 3 bytes: header + path_len + first path byte if (rawPacket.length < 3) { - print(' โš ๏ธ [Echo] Packet too short for association'); + debugPrint(' โš ๏ธ [Echo] Packet too short for association'); return; } // Check if this is a GRP_TXT packet (payload type = 0x05) final header = rawPacket[0]; final payloadType = (header >> 2) & 0x0F; - print(' ๐Ÿ” [Echo] Association check - Payload type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); + debugPrint(' ๐Ÿ” [Echo] Association check - Payload type: 0x${payloadType.toRadixString(16).padLeft(2, '0')}'); if (payloadType != 0x05) { // Not a group message - print(' โš ๏ธ [Echo] Not GRP_TXT, skipping association'); + debugPrint(' โš ๏ธ [Echo] Not GRP_TXT, skipping association'); return; } final pathLen = rawPacket[1]; - print(' ๐Ÿ” [Echo] Path length for association: $pathLen'); + debugPrint(' ๐Ÿ” [Echo] Path length for association: $pathLen'); if (pathLen == 0) { - print(' โš ๏ธ [Echo] Path length is 0, skipping'); + debugPrint(' โš ๏ธ [Echo] Path length is 0, skipping'); return; } @@ -703,7 +703,7 @@ class BleResponseHandler { return; } - print(' โœ… [Echo] Packet contains our hash (0x${_ourNodeHash!.toRadixString(16).padLeft(2, '0')}) in path: $pathSignature'); + debugPrint(' โœ… [Echo] Packet contains our hash (0x${_ourNodeHash!.toRadixString(16).padLeft(2, '0')}) in path: $pathSignature'); // Extract encrypted payload (everything after path) final payloadStart = 2 + pathLen; @@ -736,19 +736,19 @@ class BleResponseHandler { ); _sentMessageTrackers[payloadHash] = updatedTracker; - print(' ๐Ÿ“ฆ [Echo] Captured packet for tracking!'); - print(' Message ID: ${tracker.messageId}'); - print(' Path: $pathSignature'); - print(' Time delta: ${timeSinceSent.inMilliseconds}ms'); - print(' Payload hash: $payloadHash'); - print(' Echo count: 1 (first detection)'); + debugPrint(' ๐Ÿ“ฆ [Echo] Captured packet for tracking!'); + debugPrint(' Message ID: ${tracker.messageId}'); + debugPrint(' Path: $pathSignature'); + debugPrint(' Time delta: ${timeSinceSent.inMilliseconds}ms'); + debugPrint(' Payload hash: $payloadHash'); + debugPrint(' Echo count: 1 (first detection)'); // Notify immediately that we have 1 echo onMessageEchoDetected?.call(tracker.messageId, 1, 0, 0); break; // Only associate with first pending tracker } } catch (e) { - print(' โš ๏ธ [Echo] Error associating packet: $e'); + debugPrint(' โš ๏ธ [Echo] Error associating packet: $e'); } } @@ -756,11 +756,11 @@ class BleResponseHandler { void _cleanupExpiredTrackers() { final expiredCount = _sentMessageTrackers.values.where((t) => t.isExpired).length; if (expiredCount > 0) { - print(' ๐Ÿงน [Echo] Cleaning up $expiredCount expired tracker(s)'); + debugPrint(' ๐Ÿงน [Echo] Cleaning up $expiredCount expired tracker(s)'); } _sentMessageTrackers.removeWhere((key, tracker) { if (tracker.isExpired && tracker.packetHashHex == 'pending') { - print(' โฑ๏ธ [Echo] Tracker expired without capturing: ${tracker.messageId}'); + debugPrint(' โฑ๏ธ [Echo] Tracker expired without capturing: ${tracker.messageId}'); } return tracker.isExpired; }); @@ -779,18 +779,18 @@ class BleResponseHandler { _sentMessageTrackers.remove(entry.key); } - print(' ๐Ÿงน [Echo] Cleaned up ${toRemove.length} old trackers'); + debugPrint(' ๐Ÿงน [Echo] Cleaned up ${toRemove.length} old trackers'); } /// Handle NewAdvert push void _handleNewAdvert(BufferReader reader) { try { final contact = FrameParser.parseContact(reader); - print(' โœ… [NewAdvert] Parsed successfully: ${contact.advName}'); - print(' outPathLen: ${contact.outPathLen} (${contact.pathDescription})'); + debugPrint(' โœ… [NewAdvert] Parsed successfully: ${contact.advName}'); + debugPrint(' outPathLen: ${contact.outPathLen} (${contact.pathDescription})'); onContactReceived?.call(contact); } catch (e) { - print(' โŒ [NewAdvert] Parsing error: $e'); + debugPrint(' โŒ [NewAdvert] Parsing error: $e'); onError?.call('NewAdvert parsing error: $e'); } } @@ -800,24 +800,24 @@ class BleResponseHandler { try { final result = FrameParser.parseSendConfirmed(reader); if (result.isNotEmpty) { - print(' โœ… [SendConfirmed] Message delivery confirmed'); + debugPrint(' โœ… [SendConfirmed] Message delivery confirmed'); onMessageDelivered?.call( result['ackCode'] as int, result['roundTripTime'] as int, ); } } catch (e) { - print(' โŒ [SendConfirmed] Parsing error: $e'); + debugPrint(' โŒ [SendConfirmed] Parsing error: $e'); } } /// Handle MsgWaiting push void _handleMsgWaiting(BufferReader reader) { try { - print(' [MsgWaiting] New message(s) waiting in queue'); + debugPrint(' [MsgWaiting] New message(s) waiting in queue'); onMessageWaiting?.call(); } catch (e) { - print(' โŒ [MsgWaiting] Parsing error: $e'); + debugPrint(' โŒ [MsgWaiting] Parsing error: $e'); } } @@ -826,7 +826,7 @@ class BleResponseHandler { try { final result = FrameParser.parseLoginSuccess(reader); if (result.isNotEmpty) { - print(' โœ… [LoginSuccess] Successfully logged into room'); + debugPrint(' โœ… [LoginSuccess] Successfully logged into room'); onLoginSuccess?.call( result['publicKeyPrefix'] as Uint8List, result['permissions'] as int, @@ -835,7 +835,7 @@ class BleResponseHandler { ); } } catch (e) { - print(' โŒ [LoginSuccess] Parsing error: $e'); + debugPrint(' โŒ [LoginSuccess] Parsing error: $e'); onError?.call('Login success parsing error: $e'); } } @@ -845,11 +845,11 @@ class BleResponseHandler { try { final publicKeyPrefix = FrameParser.parseLoginFail(reader); if (publicKeyPrefix != null) { - print(' โŒ [LoginFail] Failed to login to room'); + debugPrint(' โŒ [LoginFail] Failed to login to room'); onLoginFail?.call(publicKeyPrefix); } } catch (e) { - print(' โŒ [LoginFail] Parsing error: $e'); + debugPrint(' โŒ [LoginFail] Parsing error: $e'); onError?.call('Login fail parsing error: $e'); } } @@ -864,20 +864,20 @@ class BleResponseHandler { final statusData = result['statusData'] as Uint8List; final statusText = utf8.decode(statusData, allowMalformed: true); if (statusText.isNotEmpty && _isPrintableAscii(statusText)) { - print(' Status data (text): $statusText'); + debugPrint(' Status data (text): $statusText'); } } catch (e) { // Not text data } - print(' โœ… [StatusResponse] Received status response'); + debugPrint(' โœ… [StatusResponse] Received status response'); onStatusResponse?.call( result['publicKeyPrefix'] as Uint8List, result['statusData'] as Uint8List, ); } } catch (e) { - print(' โŒ [StatusResponse] Parsing error: $e'); + debugPrint(' โŒ [StatusResponse] Parsing error: $e'); onError?.call('Status response parsing error: $e'); } } @@ -902,11 +902,11 @@ class BleResponseHandler { if (deviceTime != null) { final appTime = DateTime.now().millisecondsSinceEpoch ~/ 1000; final drift = appTime - deviceTime; - print(' Clock drift: $drift seconds'); + debugPrint(' Clock drift: $drift seconds'); } - print(' โœ… [CurrentTime] Parsed successfully'); + debugPrint(' โœ… [CurrentTime] Parsed successfully'); } catch (e) { - print(' โŒ [CurrentTime] Parsing error: $e'); + debugPrint(' โŒ [CurrentTime] Parsing error: $e'); onError?.call('CurrentTime parsing error: $e'); } } @@ -922,9 +922,9 @@ class BleResponseHandler { result['totalKb'] as int?, ); } - print(' โœ… [BatteryAndStorage] Parsed successfully'); + debugPrint(' โœ… [BatteryAndStorage] Parsed successfully'); } catch (e) { - print(' โŒ [BatteryAndStorage] Parsing error: $e'); + debugPrint(' โŒ [BatteryAndStorage] Parsing error: $e'); onError?.call('BatteryAndStorage parsing error: $e'); } } @@ -937,11 +937,11 @@ class BleResponseHandler { final channelIdx = info['channelIdx'] as int; final channelName = info['channelName'] as String; - print(' โœ… [ChannelInfo] Channel $channelIdx: "${channelName}"'); + debugPrint(' โœ… [ChannelInfo] Channel $channelIdx: "${channelName}"'); onChannelInfoReceived?.call(channelIdx, channelName); } } catch (e) { - print(' โŒ [ChannelInfo] Parsing error: $e'); + debugPrint(' โŒ [ChannelInfo] Parsing error: $e'); onError?.call('ChannelInfo parsing error: $e'); } } @@ -952,7 +952,7 @@ class BleResponseHandler { final errorCode = FrameParser.parseError(reader); if (errorCode != null) { final errorMsg = FrameParser.getErrorMessage(errorCode); - print(' โŒ [Error] $errorMsg'); + debugPrint(' โŒ [Error] $errorMsg'); // Complete any pending ACK command with error _commandQueue?.completeCommandWithError( @@ -963,14 +963,14 @@ class BleResponseHandler { // Special handling for ERR_CODE_NOT_FOUND (2) - contact not in radio if (errorCode == 2) { // ERR_CODE_NOT_FOUND - print(' โš ๏ธ [Error] Contact not found in radio - attempting auto-recovery'); + debugPrint(' โš ๏ธ [Error] Contact not found in radio - attempting auto-recovery'); onContactNotFound?.call(_lastContactPublicKey); } onError?.call(errorMsg, errorCode: errorCode); } } catch (e) { - print(' โŒ [Error] Parsing error: $e'); + debugPrint(' โŒ [Error] Parsing error: $e'); } } diff --git a/lib/services/cayenne_lpp_parser.dart b/lib/services/cayenne_lpp_parser.dart index a489bcf..4cc3668 100644 --- a/lib/services/cayenne_lpp_parser.dart +++ b/lib/services/cayenne_lpp_parser.dart @@ -1,4 +1,5 @@ import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:latlong2/latlong.dart'; import '../models/contact_telemetry.dart'; import 'buffer_reader.dart'; @@ -9,9 +10,9 @@ import 'meshcore_constants.dart'; class CayenneLppParser { /// Parse Cayenne LPP data into ContactTelemetry static ContactTelemetry parse(Uint8List data) { - print(' [CayenneLPP] Parsing LPP data...'); - print(' Data length: ${data.length} bytes'); - print(' Data (hex): ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); + debugPrint(' [CayenneLPP] Parsing LPP data...'); + debugPrint(' Data length: ${data.length} bytes'); + debugPrint(' Data (hex): ${data.map((b) => b.toRadixString(16).padLeft(2, '0')).join(' ')}'); final reader = BufferReader(data); @@ -27,106 +28,106 @@ class CayenneLppParser { while (reader.hasRemaining) { try { fieldCount++; - print(' [Field $fieldCount] Position: ${data.length - reader.remainingBytesCount}'); + debugPrint(' [Field $fieldCount] Position: ${data.length - reader.remainingBytesCount}'); final channel = reader.readByte(); - print(' Channel: $channel'); + debugPrint(' Channel: $channel'); final type = reader.readByte(); - print(' Type: $type (0x${type.toRadixString(16).padLeft(2, '0')})'); + debugPrint(' Type: $type (0x${type.toRadixString(16).padLeft(2, '0')})'); switch (type) { case MeshCoreConstants.lppDigitalInput: final value = reader.readByte(); - print(' Digital Input: $value'); + debugPrint(' Digital Input: $value'); extraSensorData['digital_input_$channel'] = value; break; case MeshCoreConstants.lppDigitalOutput: final value = reader.readByte(); - print(' Digital Output: $value'); + debugPrint(' Digital Output: $value'); extraSensorData['digital_output_$channel'] = value; break; case MeshCoreConstants.lppAnalogInput: final rawValue = reader.readInt16BE(); final value = rawValue / 100.0; - print(' Analog Input (raw): $rawValue'); - print(' Analog Input (volts): ${value}V'); + debugPrint(' Analog Input (raw): $rawValue'); + debugPrint(' Analog Input (volts): ${value}V'); extraSensorData['analog_input_$channel'] = value; // If this is a battery reading if (channel == 0 || channel == 1) { batteryMilliVolts = value * 1000; batteryPercentage = _calculateBatteryPercentage(value); - print(' โ†’ Battery: ${batteryPercentage?.toStringAsFixed(1)}% (${batteryMilliVolts?.toStringAsFixed(0)}mV)'); + debugPrint(' โ†’ Battery: ${batteryPercentage?.toStringAsFixed(1)}% (${batteryMilliVolts?.toStringAsFixed(0)}mV)'); } break; case MeshCoreConstants.lppAnalogOutput: final rawValue = reader.readInt16BE(); final value = rawValue / 100.0; - print(' Analog Output (raw): $rawValue'); - print(' Analog Output (volts): ${value}V'); + debugPrint(' Analog Output (raw): $rawValue'); + debugPrint(' Analog Output (volts): ${value}V'); extraSensorData['analog_output_$channel'] = value; break; case MeshCoreConstants.lppIlluminanceSensor: final value = reader.readUInt16BE(); - print(' Illuminance: $value lux'); + debugPrint(' Illuminance: $value lux'); extraSensorData['illuminance_$channel'] = value; break; case MeshCoreConstants.lppPresenceSensor: final value = reader.readByte(); - print(' Presence: $value'); + debugPrint(' Presence: $value'); extraSensorData['presence_$channel'] = value; break; case MeshCoreConstants.lppTemperatureSensor: final rawValue = reader.readInt16BE(); temperature = rawValue / 10.0; - print(' Temperature (raw): $rawValue'); - print(' Temperature: ${temperature?.toStringAsFixed(1)}ยฐC'); + debugPrint(' Temperature (raw): $rawValue'); + debugPrint(' Temperature: ${temperature?.toStringAsFixed(1)}ยฐC'); break; case MeshCoreConstants.lppHumiditySensor: final rawValue = reader.readByte(); humidity = rawValue / 2.0; - print(' Humidity (raw): $rawValue'); - print(' Humidity: ${humidity?.toStringAsFixed(1)}%'); + debugPrint(' Humidity (raw): $rawValue'); + debugPrint(' Humidity: ${humidity?.toStringAsFixed(1)}%'); break; case MeshCoreConstants.lppAccelerometer: final x = reader.readInt16BE() / 1000.0; final y = reader.readInt16BE() / 1000.0; final z = reader.readInt16BE() / 1000.0; - print(' Accelerometer: x=$x, y=$y, z=$z'); + debugPrint(' Accelerometer: x=$x, y=$y, z=$z'); extraSensorData['accelerometer_$channel'] = {'x': x, 'y': y, 'z': z}; break; case MeshCoreConstants.lppBarometer: final rawValue = reader.readUInt16BE(); pressure = rawValue / 10.0; - print(' Barometer (raw): $rawValue'); - print(' Barometer: ${pressure?.toStringAsFixed(1)} hPa'); + debugPrint(' Barometer (raw): $rawValue'); + debugPrint(' Barometer: ${pressure?.toStringAsFixed(1)} hPa'); break; case MeshCoreConstants.lppVoltageSensor: final rawValue = reader.readUInt16BE(); final value = rawValue / 100.0; - print(' Voltage (raw): $rawValue'); - print(' Voltage: ${value}V'); + debugPrint(' Voltage (raw): $rawValue'); + debugPrint(' Voltage: ${value}V'); // Treat voltage sensor as battery reading batteryMilliVolts = value * 1000; batteryPercentage = _calculateBatteryPercentage(value); - print(' โ†’ Battery: ${batteryPercentage?.toStringAsFixed(1)}% (${batteryMilliVolts?.toStringAsFixed(0)}mV)'); + debugPrint(' โ†’ Battery: ${batteryPercentage?.toStringAsFixed(1)}% (${batteryMilliVolts?.toStringAsFixed(0)}mV)'); break; case MeshCoreConstants.lppGyrometer: final x = reader.readInt16BE() / 100.0; final y = reader.readInt16BE() / 100.0; final z = reader.readInt16BE() / 100.0; - print(' Gyrometer: x=$x, y=$y, z=$z'); + debugPrint(' Gyrometer: x=$x, y=$y, z=$z'); extraSensorData['gyrometer_$channel'] = {'x': x, 'y': y, 'z': z}; break; @@ -137,30 +138,30 @@ class CayenneLppParser { final lat = rawLat / 1000000.0; final lon = rawLon / 1000000.0; final alt = rawAlt / 100.0; - print(' GPS Location (raw): lat=$rawLat, lon=$rawLon, alt=$rawAlt'); - print(' GPS Location: ${lat}ยฐ, ${lon}ยฐ, altitude=${alt}m'); + debugPrint(' GPS Location (raw): lat=$rawLat, lon=$rawLon, alt=$rawAlt'); + debugPrint(' GPS Location: ${lat}ยฐ, ${lon}ยฐ, altitude=${alt}m'); gpsLocation = LatLng(lat, lon); extraSensorData['altitude_$channel'] = alt; break; default: - print(' โš ๏ธ Unknown type, skipping remaining ${reader.remainingBytesCount} bytes'); + debugPrint(' โš ๏ธ Unknown type, skipping remaining ${reader.remainingBytesCount} bytes'); // Unknown type, skip remaining to avoid parsing errors reader.skip(reader.remainingBytesCount); break; } } catch (e) { - print(' โŒ Parsing error: $e'); + debugPrint(' โŒ Parsing error: $e'); // If we encounter a parsing error, break and return what we have break; } } - print(' Parsed $fieldCount fields'); - print(' โœ… [CayenneLPP] Parsing complete'); - print(' GPS: ${gpsLocation != null ? '${gpsLocation.latitude}ยฐ, ${gpsLocation.longitude}ยฐ' : 'none'}'); - print(' Battery: ${batteryPercentage != null ? '${batteryPercentage.toStringAsFixed(1)}%' : 'none'}'); - print(' Temperature: ${temperature != null ? '${temperature.toStringAsFixed(1)}ยฐC' : 'none'}'); + debugPrint(' Parsed $fieldCount fields'); + debugPrint(' โœ… [CayenneLPP] Parsing complete'); + debugPrint(' GPS: ${gpsLocation != null ? '${gpsLocation.latitude}ยฐ, ${gpsLocation.longitude}ยฐ' : 'none'}'); + debugPrint(' Battery: ${batteryPercentage != null ? '${batteryPercentage.toStringAsFixed(1)}%' : 'none'}'); + debugPrint(' Temperature: ${temperature != null ? '${temperature.toStringAsFixed(1)}ยฐC' : 'none'}'); // IMPORTANT: Cayenne LPP format does NOT include a timestamp field. // We use DateTime.now() as the timestamp, which represents when the data @@ -172,7 +173,7 @@ class CayenneLppParser { // - The actual age of the telemetry data cannot be determined from the LPP format // - Devices may cache telemetry for hours and send it later when requested final parseTimestamp = DateTime.now(); - print(' Timestamp: $parseTimestamp (parse time, NOT device collection time)'); + debugPrint(' Timestamp: $parseTimestamp (parse time, NOT device collection time)'); return ContactTelemetry( gpsLocation: gpsLocation, diff --git a/lib/services/contact_storage_service.dart b/lib/services/contact_storage_service.dart index 6681df7..f12d7f7 100644 --- a/lib/services/contact_storage_service.dart +++ b/lib/services/contact_storage_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/contact.dart'; import '../models/contact_telemetry.dart'; @@ -26,9 +27,9 @@ class ContactStorageService { final jsonString = jsonEncode(limitedList); await prefs.setString(_contactsKey, jsonString); - print('โœ… [ContactStorage] Saved ${limitedList.length} contacts to storage'); + debugPrint('โœ… [ContactStorage] Saved ${limitedList.length} contacts to storage'); } catch (e) { - print('โŒ [ContactStorage] Error saving contacts: $e'); + debugPrint('โŒ [ContactStorage] Error saving contacts: $e'); } } @@ -40,7 +41,7 @@ class ContactStorageService { final jsonString = prefs.getString(_contactsKey); if (jsonString == null || jsonString.isEmpty) { - print('โ„น๏ธ [ContactStorage] No stored contacts found'); + debugPrint('โ„น๏ธ [ContactStorage] No stored contacts found'); return []; } @@ -56,17 +57,17 @@ class ContactStorageService { ? contacts.where((contact) { final matches = _publicKeysMatch(contact.publicKey, excludePublicKey); if (matches) { - print('โ„น๏ธ [ContactStorage] Excluding contact with matching public key: ${contact.advName}'); + debugPrint('โ„น๏ธ [ContactStorage] Excluding contact with matching public key: ${contact.advName}'); } return !matches; }).toList() : contacts; - print('โœ… [ContactStorage] Loaded ${filteredContacts.length} contacts from storage' + debugPrint('โœ… [ContactStorage] Loaded ${filteredContacts.length} contacts from storage' '${excludePublicKey != null ? ' (${contacts.length - filteredContacts.length} excluded)' : ''}'); return filteredContacts; } catch (e) { - print('โŒ [ContactStorage] Error loading contacts: $e'); + debugPrint('โŒ [ContactStorage] Error loading contacts: $e'); return []; } } @@ -85,9 +86,9 @@ class ContactStorageService { try { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_contactsKey); - print('โœ… [ContactStorage] Cleared all stored contacts'); + debugPrint('โœ… [ContactStorage] Cleared all stored contacts'); } catch (e) { - print('โŒ [ContactStorage] Error clearing contacts: $e'); + debugPrint('โŒ [ContactStorage] Error clearing contacts: $e'); } } @@ -114,7 +115,7 @@ class ContactStorageService { 'storageSizeKB': (sizeBytes / 1024).toStringAsFixed(2), }; } catch (e) { - print('โŒ [ContactStorage] Error getting storage stats: $e'); + debugPrint('โŒ [ContactStorage] Error getting storage stats: $e'); return { 'contactCount': 0, 'storageSizeBytes': 0, @@ -159,7 +160,7 @@ class ContactStorageService { : null, ); } catch (e) { - print('โŒ [ContactStorage] Error parsing contact from JSON: $e'); + debugPrint('โŒ [ContactStorage] Error parsing contact from JSON: $e'); return null; } } @@ -203,7 +204,7 @@ class ContactStorageService { extraSensorData: json['extraSensorData'] as Map?, ); } catch (e) { - print('โŒ [ContactStorage] Error parsing telemetry from JSON: $e'); + debugPrint('โŒ [ContactStorage] Error parsing telemetry from JSON: $e'); return null; } } diff --git a/lib/services/meshcore_ble_service.dart b/lib/services/meshcore_ble_service.dart index 5ba6968..9a648ca 100644 --- a/lib/services/meshcore_ble_service.dart +++ b/lib/services/meshcore_ble_service.dart @@ -89,7 +89,7 @@ class MeshCoreBleService { onError?.call(error); }; _connectionManager.onReconnectionAttempt = (attemptNumber, maxAttempts) { - print('๐Ÿ”„ [Service] Reconnection attempt $attemptNumber/$maxAttempts'); + debugPrint('๐Ÿ”„ [Service] Reconnection attempt $attemptNumber/$maxAttempts'); onReconnectionAttempt?.call(attemptNumber, maxAttempts); }; _connectionManager.onRssiUpdate = (rssi) { @@ -218,10 +218,10 @@ class MeshCoreBleService { // Send initial device query and wait for responses await _sendDeviceQuery(); - print('โœ… [Service] Device initialization complete'); + debugPrint('โœ… [Service] Device initialization complete'); return true; } catch (e) { - print('โŒ [Service] Device initialization failed: $e'); + debugPrint('โŒ [Service] Device initialization failed: $e'); // Disconnect on initialization failure await disconnect(); onError?.call('Device initialization failed: $e'); @@ -240,28 +240,28 @@ class MeshCoreBleService { Future _sendDeviceQuery() async { // STEP 1: Send device query FIRST to get device capabilities // This is the first command to send per protocol documentation - print('๐Ÿ” [Service] Querying device information (CMD_DEVICE_QUERY)...'); + debugPrint('๐Ÿ” [Service] Querying device information (CMD_DEVICE_QUERY)...'); final deviceInfo = await _commandSender.writeDataAndWaitForResponse>( FrameBuilder.buildDeviceQuery(), MeshCoreConstants.respDeviceInfo, ); - print('โœ… [Service] Device info received: firmware=${deviceInfo['firmwareVersion']}'); + debugPrint('โœ… [Service] Device info received: firmware=${deviceInfo['firmwareVersion']}'); // STEP 2: Send app start to initialize the app session // This is the first command after connection per protocol documentation - print('๐Ÿš€ [Service] Sending app start (CMD_APP_START)...'); + debugPrint('๐Ÿš€ [Service] Sending app start (CMD_APP_START)...'); final selfInfo = await _commandSender.writeDataAndWaitForResponse>( FrameBuilder.buildAppStart(), MeshCoreConstants.respSelfInfo, ); - print('โœ… [Service] Self info received: node initialized'); + debugPrint('โœ… [Service] Self info received: node initialized'); // STEP 3: Set device clock AFTER initialization // This ensures the device has correct timestamps for all subsequent operations // Note: This command does not return an ACK, so we use writeData (fire-and-forget) - print('โฐ [Service] Setting device clock (CMD_SET_DEVICE_TIME)...'); + debugPrint('โฐ [Service] Setting device clock (CMD_SET_DEVICE_TIME)...'); await _commandSender.writeData(FrameBuilder.buildSetDeviceTime()); - print('โœ… [Service] Device clock sent (no ACK expected)'); + debugPrint('โœ… [Service] Device clock sent (no ACK expected)'); } /// Refresh device info (public method) @@ -276,14 +276,14 @@ class MeshCoreBleService { /// Manually add or update a contact on the companion radio Future addOrUpdateContact(Contact contact) async { - print('๐Ÿ“ [BLE] Adding/updating contact on companion radio:'); - print(' Name: ${contact.advName}'); - print(' Public key prefix: ${contact.publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); - print(' Type: ${contact.type} (${contact.type.value})'); + debugPrint('๐Ÿ“ [BLE] Adding/updating contact on companion radio:'); + debugPrint(' Name: ${contact.advName}'); + debugPrint(' Public key prefix: ${contact.publicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint(' Type: ${contact.type} (${contact.type.value})'); await _commandSender.writeData(FrameBuilder.buildAddUpdateContact(contact)); - print('โœ… [BLE] CMD_ADD_UPDATE_CONTACT sent'); + debugPrint('โœ… [BLE] CMD_ADD_UPDATE_CONTACT sent'); } /// Send text message to contact (DM) @@ -443,9 +443,9 @@ class MeshCoreBleService { throw ArgumentError('Password exceeds 15 character limit'); } - print('๐Ÿ” [BLE] Preparing login request:'); - print(' Room public key prefix: ${roomPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); - print(' Password: ${"*" * password.length} (${password.length} chars)'); + debugPrint('๐Ÿ” [BLE] Preparing login request:'); + debugPrint(' Room public key prefix: ${roomPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint(' Password: ${"*" * password.length} (${password.length} chars)'); await _commandSender.writeData(FrameBuilder.buildSendLogin( roomPublicKey: roomPublicKey, @@ -455,27 +455,27 @@ class MeshCoreBleService { /// Send status request to repeater or sensor node Future sendStatusRequest(Uint8List contactPublicKey) async { - print('๐Ÿ“Š [BLE] Preparing status request:'); - print(' Target node public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint('๐Ÿ“Š [BLE] Preparing status request:'); + debugPrint(' Target node public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); await _commandSender.writeData(FrameBuilder.buildSendStatusReq(contactPublicKey)); } /// Reset path for a contact - forces next message to flood and re-learn route Future resetPath(Uint8List contactPublicKey) async { - print('๐Ÿ”„ [BLE] Resetting path for contact:'); - print(' Contact public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint('๐Ÿ”„ [BLE] Resetting path for contact:'); + debugPrint(' Contact public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); await _commandSender.writeData(FrameBuilder.buildResetPath(contactPublicKey)); } /// Remove a contact from the companion radio Future removeContact(Uint8List contactPublicKey) async { - print('๐Ÿ—‘๏ธ [BLE] Removing contact from companion radio:'); - print(' Public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint('๐Ÿ—‘๏ธ [BLE] Removing contact from companion radio:'); + debugPrint(' Public key prefix: ${contactPublicKey.sublist(0, 6).map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); await _commandSender.writeData(FrameBuilder.buildRemoveContact(contactPublicKey)); - print('โœ… [BLE] CMD_REMOVE_CONTACT sent'); + debugPrint('โœ… [BLE] CMD_REMOVE_CONTACT sent'); } /// Get information for a specific channel @@ -488,21 +488,21 @@ class MeshCoreBleService { required int channelIdx, required String channelName, }) async { - print('๐Ÿ“ป [BLE] Setting channel name:'); - print(' Channel index: $channelIdx'); - print(' Channel name: $channelName'); + debugPrint('๐Ÿ“ป [BLE] Setting channel name:'); + debugPrint(' Channel index: $channelIdx'); + debugPrint(' Channel name: $channelName'); await _commandSender.writeDataAndWaitForAck(FrameBuilder.buildSetChannel( channelIdx: channelIdx, channelName: channelName, )); - print('โœ… [BLE] CMD_SET_CHANNEL sent'); + debugPrint('โœ… [BLE] CMD_SET_CHANNEL sent'); } /// Sync all channels from the device (typically 0-39) /// This queries each channel to get its name and metadata Future syncAllChannels({int maxChannels = 40}) async { - print('๐Ÿ“ป [Service] Syncing channels (0-${maxChannels - 1})...'); + debugPrint('๐Ÿ“ป [Service] Syncing channels (0-${maxChannels - 1})...'); for (int i = 0; i < maxChannels; i++) { await getChannel(i); @@ -510,7 +510,7 @@ class MeshCoreBleService { await Future.delayed(const Duration(milliseconds: 50)); } - print('โœ… [Service] Channel sync complete'); + debugPrint('โœ… [Service] Channel sync complete'); } /// Clear packet logs diff --git a/lib/services/message_storage_service.dart b/lib/services/message_storage_service.dart index 1a0a024..a643af4 100644 --- a/lib/services/message_storage_service.dart +++ b/lib/services/message_storage_service.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../models/message.dart'; import '../models/sar_marker.dart'; @@ -26,9 +27,9 @@ class MessageStorageService { final jsonString = jsonEncode(limitedList); await prefs.setString(_messagesKey, jsonString); - print('โœ… [MessageStorage] Saved ${limitedList.length} messages to storage'); + debugPrint('โœ… [MessageStorage] Saved ${limitedList.length} messages to storage'); } catch (e) { - print('โŒ [MessageStorage] Error saving messages: $e'); + debugPrint('โŒ [MessageStorage] Error saving messages: $e'); } } @@ -39,7 +40,7 @@ class MessageStorageService { final jsonString = prefs.getString(_messagesKey); if (jsonString == null || jsonString.isEmpty) { - print('โ„น๏ธ [MessageStorage] No stored messages found'); + debugPrint('โ„น๏ธ [MessageStorage] No stored messages found'); return []; } @@ -50,10 +51,10 @@ class MessageStorageService { .cast() .toList(); - print('โœ… [MessageStorage] Loaded ${messages.length} messages from storage'); + debugPrint('โœ… [MessageStorage] Loaded ${messages.length} messages from storage'); return messages; } catch (e) { - print('โŒ [MessageStorage] Error loading messages: $e'); + debugPrint('โŒ [MessageStorage] Error loading messages: $e'); return []; } } @@ -63,9 +64,9 @@ class MessageStorageService { try { final prefs = await SharedPreferences.getInstance(); await prefs.remove(_messagesKey); - print('โœ… [MessageStorage] Cleared all stored messages'); + debugPrint('โœ… [MessageStorage] Cleared all stored messages'); } catch (e) { - print('โŒ [MessageStorage] Error clearing messages: $e'); + debugPrint('โŒ [MessageStorage] Error clearing messages: $e'); } } @@ -92,7 +93,7 @@ class MessageStorageService { 'storageSizeKB': (sizeBytes / 1024).toStringAsFixed(2), }; } catch (e) { - print('โŒ [MessageStorage] Error getting storage stats: $e'); + debugPrint('โŒ [MessageStorage] Error getting storage stats: $e'); return { 'messageCount': 0, 'storageSizeBytes': 0, @@ -184,7 +185,7 @@ class MessageStorageService { isRead: json['isRead'] as bool? ?? false, ); } catch (e) { - print('โŒ [MessageStorage] Error parsing message from JSON: $e'); + debugPrint('โŒ [MessageStorage] Error parsing message from JSON: $e'); return null; } } diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index 16d57d6..63062b2 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -32,7 +32,7 @@ class NotificationService { if (_isInitialized) return; try { - print('๐Ÿ“ฌ [NotificationService] Initializing...'); + debugPrint('๐Ÿ“ฌ [NotificationService] Initializing...'); // Initialize timezone data tz.initializeTimeZones(); @@ -67,10 +67,10 @@ class NotificationService { await _createNotificationChannels(); _isInitialized = true; - print('โœ… [NotificationService] Initialized successfully'); - print(' Permission granted: $_permissionGranted'); + debugPrint('โœ… [NotificationService] Initialized successfully'); + debugPrint(' Permission granted: $_permissionGranted'); } catch (e) { - print('โŒ [NotificationService] Initialization error: $e'); + debugPrint('โŒ [NotificationService] Initialization error: $e'); } } @@ -88,7 +88,7 @@ class NotificationService { critical: true, // Request critical alert permission for urgent SAR notifications ); _permissionGranted = granted ?? false; - print('๐Ÿ“ฑ [NotificationService] iOS permissions granted: $_permissionGranted'); + debugPrint('๐Ÿ“ฑ [NotificationService] iOS permissions granted: $_permissionGranted'); } // Android 13+ permissions @@ -97,10 +97,10 @@ class NotificationService { if (androidPlugin != null) { final granted = await androidPlugin.requestNotificationsPermission(); _permissionGranted = granted ?? false; - print('๐Ÿค– [NotificationService] Android permissions granted: $_permissionGranted'); + debugPrint('๐Ÿค– [NotificationService] Android permissions granted: $_permissionGranted'); } } catch (e) { - print('โš ๏ธ [NotificationService] Error requesting permissions: $e'); + debugPrint('โš ๏ธ [NotificationService] Error requesting permissions: $e'); } } @@ -126,15 +126,15 @@ class NotificationService { ); await androidPlugin.createNotificationChannel(urgentChannel); - print('โœ… [NotificationService] Created urgent notification channel'); + debugPrint('โœ… [NotificationService] Created urgent notification channel'); } catch (e) { - print('โš ๏ธ [NotificationService] Error creating channels: $e'); + debugPrint('โš ๏ธ [NotificationService] Error creating channels: $e'); } } /// Handle notification tap (foreground) void _onNotificationResponse(NotificationResponse response) { - print('๐Ÿ”” [NotificationService] Notification tapped: ${response.payload}'); + debugPrint('๐Ÿ”” [NotificationService] Notification tapped: ${response.payload}'); // TODO: Navigate to map tab and show SAR marker // This would require a callback to the app layer } @@ -148,12 +148,12 @@ class NotificationService { AppLocalizations? localizations, }) async { if (!_isInitialized) { - print('โš ๏ธ [NotificationService] Not initialized, skipping notification'); + debugPrint('โš ๏ธ [NotificationService] Not initialized, skipping notification'); return; } if (!_permissionGranted) { - print('โš ๏ธ [NotificationService] Permission not granted, skipping notification'); + debugPrint('โš ๏ธ [NotificationService] Permission not granted, skipping notification'); return; } @@ -222,12 +222,12 @@ class NotificationService { payload: 'sar:${type.name}:$coordinates', ); - print('โœ… [NotificationService] Showed SAR notification: $title'); - print(' Type: ${type.displayName}'); - print(' Sender: $senderName'); - print(' Coordinates: $coordinates'); + debugPrint('โœ… [NotificationService] Showed SAR notification: $title'); + debugPrint(' Type: ${type.displayName}'); + debugPrint(' Sender: $senderName'); + debugPrint(' Coordinates: $coordinates'); } catch (e) { - print('โŒ [NotificationService] Error showing notification: $e'); + debugPrint('โŒ [NotificationService] Error showing notification: $e'); } } @@ -307,9 +307,9 @@ class NotificationService { Future cancelAll() async { try { await _notificationsPlugin.cancelAll(); - print('โœ… [NotificationService] Cancelled all notifications'); + debugPrint('โœ… [NotificationService] Cancelled all notifications'); } catch (e) { - print('โŒ [NotificationService] Error canceling notifications: $e'); + debugPrint('โŒ [NotificationService] Error canceling notifications: $e'); } } @@ -317,9 +317,9 @@ class NotificationService { Future cancel(int id) async { try { await _notificationsPlugin.cancel(id); - print('โœ… [NotificationService] Cancelled notification: $id'); + debugPrint('โœ… [NotificationService] Cancelled notification: $id'); } catch (e) { - print('โŒ [NotificationService] Error canceling notification: $e'); + debugPrint('โŒ [NotificationService] Error canceling notification: $e'); } } @@ -336,7 +336,7 @@ class NotificationService { // For iOS, assume enabled if permission was granted return _permissionGranted; } catch (e) { - print('โš ๏ธ [NotificationService] Error checking notification status: $e'); + debugPrint('โš ๏ธ [NotificationService] Error checking notification status: $e'); return false; } } @@ -346,7 +346,7 @@ class NotificationService { try { return await _notificationsPlugin.pendingNotificationRequests(); } catch (e) { - print('โš ๏ธ [NotificationService] Error getting pending notifications: $e'); + debugPrint('โš ๏ธ [NotificationService] Error getting pending notifications: $e'); return []; } } diff --git a/lib/services/tile_cache_service.dart b/lib/services/tile_cache_service.dart index 8f447e3..4eabe17 100644 --- a/lib/services/tile_cache_service.dart +++ b/lib/services/tile_cache_service.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; import 'package:flutter_map_tile_caching/custom_backend_api.dart'; @@ -92,7 +93,7 @@ class TileCacheService { // Use attemptedTilesCount instead of successfulTilesCount // attemptedTilesCount includes successful + buffered + skipped tiles final percentage = progress.percentageProgress; - print( + debugPrint( 'Download progress: ${progress.attemptedTilesCount}/${progress.maxTilesCount} = ${percentage.toStringAsFixed(1)}% (successful: ${progress.successfulTilesCount}, buffered: ${progress.bufferedTilesCount}, skipped: ${progress.skippedTilesCount})', ); onProgress(percentage); @@ -167,7 +168,7 @@ class TileCacheService { silenceTileNotFound: true, ); } catch (e) { - print('Error creating vector tile provider: $e'); + debugPrint('Error creating vector tile provider: $e'); return null; } } diff --git a/lib/utils/debug_print.dart b/lib/utils/debug_print.dart index 651a1d4..2034687 100644 --- a/lib/utils/debug_print.dart +++ b/lib/utils/debug_print.dart @@ -3,6 +3,6 @@ import 'package:flutter/foundation.dart'; /// Debug print that only outputs in debug builds void debugPrint(Object? message) { if (kDebugMode) { - print(message); + debugPrint(message); } } diff --git a/lib/widgets/contacts/room_login_sheet.dart b/lib/widgets/contacts/room_login_sheet.dart index 8a4ed3e..0cb5882 100644 --- a/lib/widgets/contacts/room_login_sheet.dart +++ b/lib/widgets/contacts/room_login_sheet.dart @@ -86,29 +86,29 @@ class _RoomLoginSheetState extends State { }); // ๐Ÿ• CLOCK DRIFT CHECK: Get device time to detect synchronization issues - print('๐Ÿ• [RoomLogin] Checking for clock drift between app and radio...'); + debugPrint('๐Ÿ• [RoomLogin] Checking for clock drift between app and radio...'); try { await connectionProvider.getDeviceTime(); // Give time for response to be logged await Future.delayed(const Duration(milliseconds: 300)); } catch (e) { - print('โš ๏ธ [RoomLogin] Failed to get device time: $e'); + debugPrint('โš ๏ธ [RoomLogin] Failed to get device time: $e'); // Don't fail login - this is just a diagnostic check } // ๐Ÿ” PRE-LOGIN CHECK: Ensure room contact exists in device - print('๐Ÿ” [RoomLogin] Checking if room "${widget.contact.advName}" exists in contacts...'); - print(' Target public key prefix: ${widget.contact.publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); + debugPrint('๐Ÿ” [RoomLogin] Checking if room "${widget.contact.advName}" exists in contacts...'); + debugPrint(' Target public key prefix: ${widget.contact.publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')}'); // Check if the room exists in our local contacts bool roomExists = contactsProvider.rooms.any( (room) => room.publicKeyHex == widget.contact.publicKeyHex, ); - print(' Local contact list: ${roomExists ? "โœ… Found" : "โŒ Not found"}'); + debugPrint(' Local contact list: ${roomExists ? "โœ… Found" : "โŒ Not found"}'); if (!roomExists) { - print('โš ๏ธ [RoomLogin] Room not in local contacts - syncing with device...'); + debugPrint('โš ๏ธ [RoomLogin] Room not in local contacts - syncing with device...'); try { // Sync contacts from device @@ -122,26 +122,26 @@ class _RoomLoginSheetState extends State { (room) => room.publicKeyHex == widget.contact.publicKeyHex, ); - print(' After sync: ${roomExists ? "โœ… Found" : "โŒ Still not found"}'); + debugPrint(' After sync: ${roomExists ? "โœ… Found" : "โŒ Still not found"}'); if (!roomExists) { // Room still doesn't exist on the device - try to add it manually - print('โŒ [RoomLogin] Room still not found after sync'); - print('๐Ÿ”ง [RoomLogin] Attempting to add room contact to companion radio...'); + debugPrint('โŒ [RoomLogin] Room still not found after sync'); + debugPrint('๐Ÿ”ง [RoomLogin] Attempting to add room contact to companion radio...'); try { // Manually add the room contact to the radio's flash storage await connectionProvider.addOrUpdateContact(widget.contact); - print('โœ… [RoomLogin] Room contact added via CMD_ADD_UPDATE_CONTACT'); - print(' Waiting 500ms for radio to save to flash...'); + debugPrint('โœ… [RoomLogin] Room contact added via CMD_ADD_UPDATE_CONTACT'); + debugPrint(' Waiting 500ms for radio to save to flash...'); // Give the radio time to save the contact to flash await Future.delayed(const Duration(milliseconds: 500)); - print('โœ… [RoomLogin] Room contact should now be available - proceeding with login'); + debugPrint('โœ… [RoomLogin] Room contact should now be available - proceeding with login'); } catch (e) { - print('โŒ [RoomLogin] Failed to add room contact: $e'); + debugPrint('โŒ [RoomLogin] Failed to add room contact: $e'); if (!mounted) return; @@ -159,18 +159,18 @@ class _RoomLoginSheetState extends State { // Log available rooms for debugging final availableRooms = contactsProvider.rooms; - print('๐Ÿ“‹ [RoomLogin] Available rooms on device (${availableRooms.length}):'); + debugPrint('๐Ÿ“‹ [RoomLogin] Available rooms on device (${availableRooms.length}):'); for (final room in availableRooms) { - print(' - ${room.advName} (${room.publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')})'); + debugPrint(' - ${room.advName} (${room.publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':')})'); } return; } } - print('โœ… [RoomLogin] Room contact found after sync - proceeding with login'); + debugPrint('โœ… [RoomLogin] Room contact found after sync - proceeding with login'); } catch (e) { - print('โŒ [RoomLogin] Contact sync failed: $e'); + debugPrint('โŒ [RoomLogin] Contact sync failed: $e'); if (!mounted) return; @@ -187,7 +187,7 @@ class _RoomLoginSheetState extends State { return; } } else { - print('โœ… [RoomLogin] Room contact found in local contacts - proceeding with login'); + debugPrint('โœ… [RoomLogin] Room contact found in local contacts - proceeding with login'); } // Save password before sending @@ -205,9 +205,9 @@ class _RoomLoginSheetState extends State { connectionProvider.onLoginSuccess = originalOnSuccess; connectionProvider.onLoginFail = originalOnFail; - print('โœ… [RoomLogin] Login successful! Tag: $tag, Permissions: $permissions, Admin: $isAdmin'); - print('๐Ÿ“ก [RoomLogin] Room server will now push messages automatically via PUSH_CODE_MSG_WAITING'); - print(' Messages will be fetched when onMessageWaiting callback is triggered'); + debugPrint('โœ… [RoomLogin] Login successful! Tag: $tag, Permissions: $permissions, Admin: $isAdmin'); + debugPrint('๐Ÿ“ก [RoomLogin] Room server will now push messages automatically via PUSH_CODE_MSG_WAITING'); + debugPrint(' Messages will be fetched when onMessageWaiting callback is triggered'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( @@ -225,7 +225,7 @@ class _RoomLoginSheetState extends State { connectionProvider.onLoginSuccess = originalOnSuccess; connectionProvider.onLoginFail = originalOnFail; - print('โŒ [RoomLogin] Login failed - incorrect password'); + debugPrint('โŒ [RoomLogin] Login failed - incorrect password'); if (mounted) { ScaffoldMessenger.of(context).showSnackBar( diff --git a/pubspec.lock b/pubspec.lock index 9ff4baf..77265c1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -294,11 +294,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.8.1" - flutter_driver: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" flutter_launcher_icons: dependency: "direct dev" description: @@ -378,11 +373,6 @@ packages: description: flutter source: sdk version: "0.0.0" - fuchsia_remote_debug_protocol: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" geoclue: dependency: transitive description: @@ -487,11 +477,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.4" - integration_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" intl: dependency: "direct main" description: @@ -788,14 +773,6 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.3" - process: - dependency: transitive - description: - name: process - sha256: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744 - url: "https://pub.dev" - source: hosted - version: "5.0.5" proj4dart: dependency: transitive description: @@ -961,14 +938,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" - sync_http: - dependency: transitive - description: - name: sync_http - sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://pub.dev" - source: hosted - version: "0.3.1" synchronized: dependency: transitive description: @@ -1138,14 +1107,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - webdriver: - dependency: transitive - description: - name: webdriver - sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade" - url: "https://pub.dev" - source: hosted - version: "3.1.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1330c18..99db13c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -93,8 +93,6 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - integration_test: - sdk: flutter # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is