mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
Fix RX advert data parsing
This commit is contained in:
@@ -10,7 +10,9 @@ import 'package:latlong2/latlong.dart';
|
||||
class ContactStorageService {
|
||||
static const String _contactsKey = 'stored_contacts';
|
||||
static const String _contactGroupsKey = 'stored_contact_groups';
|
||||
static const String _pendingAdvertsKey = 'stored_pending_adverts';
|
||||
static const int _maxStoredContacts = 500; // Store up to 500 contacts
|
||||
static const int _maxStoredPendingAdverts = 500;
|
||||
|
||||
/// Save contacts to persistent storage
|
||||
Future<void> saveContacts(List<Contact> contacts) async {
|
||||
@@ -126,6 +128,47 @@ class ContactStorageService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> savePendingAdverts(List<Map<String, dynamic>> adverts) async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final limitedList = adverts.length > _maxStoredPendingAdverts
|
||||
? adverts.sublist(adverts.length - _maxStoredPendingAdverts)
|
||||
: adverts;
|
||||
await prefs.setString(_pendingAdvertsKey, jsonEncode(limitedList));
|
||||
debugPrint(
|
||||
'✅ [ContactStorage] Saved ${limitedList.length} pending adverts to storage',
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('❌ [ContactStorage] Error saving pending adverts: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Map<String, dynamic>>> loadPendingAdverts() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final jsonString = prefs.getString(_pendingAdvertsKey);
|
||||
if (jsonString == null || jsonString.isEmpty) {
|
||||
return const [];
|
||||
}
|
||||
|
||||
final jsonList = jsonDecode(jsonString) as List<dynamic>;
|
||||
return jsonList.whereType<Map<String, dynamic>>().toList();
|
||||
} catch (e) {
|
||||
debugPrint('❌ [ContactStorage] Error loading pending adverts: $e');
|
||||
return const [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> clearPendingAdverts() async {
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_pendingAdvertsKey);
|
||||
debugPrint('✅ [ContactStorage] Cleared all stored pending adverts');
|
||||
} catch (e) {
|
||||
debugPrint('❌ [ContactStorage] Error clearing pending adverts: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// Get storage statistics
|
||||
Future<Map<String, dynamic>> getStorageStats() async {
|
||||
try {
|
||||
|
||||
@@ -31,6 +31,8 @@ class NotificationService {
|
||||
static const int _sarNotificationId = 1000;
|
||||
static const int _messageNotificationId = 2000;
|
||||
static const int _updateNotificationId = 3000;
|
||||
static const int _batteryNotificationId = 4000;
|
||||
static const int _discoveryNotificationId = 5000;
|
||||
|
||||
// Notification channels
|
||||
static const String _urgentChannelId = 'sar_urgent';
|
||||
@@ -48,6 +50,16 @@ class NotificationService {
|
||||
static const String _updateChannelDescription =
|
||||
'Notifications for available app updates';
|
||||
|
||||
static const String _batteryChannelId = 'battery_alerts';
|
||||
static const String _batteryChannelName = 'Battery Alerts';
|
||||
static const String _batteryChannelDescription =
|
||||
'Notifications when a device or contact battery is low';
|
||||
|
||||
static const String _discoveryChannelId = 'discovery_alerts';
|
||||
static const String _discoveryChannelName = 'Discovery Alerts';
|
||||
static const String _discoveryChannelDescription =
|
||||
'Notifications when new contacts are discovered';
|
||||
|
||||
bool get messageNotificationsEnabled => _messageNotificationsEnabled;
|
||||
bool get sarNotificationsEnabled => _sarNotificationsEnabled;
|
||||
bool get updateNotificationsEnabled => _updateNotificationsEnabled;
|
||||
@@ -237,9 +249,31 @@ class NotificationService {
|
||||
showBadge: true,
|
||||
);
|
||||
|
||||
const batteryChannel = AndroidNotificationChannel(
|
||||
_batteryChannelId,
|
||||
_batteryChannelName,
|
||||
description: _batteryChannelDescription,
|
||||
importance: Importance.high,
|
||||
playSound: true,
|
||||
enableVibration: true,
|
||||
showBadge: true,
|
||||
);
|
||||
|
||||
const discoveryChannel = AndroidNotificationChannel(
|
||||
_discoveryChannelId,
|
||||
_discoveryChannelName,
|
||||
description: _discoveryChannelDescription,
|
||||
importance: Importance.high,
|
||||
playSound: true,
|
||||
enableVibration: true,
|
||||
showBadge: true,
|
||||
);
|
||||
|
||||
await androidPlugin.createNotificationChannel(urgentChannel);
|
||||
await androidPlugin.createNotificationChannel(messagesChannel);
|
||||
await androidPlugin.createNotificationChannel(updateChannel);
|
||||
await androidPlugin.createNotificationChannel(batteryChannel);
|
||||
await androidPlugin.createNotificationChannel(discoveryChannel);
|
||||
debugPrint('✅ [NotificationService] Created notification channels');
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ [NotificationService] Error creating channels: $e');
|
||||
@@ -707,6 +741,156 @@ class NotificationService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> showLowBatteryNotification({
|
||||
required String nodeId,
|
||||
required String nodeName,
|
||||
required double batteryPercent,
|
||||
required bool isCurrentDevice,
|
||||
}) async {
|
||||
if (!_isInitialized) {
|
||||
debugPrint(
|
||||
'⚠️ [NotificationService] Not initialized, skipping notification',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_permissionGranted) {
|
||||
debugPrint(
|
||||
'⚠️ [NotificationService] Permission not granted, skipping notification',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!_messageNotificationsEnabled) {
|
||||
debugPrint('ℹ️ [NotificationService] Message notifications disabled');
|
||||
return false;
|
||||
}
|
||||
if (_shouldSuppressForegroundNotifications()) {
|
||||
debugPrint(
|
||||
'ℹ️ [NotificationService] App in foreground, skipping low battery notification',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
final roundedPercent = batteryPercent.round().clamp(0, 100);
|
||||
final title = isCurrentDevice
|
||||
? 'Device battery low'
|
||||
: 'Contact battery low';
|
||||
final body = isCurrentDevice
|
||||
? '$nodeName battery is at $roundedPercent%.'
|
||||
: '$nodeName is at $roundedPercent% battery.';
|
||||
final notificationId =
|
||||
_batteryNotificationId + ((nodeId.hashCode & 0x7fffffff) % 1000);
|
||||
|
||||
try {
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
_batteryChannelId,
|
||||
_batteryChannelName,
|
||||
channelDescription: _batteryChannelDescription,
|
||||
importance: Importance.high,
|
||||
priority: Priority.high,
|
||||
ticker: title,
|
||||
playSound: true,
|
||||
enableVibration: true,
|
||||
showWhen: true,
|
||||
when: DateTime.now().millisecondsSinceEpoch,
|
||||
styleInformation: BigTextStyleInformation(
|
||||
body,
|
||||
contentTitle: title,
|
||||
summaryText: '$roundedPercent%',
|
||||
),
|
||||
);
|
||||
|
||||
final darwinDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
sound: 'default',
|
||||
threadIdentifier: 'battery_alerts',
|
||||
subtitle: '$roundedPercent%',
|
||||
);
|
||||
|
||||
final notificationDetails = NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: darwinDetails,
|
||||
);
|
||||
|
||||
await _notificationsPlugin.show(
|
||||
id: notificationId,
|
||||
title: title,
|
||||
body: body,
|
||||
notificationDetails: notificationDetails,
|
||||
payload: 'battery:$nodeId',
|
||||
);
|
||||
|
||||
debugPrint(
|
||||
'✅ [NotificationService] Showed low battery notification for $nodeName ($roundedPercent%)',
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'❌ [NotificationService] Error showing low battery notification: $e',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> showContactDiscoveredNotification({
|
||||
required String contactKey,
|
||||
}) async {
|
||||
if (!_isInitialized) return false;
|
||||
if (!_permissionGranted) return false;
|
||||
if (!_messageNotificationsEnabled) return false;
|
||||
if (_shouldSuppressForegroundNotifications()) return false;
|
||||
|
||||
final shortKey = contactKey.length > 12
|
||||
? contactKey.substring(0, 12).toUpperCase()
|
||||
: contactKey.toUpperCase();
|
||||
final title = 'New contact discovered';
|
||||
final body = 'New contact $shortKey is available in Discovery.';
|
||||
final notificationId =
|
||||
_discoveryNotificationId + ((contactKey.hashCode & 0x7fffffff) % 1000);
|
||||
|
||||
try {
|
||||
final androidDetails = AndroidNotificationDetails(
|
||||
_discoveryChannelId,
|
||||
_discoveryChannelName,
|
||||
channelDescription: _discoveryChannelDescription,
|
||||
importance: Importance.high,
|
||||
priority: Priority.high,
|
||||
ticker: title,
|
||||
playSound: true,
|
||||
enableVibration: true,
|
||||
showWhen: true,
|
||||
when: DateTime.now().millisecondsSinceEpoch,
|
||||
);
|
||||
|
||||
final darwinDetails = DarwinNotificationDetails(
|
||||
presentAlert: true,
|
||||
presentBadge: true,
|
||||
presentSound: true,
|
||||
sound: 'default',
|
||||
threadIdentifier: 'discovery_alerts',
|
||||
);
|
||||
|
||||
await _notificationsPlugin.show(
|
||||
id: notificationId,
|
||||
title: title,
|
||||
body: body,
|
||||
notificationDetails: NotificationDetails(
|
||||
android: androidDetails,
|
||||
iOS: darwinDetails,
|
||||
),
|
||||
payload: 'discovery:$contactKey',
|
||||
);
|
||||
return true;
|
||||
} catch (e) {
|
||||
debugPrint(
|
||||
'❌ [NotificationService] Error showing discovery notification: $e',
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _LifecycleObserver with WidgetsBindingObserver {
|
||||
|
||||
Reference in New Issue
Block a user