refactor: Improve connection and room login state management by utilizing helper methods

This commit is contained in:
Janez T
2025-10-15 10:34:50 +02:00
parent e831612a1a
commit 86711dbb27
3 changed files with 87 additions and 52 deletions

View File

@@ -29,27 +29,58 @@ lib/
│ ├── message.dart # Messages and SAR markers │ ├── message.dart # Messages and SAR markers
│ ├── sar_marker.dart # SAR tactical markers │ ├── sar_marker.dart # SAR tactical markers
│ ├── device_info.dart # BLE device connection state │ ├── device_info.dart # BLE device connection state
│ ├── room_login_state.dart # Room login status tracking
│ └── map_layer.dart # Map tile layer definitions │ └── map_layer.dart # Map tile layer definitions
├── services/ # Business logic services ├── services/ # Business logic services
│ ├── meshcore_ble_service.dart # BLE communication │ ├── meshcore_ble_service.dart # BLE service coordinator (399 lines)
│ ├── meshcore_constants.dart # Protocol constants │ ├── meshcore_constants.dart # Protocol constants
│ ├── buffer_reader.dart # Binary protocol reader │ ├── buffer_reader.dart # Binary protocol reader
│ ├── buffer_writer.dart # Binary protocol writer │ ├── buffer_writer.dart # Binary protocol writer
│ ├── cayenne_lpp_parser.dart # Telemetry decoder │ ├── cayenne_lpp_parser.dart # Telemetry decoder
── tile_cache_service.dart # Offline map tiles ── tile_cache_service.dart # Offline map tiles
│ ├── protocol/ # Protocol layer (628 lines)
│ │ ├── frame_parser.dart # Parse incoming BLE frames
│ │ └── frame_builder.dart # Build outgoing BLE frames
│ └── ble/ # BLE layer (963 lines)
│ ├── ble_connection_manager.dart # Connection lifecycle
│ ├── ble_command_sender.dart # Command transmission
│ └── ble_response_handler.dart # Response processing
├── providers/ # State management ├── providers/ # State management
│ ├── connection_provider.dart # BLE connection state │ ├── connection_provider.dart # BLE connection state (957 lines)
│ ├── contacts_provider.dart # Contact list management │ ├── contacts_provider.dart # Contact list management
│ ├── messages_provider.dart # Message history + SAR markers │ ├── messages_provider.dart # Message history + SAR markers
│ ├── map_provider.dart # Map navigation state │ ├── map_provider.dart # Map navigation state
── app_provider.dart # Coordinator provider ── app_provider.dart # Coordinator provider
│ └── helpers/ # Provider helpers (155 lines)
│ ├── room_login_manager.dart # Room login state management
│ └── message_delivery_tracker.dart # Message delivery tracking
├── screens/ # UI screens ├── screens/ # UI screens
│ ├── home_screen.dart # Main screen with tabs │ ├── home_screen.dart # Main screen with tabs
│ ├── messages_tab.dart # Message list view │ ├── messages_tab.dart # Message list view (767 lines)
│ ├── contacts_tab.dart # Contact list view │ ├── contacts_tab.dart # Contact list view (208 lines)
── map_tab.dart # Interactive map view ── map_tab.dart # Interactive map view (1,217 lines)
│ ├── settings_screen.dart # App settings (875 lines)
│ ├── device_config_screen.dart # Device configuration (718 lines)
│ ├── map_management_screen.dart # Map tile management (728 lines)
│ └── packet_log_screen.dart # BLE packet diagnostics (565 lines)
├── widgets/ # Reusable UI components ├── widgets/ # Reusable UI components
── map_markers.dart # Custom map marker widgets ── map_markers.dart # Custom map marker widgets
│ ├── messages/ # Message components (698 lines)
│ │ └── sar_update_sheet.dart # SAR marker creation modal
│ ├── contacts/ # Contact components (1,578 lines)
│ │ ├── contact_tile.dart # Contact list tile + details dialog
│ │ ├── direct_message_sheet.dart # Direct messaging modal
│ │ ├── room_login_sheet.dart # Room login modal
│ │ └── section_header.dart # Section header component
│ └── map/ # Map components (2,867 lines)
│ ├── detailed_compass_dialog.dart # Main compass dialog (570 lines)
│ ├── map_legend.dart # Map legend with counts
│ ├── compass_widget.dart # Small compass widget
│ └── compass/ # Compass subcomponents (1,150 lines)
│ ├── compass_header.dart # Compass rose + location display
│ ├── compass_filters.dart # Filter controls
│ ├── compass_sar_list.dart # SAR marker list
│ └── compass_contact_list.dart # Contact list
├── utils/ # Utilities ├── utils/ # Utilities
│ └── sar_message_parser.dart # Parse S:<emoji>:lat,lon format │ └── sar_message_parser.dart # Parse S:<emoji>:lat,lon format
└── main.dart # App entry point └── main.dart # App entry point
@@ -1011,28 +1042,57 @@ flutter build apk --split-per-abi
### Adding a New BLE Command ### Adding a New BLE Command
The BLE service is now split into modular components. Follow these steps:
1. **Add command code** to `lib/services/meshcore_constants.dart`: 1. **Add command code** to `lib/services/meshcore_constants.dart`:
```dart ```dart
static const int cmdYourCommand = 42; static const int cmdYourCommand = 42;
``` ```
2. **Create command method** in `lib/services/meshcore_ble_service.dart`: 2. **Add frame builder** in `lib/services/protocol/frame_builder.dart`:
```dart ```dart
Future<void> yourCommand() async { /// Build YOUR_COMMAND frame
static Uint8List buildYourCommand({required String param}) {
final writer = BufferWriter(); final writer = BufferWriter();
writer.writeByte(MeshCoreConstants.cmdYourCommand); writer.writeByte(MeshCoreConstants.cmdYourCommand);
await _sendCommand(writer.toBytes()); writer.writeString(param);
return writer.toBytes();
} }
``` ```
3. **Handle response** in `_onDataReceived()`: 3. **Add public API method** in `lib/services/meshcore_ble_service.dart`:
```dart
Future<void> yourCommand({required String param}) async {
final frame = FrameBuilder.buildYourCommand(param: param);
await _commandSender.sendCommand(frame);
}
```
4. **Add response parser** in `lib/services/protocol/frame_parser.dart`:
```dart
static Map<String, dynamic> parseYourResponse(Uint8List data) {
final reader = BufferReader(data);
reader.readByte(); // Skip response code
return {
'yourField': reader.readString(),
// ... parse other fields
};
}
```
5. **Handle response** in `lib/services/ble/ble_response_handler.dart`:
```dart ```dart
case MeshCoreConstants.respYourResponse: case MeshCoreConstants.respYourResponse:
// Parse response data final parsed = FrameParser.parseYourResponse(data);
onYourCallback?.call(data); _bleService.onYourCallback?.call(parsed);
break; break;
``` ```
6. **Add callback** in `lib/services/meshcore_ble_service.dart`:
```dart
Function(Map<String, dynamic>)? onYourCallback;
```
### Adding a New SAR Marker Type ### Adding a New SAR Marker Type
1. **Update enum** in `lib/models/sar_marker.dart`: 1. **Update enum** in `lib/models/sar_marker.dart`:

View File

@@ -1,5 +1,4 @@
import 'dart:async'; import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart'; import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import '../models/device_info.dart'; import '../models/device_info.dart';
@@ -7,7 +6,6 @@ import '../models/contact.dart';
import '../models/message.dart'; import '../models/message.dart';
import '../models/room_login_state.dart'; import '../models/room_login_state.dart';
import '../services/meshcore_ble_service.dart'; import '../services/meshcore_ble_service.dart';
import '../services/cayenne_lpp_parser.dart';
import '../utils/sar_message_parser.dart'; import '../utils/sar_message_parser.dart';
import 'helpers/room_login_manager.dart'; import 'helpers/room_login_manager.dart';
import 'helpers/message_delivery_tracker.dart'; import 'helpers/message_delivery_tracker.dart';
@@ -193,14 +191,14 @@ class ConnectionProvider with ChangeNotifier {
_bleService.onMessageSent = (expectedAckTag, suggestedTimeoutMs, isFloodMode) { _bleService.onMessageSent = (expectedAckTag, suggestedTimeoutMs, isFloodMode) {
print('📥 [Provider] Message sent - ACK tag: $expectedAckTag, timeout: ${suggestedTimeoutMs}ms'); print('📥 [Provider] Message sent - ACK tag: $expectedAckTag, timeout: ${suggestedTimeoutMs}ms');
// Pop the first pending message ID from the queue (FIFO) // Pop the first pending message ID from the queue (FIFO) via helper
// This assumes messages are sent sequentially and SENT responses arrive in order final messageId = _messageDeliveryTracker.popPendingMessageId();
if (_pendingSentMessageIds.isNotEmpty) {
final messageId = _pendingSentMessageIds.removeAt(0); if (messageId != null) {
print(' Matched with message ID: $messageId'); print(' Matched with message ID: $messageId');
// Store the ACK tag to message ID mapping for delivery confirmation // Store the ACK tag to message ID mapping for delivery confirmation
_ackTagToMessageId[expectedAckTag] = messageId; _messageDeliveryTracker.mapAckTagToMessageId(expectedAckTag, messageId);
// Notify callback with message ID // Notify callback with message ID
onMessageSent?.call(messageId, expectedAckTag, suggestedTimeoutMs); onMessageSent?.call(messageId, expectedAckTag, suggestedTimeoutMs);
@@ -398,7 +396,7 @@ class ConnectionProvider with ChangeNotifier {
_deviceInfo = DeviceInfo( _deviceInfo = DeviceInfo(
connectionState: ConnectionState.disconnected, connectionState: ConnectionState.disconnected,
); );
clearRoomLoginStates(); // Clear login states on disconnect _roomLoginManager.clearRoomLoginStates(); // Clear login states on disconnect
notifyListeners(); notifyListeners();
} }
@@ -462,11 +460,11 @@ class ConnectionProvider with ChangeNotifier {
text: text, text: text,
); );
// If message ID provided, add it to the pending queue // If message ID provided, add it to the pending queue via helper
// When the SENT response arrives, it will be matched with this message ID // When the SENT response arrives, it will be matched with this message ID
// Note: Messages must be sent sequentially for this to work correctly // Note: Messages must be sent sequentially for this to work correctly
if (messageId != null) { if (messageId != null) {
_pendingSentMessageIds.add(messageId); _messageDeliveryTracker.trackPendingMessage(messageId);
print(' Added message ID to pending queue: $messageId'); print(' Added message ID to pending queue: $messageId');
} }
@@ -498,9 +496,9 @@ class ConnectionProvider with ChangeNotifier {
text: text, text: text,
); );
// If message ID provided, add it to the pending queue // If message ID provided, add it to the pending queue via helper
if (messageId != null) { if (messageId != null) {
_pendingSentMessageIds.add(messageId); _messageDeliveryTracker.trackPendingMessage(messageId);
print(' Added message ID to pending queue: $messageId'); print(' Added message ID to pending queue: $messageId');
} }
} catch (e) { } catch (e) {
@@ -939,36 +937,14 @@ class ConnectionProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
/// Check if a password exists for a room (by public key prefix)
Future<bool> _hasPasswordForRoom(Uint8List publicKeyPrefix) async {
try {
final prefs = await SharedPreferences.getInstance();
// Convert prefix to hex string for storage key
final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':');
final roomKey = 'room_password_$prefixHex';
return prefs.getString(roomKey) != null;
} catch (e) {
debugPrint('Error checking password for room: $e');
return false;
}
}
/// Get login state for a room by public key prefix /// Get login state for a room by public key prefix
RoomLoginState? getRoomLoginState(Uint8List publicKeyPrefix) { RoomLoginState? getRoomLoginState(Uint8List publicKeyPrefix) {
final prefixHex = publicKeyPrefix.map((b) => b.toRadixString(16).padLeft(2, '0')).join(':'); return _roomLoginManager.getRoomLoginState(publicKeyPrefix);
return _roomLoginStates[prefixHex];
} }
/// Check if logged into a specific room /// Check if logged into a specific room
bool isLoggedIntoRoom(Uint8List publicKeyPrefix) { bool isLoggedIntoRoom(Uint8List publicKeyPrefix) {
final state = getRoomLoginState(publicKeyPrefix); return _roomLoginManager.isLoggedIntoRoom(publicKeyPrefix);
return state?.isLoggedIn ?? false;
}
/// Clear all room login states (call on disconnect)
void clearRoomLoginStates() {
_roomLoginStates.clear();
notifyListeners();
} }
@override @override

View File

@@ -1,4 +1,3 @@
import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../../models/room_login_state.dart'; import '../../models/room_login_state.dart';
@@ -6,7 +5,7 @@ import '../../models/room_login_state.dart';
/// Room login state management helper /// Room login state management helper
/// ///
/// Manages login state tracking for room contacts, including: /// Manages login state tracking for room contacts, including:
/// - Room login state per contact (Map<String, RoomLoginState>) /// - Room login state per contact (Map of String to RoomLoginState)
/// - Password checking logic /// - Password checking logic
/// - Login success/fail state updates /// - Login success/fail state updates
class RoomLoginManager { class RoomLoginManager {