mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
feat: Implement location tracking service and haptic feedback for device advertising
This commit is contained in:
Binary file not shown.
BIN
ios/Runner.ipa
BIN
ios/Runner.ipa
Binary file not shown.
@@ -489,7 +489,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -511,7 +511,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
||||||
@@ -529,7 +529,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
||||||
@@ -545,7 +545,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 1.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.sar.meshcoreSarApp.RunnerTests;
|
||||||
@@ -676,7 +676,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
@@ -699,7 +699,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 17;
|
CURRENT_PROJECT_VERSION = 18;
|
||||||
DEVELOPMENT_TEAM = JND55328G8;
|
DEVELOPMENT_TEAM = JND55328G8;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Runner/Info.plist;
|
INFOPLIST_FILE = Runner/Info.plist;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<key>CFBundleSignature</key>
|
<key>CFBundleSignature</key>
|
||||||
<string>????</string>
|
<string>????</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>17</string>
|
<string>18</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
|
|||||||
@@ -1776,5 +1776,10 @@
|
|||||||
"placeholders": {
|
"placeholders": {
|
||||||
"power": { "type": "int" }
|
"power": { "type": "int" }
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"you": "You",
|
||||||
|
"@you": {
|
||||||
|
"description": "Label for the current user in message bubbles"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -590,5 +590,7 @@
|
|||||||
"spreadingFactor": "Faktor širenja",
|
"spreadingFactor": "Faktor širenja",
|
||||||
"codingRate": "Omjer kodiranja",
|
"codingRate": "Omjer kodiranja",
|
||||||
"txPowerDbm": "TX snaga (dBm)",
|
"txPowerDbm": "TX snaga (dBm)",
|
||||||
"maxPowerDbm": "Maks: {power} dBm"
|
"maxPowerDbm": "Maks: {power} dBm",
|
||||||
|
|
||||||
|
"you": "Ti"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1898,6 +1898,12 @@ abstract class AppLocalizations {
|
|||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Max: {power} dBm'**
|
/// **'Max: {power} dBm'**
|
||||||
String maxPowerDbm(int power);
|
String maxPowerDbm(int power);
|
||||||
|
|
||||||
|
/// Label for the current user in message bubbles
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'You'**
|
||||||
|
String get you;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AppLocalizationsDelegate
|
class _AppLocalizationsDelegate
|
||||||
|
|||||||
@@ -1035,4 +1035,7 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
String maxPowerDbm(int power) {
|
String maxPowerDbm(int power) {
|
||||||
return 'Max: $power dBm';
|
return 'Max: $power dBm';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get you => 'You';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1037,4 +1037,7 @@ class AppLocalizationsHr extends AppLocalizations {
|
|||||||
String maxPowerDbm(int power) {
|
String maxPowerDbm(int power) {
|
||||||
return 'Maks: $power dBm';
|
return 'Maks: $power dBm';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get you => 'Ti';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1037,4 +1037,7 @@ class AppLocalizationsSl extends AppLocalizations {
|
|||||||
String maxPowerDbm(int power) {
|
String maxPowerDbm(int power) {
|
||||||
return 'Največ: $power dBm';
|
return 'Največ: $power dBm';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get you => 'Ti';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -590,5 +590,7 @@
|
|||||||
"spreadingFactor": "Faktor razširitve",
|
"spreadingFactor": "Faktor razširitve",
|
||||||
"codingRate": "Razmerje kodiranja",
|
"codingRate": "Razmerje kodiranja",
|
||||||
"txPowerDbm": "Izhodna moč (dBm)",
|
"txPowerDbm": "Izhodna moč (dBm)",
|
||||||
"maxPowerDbm": "Največ: {power} dBm"
|
"maxPowerDbm": "Največ: {power} dBm",
|
||||||
|
|
||||||
|
"you": "Ti"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'contacts_provider.dart';
|
|||||||
import 'messages_provider.dart';
|
import 'messages_provider.dart';
|
||||||
import 'drawing_provider.dart';
|
import 'drawing_provider.dart';
|
||||||
import '../services/tile_cache_service.dart';
|
import '../services/tile_cache_service.dart';
|
||||||
|
import '../services/location_tracking_service.dart';
|
||||||
import '../models/contact.dart';
|
import '../models/contact.dart';
|
||||||
import '../utils/drawing_message_parser.dart';
|
import '../utils/drawing_message_parser.dart';
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ class AppProvider with ChangeNotifier {
|
|||||||
final MessagesProvider messagesProvider;
|
final MessagesProvider messagesProvider;
|
||||||
final DrawingProvider drawingProvider;
|
final DrawingProvider drawingProvider;
|
||||||
final TileCacheService tileCacheService;
|
final TileCacheService tileCacheService;
|
||||||
|
final LocationTrackingService locationTrackingService = LocationTrackingService();
|
||||||
|
|
||||||
bool _isInitialized = false;
|
bool _isInitialized = false;
|
||||||
bool get isInitialized => _isInitialized;
|
bool get isInitialized => _isInitialized;
|
||||||
@@ -29,6 +31,7 @@ class AppProvider with ChangeNotifier {
|
|||||||
}) {
|
}) {
|
||||||
_setupCallbacks();
|
_setupCallbacks();
|
||||||
_initializeTileCache();
|
_initializeTileCache();
|
||||||
|
_initializeLocationTracking();
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,8 +45,39 @@ class AppProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Initialize location tracking service
|
||||||
|
Future<void> _initializeLocationTracking() async {
|
||||||
|
try {
|
||||||
|
// Initialize location tracking with BLE service
|
||||||
|
await locationTrackingService.initialize(connectionProvider.bleService);
|
||||||
|
|
||||||
|
// Setup callbacks
|
||||||
|
locationTrackingService.onPositionUpdate = (position) {
|
||||||
|
debugPrint('📍 [AppProvider] Position updated: ${position.latitude}, ${position.longitude}');
|
||||||
|
};
|
||||||
|
|
||||||
|
locationTrackingService.onBroadcastSent = (position) {
|
||||||
|
debugPrint('📡 [AppProvider] Position broadcast to mesh network');
|
||||||
|
};
|
||||||
|
|
||||||
|
locationTrackingService.onError = (error) {
|
||||||
|
debugPrint('❌ [AppProvider] Location tracking error: $error');
|
||||||
|
};
|
||||||
|
|
||||||
|
locationTrackingService.onTrackingStateChanged = (isTracking) {
|
||||||
|
debugPrint('🔄 [AppProvider] Location tracking state: ${isTracking ? "started" : "stopped"}');
|
||||||
|
};
|
||||||
|
|
||||||
|
debugPrint('✅ [AppProvider] Location tracking service initialized');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ [AppProvider] Error initializing location tracking: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Setup callbacks between providers
|
/// Setup callbacks between providers
|
||||||
void _setupCallbacks() {
|
void _setupCallbacks() {
|
||||||
|
// Monitor connection state changes to start/stop location tracking
|
||||||
|
connectionProvider.addListener(_handleConnectionStateChange);
|
||||||
// When a contact is received from BLE
|
// When a contact is received from BLE
|
||||||
connectionProvider.onContactReceived = (contact) {
|
connectionProvider.onContactReceived = (contact) {
|
||||||
// Pass device public key to filter out our own contact
|
// Pass device public key to filter out our own contact
|
||||||
@@ -339,6 +373,46 @@ class AppProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle connection state changes to manage location tracking
|
||||||
|
void _handleConnectionStateChange() {
|
||||||
|
final isConnected = connectionProvider.deviceInfo.isConnected;
|
||||||
|
final wasTracking = locationTrackingService.isTracking;
|
||||||
|
|
||||||
|
if (isConnected && !wasTracking) {
|
||||||
|
// Connection established - start location tracking
|
||||||
|
debugPrint('🔵 [AppProvider] BLE connected - starting location tracking');
|
||||||
|
_startLocationTracking();
|
||||||
|
} else if (!isConnected && wasTracking) {
|
||||||
|
// Connection lost - stop location tracking
|
||||||
|
debugPrint('🔴 [AppProvider] BLE disconnected - stopping location tracking');
|
||||||
|
_stopLocationTracking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start location tracking
|
||||||
|
Future<void> _startLocationTracking() async {
|
||||||
|
try {
|
||||||
|
final started = await locationTrackingService.startTracking();
|
||||||
|
if (started) {
|
||||||
|
debugPrint('✅ [AppProvider] Location tracking started successfully');
|
||||||
|
} else {
|
||||||
|
debugPrint('⚠️ [AppProvider] Failed to start location tracking');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ [AppProvider] Error starting location tracking: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stop location tracking
|
||||||
|
Future<void> _stopLocationTracking() async {
|
||||||
|
try {
|
||||||
|
await locationTrackingService.stopTracking();
|
||||||
|
debugPrint('✅ [AppProvider] Location tracking stopped');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ [AppProvider] Error stopping location tracking: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear all data
|
/// Clear all data
|
||||||
void clearAllData() {
|
void clearAllData() {
|
||||||
contactsProvider.clearContacts();
|
contactsProvider.clearContacts();
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:vibration/vibration.dart';
|
||||||
import '../providers/connection_provider.dart';
|
import '../providers/connection_provider.dart';
|
||||||
import '../providers/app_provider.dart';
|
import '../providers/app_provider.dart';
|
||||||
import '../providers/messages_provider.dart';
|
import '../providers/messages_provider.dart';
|
||||||
@@ -765,7 +767,30 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
// CENTER: Broadcast button
|
// CENTER: Broadcast button
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
FilledButton(
|
FilledButton(
|
||||||
onPressed: () => _advertiseDevice(context),
|
onPressed: () async {
|
||||||
|
// iOS: Use haptic feedback (always works)
|
||||||
|
// Android: Try vibration package for better control
|
||||||
|
try {
|
||||||
|
if (Theme.of(context).platform == TargetPlatform.iOS) {
|
||||||
|
// iOS: Try multiple haptic types for reliability
|
||||||
|
await HapticFeedback.lightImpact();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 50));
|
||||||
|
await HapticFeedback.lightImpact();
|
||||||
|
} else {
|
||||||
|
// Android vibration
|
||||||
|
if (await Vibration.hasVibrator() ?? false) {
|
||||||
|
await Vibration.vibrate(duration: 50);
|
||||||
|
} else {
|
||||||
|
await HapticFeedback.mediumImpact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback if anything fails
|
||||||
|
print('Haptic feedback error: $e');
|
||||||
|
await HapticFeedback.vibrate();
|
||||||
|
}
|
||||||
|
_advertiseDevice(context);
|
||||||
|
},
|
||||||
style: FilledButton.styleFrom(
|
style: FilledButton.styleFrom(
|
||||||
backgroundColor: Colors.blue.shade700,
|
backgroundColor: Colors.blue.shade700,
|
||||||
foregroundColor: Colors.white,
|
foregroundColor: Colors.white,
|
||||||
|
|||||||
@@ -714,7 +714,7 @@ class _MessageBubble extends StatelessWidget {
|
|||||||
|
|
||||||
// Get rich display name (with emoji if available)
|
// Get rich display name (with emoji if available)
|
||||||
final displayName = isOwnMessage
|
final displayName = isOwnMessage
|
||||||
? 'You'
|
? AppLocalizations.of(context)!.you
|
||||||
: message.getRichDisplayName(senderContact);
|
: message.getRichDisplayName(senderContact);
|
||||||
|
|
||||||
// For sent direct messages, look up recipient contact
|
// For sent direct messages, look up recipient contact
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class LocationTrackingService {
|
|||||||
double maxDistanceMeters = 100.0;
|
double maxDistanceMeters = 100.0;
|
||||||
|
|
||||||
/// Minimum time interval in seconds between broadcasts
|
/// Minimum time interval in seconds between broadcasts
|
||||||
int minTimeIntervalSeconds = 155;
|
int minTimeIntervalSeconds = 30;
|
||||||
|
|
||||||
/// GPS update distance filter for position stream
|
/// GPS update distance filter for position stream
|
||||||
double gpsUpdateDistance = 10.0;
|
double gpsUpdateDistance = 10.0;
|
||||||
@@ -380,6 +380,18 @@ class LocationTrackingService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CRITICAL: Enforce minimum time between broadcasts to prevent mesh flooding
|
||||||
|
// This protects the mesh network from being overwhelmed by position updates
|
||||||
|
if (_lastBroadcastTime != null) {
|
||||||
|
final timeSinceLastBroadcast = DateTime.now().difference(_lastBroadcastTime!);
|
||||||
|
if (timeSinceLastBroadcast.inSeconds < minTimeIntervalSeconds) {
|
||||||
|
debugPrint(
|
||||||
|
'⏸️ [LocationTracking] Skipping broadcast: only ${timeSinceLastBroadcast.inSeconds}s since last broadcast (minimum: ${minTimeIntervalSeconds}s)',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
debugPrint('📤 [LocationTracking] Broadcasting position to mesh network');
|
debugPrint('📤 [LocationTracking] Broadcasting position to mesh network');
|
||||||
|
|
||||||
@@ -389,10 +401,10 @@ class LocationTrackingService {
|
|||||||
longitude: position.longitude,
|
longitude: position.longitude,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Send advertisement to mesh network
|
// Send advertisement to mesh network (only ONE advert per position update)
|
||||||
await _bleService!.sendSelfAdvert(floodMode: true);
|
await _bleService!.sendSelfAdvert(floodMode: true);
|
||||||
|
|
||||||
// Update broadcast tracking
|
// Update broadcast tracking IMMEDIATELY to prevent duplicate broadcasts
|
||||||
_lastBroadcastPosition = position;
|
_lastBroadcastPosition = position;
|
||||||
_lastBroadcastTime = DateTime.now();
|
_lastBroadcastTime = DateTime.now();
|
||||||
|
|
||||||
@@ -402,6 +414,7 @@ class LocationTrackingService {
|
|||||||
await prefs.setDouble(_prefKeyLastLon, position.longitude);
|
await prefs.setDouble(_prefKeyLastLon, position.longitude);
|
||||||
|
|
||||||
debugPrint('✅ [LocationTracking] Position broadcast successful');
|
debugPrint('✅ [LocationTracking] Position broadcast successful');
|
||||||
|
debugPrint(' Next broadcast allowed in ${minTimeIntervalSeconds}s');
|
||||||
onBroadcastSent?.call(position);
|
onBroadcastSent?.call(position);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('❌ [LocationTracking] Failed to broadcast position: $e');
|
debugPrint('❌ [LocationTracking] Failed to broadcast position: $e');
|
||||||
@@ -412,6 +425,9 @@ class LocationTrackingService {
|
|||||||
/// Manually broadcast current location immediately
|
/// Manually broadcast current location immediately
|
||||||
///
|
///
|
||||||
/// Useful for "Send Location Now" button functionality.
|
/// Useful for "Send Location Now" button functionality.
|
||||||
|
/// Note: Manual broadcasts bypass automatic throttling and can be sent anytime.
|
||||||
|
/// However, they still update the last broadcast time to maintain proper spacing
|
||||||
|
/// for subsequent automatic broadcasts.
|
||||||
Future<bool> broadcastLocationNow() async {
|
Future<bool> broadcastLocationNow() async {
|
||||||
if (!_isInitialized || _bleService == null) {
|
if (!_isInitialized || _bleService == null) {
|
||||||
onError?.call('Location tracking service not initialized');
|
onError?.call('Location tracking service not initialized');
|
||||||
@@ -431,7 +447,9 @@ class LocationTrackingService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast regardless of thresholds
|
debugPrint('📤 [LocationTracking] Manual broadcast requested');
|
||||||
|
|
||||||
|
// Broadcast regardless of automatic throttling thresholds
|
||||||
await _bleService!.setAdvertLatLon(
|
await _bleService!.setAdvertLatLon(
|
||||||
latitude: position.latitude,
|
latitude: position.latitude,
|
||||||
longitude: position.longitude,
|
longitude: position.longitude,
|
||||||
@@ -439,11 +457,12 @@ class LocationTrackingService {
|
|||||||
|
|
||||||
await _bleService!.sendSelfAdvert(floodMode: true);
|
await _bleService!.sendSelfAdvert(floodMode: true);
|
||||||
|
|
||||||
// Update broadcast tracking
|
// Update broadcast tracking to maintain proper spacing for automatic broadcasts
|
||||||
_lastBroadcastPosition = position;
|
_lastBroadcastPosition = position;
|
||||||
_lastBroadcastTime = DateTime.now();
|
_lastBroadcastTime = DateTime.now();
|
||||||
|
|
||||||
debugPrint('✅ [LocationTracking] Manual broadcast successful');
|
debugPrint('✅ [LocationTracking] Manual broadcast successful');
|
||||||
|
debugPrint(' Automatic broadcasts will resume after ${minTimeIntervalSeconds}s');
|
||||||
onBroadcastSent?.call(position);
|
onBroadcastSent?.call(position);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import device_info_plus
|
||||||
import flutter_blue_plus_darwin
|
import flutter_blue_plus_darwin
|
||||||
import flutter_local_notifications
|
import flutter_local_notifications
|
||||||
import geolocator_apple
|
import geolocator_apple
|
||||||
@@ -15,6 +16,7 @@ import share_plus
|
|||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
|
||||||
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
|
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
|
||||||
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin"))
|
||||||
|
|||||||
40
pubspec.lock
40
pubspec.lock
@@ -129,6 +129,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.11"
|
version: "0.7.11"
|
||||||
|
device_info_plus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus
|
||||||
|
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "11.5.0"
|
||||||
|
device_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus_platform_interface
|
||||||
|
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.3"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -946,6 +962,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
vibration:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: vibration
|
||||||
|
sha256: "3b08a0579c2f9c18d5d78cb5c74f1005f731e02eeca6d72561a2e8059bf98ec3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
|
vibration_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: vibration_platform_interface
|
||||||
|
sha256: "6ffeee63547562a6fef53c05a41d4fdcae2c0595b83ef59a4813b0612cd2bc36"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.0.3"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -970,6 +1002,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.15.0"
|
version: "5.15.0"
|
||||||
|
win32_registry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
wkt_parser:
|
wkt_parser:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ dependencies:
|
|||||||
# Notifications
|
# Notifications
|
||||||
flutter_local_notifications: ^18.0.1
|
flutter_local_notifications: ^18.0.1
|
||||||
|
|
||||||
|
# Vibration
|
||||||
|
vibration: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|||||||
Reference in New Issue
Block a user