Files
meshcore-sar_android/lib/providers/drawing_provider.dart
Janez T 021ce21cbe Refactor SAR marker handling and add template management
- Updated CompassSarList and DetailedCompassDialog to use marker.displayName instead of marker.type.displayName.
- Enhanced DrawingLayer and DrawingMarkersLayer to support simple mode for drawing visibility and interaction.
- Added toggle switches in DrawingToolbar for showing/hiding received drawings and SAR markers.
- Modified MapMarkers to utilize custom emojis and display names for markers.
- Introduced RecipientSelectorSheet for selecting message recipients with search functionality.
- Refactored SarUpdateSheet to use SAR templates instead of marker types, allowing for emoji and name customization.
- Created SarTemplateEditDialog for adding and editing SAR templates with color selection and preview.
2025-10-21 23:44:49 +02:00

299 lines
8.3 KiB
Dart

import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/map_drawing.dart';
import '../utils/drawing_message_parser.dart';
/// Drawing mode state
enum DrawingMode {
none,
line,
rectangle,
}
/// Provider for managing map drawings
class DrawingProvider with ChangeNotifier {
static const String _storageKey = 'map_drawings';
// Drawing state
DrawingMode _drawingMode = DrawingMode.none;
Color _selectedColor = DrawingColors.palette[0];
bool _showReceivedDrawings = true;
bool _showSarMarkers = true;
// Completed drawings
final List<MapDrawing> _drawings = [];
// In-progress drawing
MapDrawing? _currentDrawing;
List<LatLng> _currentLinePoints = [];
LatLng? _rectangleStartPoint;
// Getters
DrawingMode get drawingMode => _drawingMode;
Color get selectedColor => _selectedColor;
bool get showReceivedDrawings => _showReceivedDrawings;
bool get showSarMarkers => _showSarMarkers;
List<MapDrawing> get drawings => _showReceivedDrawings
? List.unmodifiable(_drawings)
: List.unmodifiable(_drawings.where((d) => !d.isReceived).toList());
MapDrawing? get currentDrawing => _currentDrawing;
List<LatLng> get currentLinePoints => List.unmodifiable(_currentLinePoints);
LatLng? get rectangleStartPoint => _rectangleStartPoint;
bool get isDrawing => _drawingMode != DrawingMode.none;
/// Initialize and load saved drawings
Future<void> initialize() async {
await _loadDrawings();
}
/// Set drawing mode
void setDrawingMode(DrawingMode mode) {
if (_drawingMode != mode) {
// Cancel any in-progress drawing when switching modes
_cancelCurrentDrawing();
_drawingMode = mode;
notifyListeners();
}
}
/// Set selected color
void setColor(Color color) {
_selectedColor = color;
notifyListeners();
}
/// Toggle visibility of received drawings
void toggleReceivedDrawings() {
_showReceivedDrawings = !_showReceivedDrawings;
notifyListeners();
}
/// Toggle visibility of SAR markers
void toggleSarMarkers() {
_showSarMarkers = !_showSarMarkers;
notifyListeners();
}
/// Start drawing a line
void startLine(LatLng point) {
if (_drawingMode != DrawingMode.line) return;
_currentLinePoints = [point];
notifyListeners();
}
/// Add point to current line
void addLinePoint(LatLng point) {
if (_drawingMode != DrawingMode.line || _currentLinePoints.isEmpty) return;
_currentLinePoints.add(point);
notifyListeners();
}
/// Complete current line drawing
void completeLine() {
if (_drawingMode != DrawingMode.line || _currentLinePoints.length < 2) {
_cancelCurrentDrawing();
return;
}
final drawing = LineDrawing(
id: DateTime.now().millisecondsSinceEpoch.toString(),
color: _selectedColor,
createdAt: DateTime.now(),
points: List.from(_currentLinePoints),
);
_drawings.add(drawing);
_currentLinePoints = [];
_saveDrawings();
notifyListeners();
}
/// Start drawing a rectangle
void startRectangle(LatLng point) {
if (_drawingMode != DrawingMode.rectangle) return;
_rectangleStartPoint = point;
notifyListeners();
}
/// Update rectangle end point (for preview)
void updateRectangleEndPoint(LatLng endPoint) {
if (_drawingMode != DrawingMode.rectangle || _rectangleStartPoint == null) return;
// Create preview rectangle
_currentDrawing = RectangleDrawing(
id: 'preview',
color: _selectedColor,
createdAt: DateTime.now(),
topLeft: LatLng(
_rectangleStartPoint!.latitude > endPoint.latitude
? endPoint.latitude
: _rectangleStartPoint!.latitude,
_rectangleStartPoint!.longitude < endPoint.longitude
? _rectangleStartPoint!.longitude
: endPoint.longitude,
),
bottomRight: LatLng(
_rectangleStartPoint!.latitude < endPoint.latitude
? endPoint.latitude
: _rectangleStartPoint!.latitude,
_rectangleStartPoint!.longitude > endPoint.longitude
? _rectangleStartPoint!.longitude
: endPoint.longitude,
),
);
notifyListeners();
}
/// Complete current rectangle drawing
void completeRectangle(LatLng endPoint) {
if (_drawingMode != DrawingMode.rectangle || _rectangleStartPoint == null) {
_cancelCurrentDrawing();
return;
}
// Calculate top-left and bottom-right corners
final topLeft = LatLng(
_rectangleStartPoint!.latitude > endPoint.latitude
? endPoint.latitude
: _rectangleStartPoint!.latitude,
_rectangleStartPoint!.longitude < endPoint.longitude
? _rectangleStartPoint!.longitude
: endPoint.longitude,
);
final bottomRight = LatLng(
_rectangleStartPoint!.latitude < endPoint.latitude
? endPoint.latitude
: _rectangleStartPoint!.latitude,
_rectangleStartPoint!.longitude > endPoint.longitude
? _rectangleStartPoint!.longitude
: endPoint.longitude,
);
final drawing = RectangleDrawing(
id: DateTime.now().millisecondsSinceEpoch.toString(),
color: _selectedColor,
createdAt: DateTime.now(),
topLeft: topLeft,
bottomRight: bottomRight,
);
_drawings.add(drawing);
_rectangleStartPoint = null;
_currentDrawing = null;
_saveDrawings();
notifyListeners();
}
/// Cancel current drawing in progress
void _cancelCurrentDrawing() {
_currentLinePoints = [];
_rectangleStartPoint = null;
_currentDrawing = null;
}
/// Clear current drawing (public method)
void cancelCurrentDrawing() {
_cancelCurrentDrawing();
notifyListeners();
}
/// Remove a specific drawing
void removeDrawing(String id) {
_drawings.removeWhere((d) => d.id == id);
_saveDrawings();
notifyListeners();
}
/// Clear all drawings
void clearAllDrawings() {
_drawings.clear();
_cancelCurrentDrawing();
_saveDrawings();
notifyListeners();
}
/// Exit drawing mode
void exitDrawingMode() {
_cancelCurrentDrawing();
_drawingMode = DrawingMode.none;
notifyListeners();
}
/// Save drawings to persistent storage
Future<void> _saveDrawings() async {
try {
final prefs = await SharedPreferences.getInstance();
final jsonList = _drawings.map((d) => d.toJson()).toList();
final jsonString = jsonEncode(jsonList);
await prefs.setString(_storageKey, jsonString);
} catch (e) {
debugPrint('Error saving drawings: $e');
}
}
/// Load drawings from persistent storage
Future<void> _loadDrawings() async {
try {
final prefs = await SharedPreferences.getInstance();
final jsonString = prefs.getString(_storageKey);
if (jsonString == null) return;
final jsonList = jsonDecode(jsonString) as List<dynamic>;
_drawings.clear();
for (final json in jsonList) {
final drawing = MapDrawing.fromJson(json as Map<String, dynamic>);
if (drawing != null) {
_drawings.add(drawing);
}
}
notifyListeners();
} catch (e) {
debugPrint('Error loading drawings: $e');
}
}
/// Get the current preview drawing for rendering
MapDrawing? getPreviewDrawing() {
if (_drawingMode == DrawingMode.line && _currentLinePoints.length >= 2) {
return LineDrawing(
id: 'preview',
color: _selectedColor,
createdAt: DateTime.now(),
points: _currentLinePoints,
);
} else if (_drawingMode == DrawingMode.rectangle && _currentDrawing != null) {
return _currentDrawing;
}
return null;
}
/// Add received drawing from another node
void addReceivedDrawing(MapDrawing drawing) {
// Check if drawing with this ID already exists
if (_drawings.any((d) => d.id == drawing.id)) {
debugPrint('Drawing ${drawing.id} already exists, skipping');
return;
}
_drawings.add(drawing);
_saveDrawings();
notifyListeners();
}
/// Broadcast a drawing to contacts
/// Returns the formatted message string ready to send
/// Sender will be determined from packet metadata on receiving end
String createDrawingBroadcastMessage(MapDrawing drawing) {
return DrawingMessageParser.createDrawingMessage(drawing);
}
}