diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2652ad7..b3ad3fa 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,17 +8,30 @@ + + android:exported="false" /> + + + + + + + + + + { token: authProvider.token, geoId: shareProvider.geoId, ); + FlutterForegroundTask.setTaskHandler(BackgroundTaskHandler()); await bgGeo.start(); } catch (e) { if (!mounted) return; diff --git a/lib/services/background_geo_service.dart b/lib/services/background_geo_service.dart index 04c4b44..947a21d 100644 --- a/lib/services/background_geo_service.dart +++ b/lib/services/background_geo_service.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'dart:convert'; -import 'package:flutter/foundation.dart'; -import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg; +import 'package:flutter_foreground_task/flutter_foreground_task.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:geolocator/geolocator.dart'; import 'package:http/http.dart' as http; import '../config/api.dart'; @@ -19,11 +19,10 @@ class BackgroundGeoService { String? _token; String? _geoId; bool _isReady = false; - bool _isStarted = false; bool _notificationsInitialized = false; bool get isReady => _isReady; - bool get isStarted => _isStarted; + Future get isStarted async => await FlutterForegroundTask.isRunningService; String? get token => _token; String? get geoId => _geoId; @@ -33,11 +32,7 @@ class BackgroundGeoService { const androidSettings = AndroidInitializationSettings( '@mipmap/ic_launcher', ); - const iosSettings = DarwinInitializationSettings( - requestAlertPermission: true, - requestBadgePermission: true, - requestSoundPermission: true, - ); + const iosSettings = DarwinInitializationSettings(); const initSettings = InitializationSettings( android: androidSettings, iOS: iosSettings, @@ -61,69 +56,59 @@ class BackgroundGeoService { await initNotifications(); - bg.BackgroundGeolocation.onLocation(_onLocation, _onLocationError); - bg.BackgroundGeolocation.onMotionChange(_onMotionChange); - bg.BackgroundGeolocation.onProviderChange(_onProviderChange); - bg.BackgroundGeolocation.onHttp(_onHttp); + FlutterForegroundTask.initCommunicationPort(); - await bg.BackgroundGeolocation.ready(bg.Config( - desiredAccuracy: bg.Config.DESIRED_ACCURACY_HIGH, - distanceFilter: 10.0, - stopOnTerminate: false, - startOnBoot: true, - debug: false, - logLevel: bg.Config.LOG_LEVEL_OFF, - autoSync: false, - )); + FlutterForegroundTask.addTaskDataCallback(_onTaskData); _isReady = true; } Future start() async { if (!_isReady) return; - if (_isStarted) return; if (_token == null || _geoId == null || _geoId!.isEmpty) return; - await bg.BackgroundGeolocation.start(); - _isStarted = true; + final androidOptions = AndroidNotificationOptions( + channelId: 'background_geo_channel', + channelName: 'Background Geolocation', + channelDescription: 'Отслеживание позиции в фоновом режиме', + channelImportance: NotificationChannelImportance.HIGH, + priority: NotificationPriority.HIGH, + ); + + final iosOptions = IOSNotificationOptions( + showNotification: true, + playSound: true, + ); + + final taskOptions = ForegroundTaskOptions( + eventAction: ForegroundTaskEventAction.repeat(15000), + autoRunOnBoot: true, + allowWakeLock: true, + ); + + FlutterForegroundTask.init( + androidNotificationOptions: androidOptions, + iosNotificationOptions: iosOptions, + foregroundTaskOptions: taskOptions, + ); + + await FlutterForegroundTask.startService( + notificationTitle: 'Family Safety', + notificationText: 'Отслеживание позиции активно', + ); } Future stop() async { - if (!_isStarted) return; - await bg.BackgroundGeolocation.stop(); - _isStarted = false; + await FlutterForegroundTask.stopService(); } void dispose() { - bg.BackgroundGeolocation.removeListeners(); _isReady = false; - _isStarted = false; _token = null; _geoId = null; } - void _onLocation(bg.Location location) { - if (_token == null || _geoId == null || _geoId!.isEmpty) return; - if (location.sample == true) return; - - _sendPosition(location.coords.latitude, location.coords.longitude); - } - - void _onLocationError(bg.LocationError error) { - _showErrorNotification('Ошибка геолокации: $error'); - } - - void _onMotionChange(bg.Location location) { - kDebugMode? print('[BackgroundGeo] Motion changed: isMoving=${location.isMoving}'):{}; - } - - void _onProviderChange(bg.ProviderChangeEvent event) { - kDebugMode?print('[BackgroundGeo] Provider changed: $event'):{}; - } - - void _onHttp(bg.HttpEvent event) { - kDebugMode? print('[BackgroundGeo] HTTP: status=${event.status}, success=${event.success}'):{}; - } + static void _onTaskData(Object data) {} Future _sendPosition(double latitude, double longitude) async { if (_token == null || _geoId == null || _geoId!.isEmpty) return; @@ -162,10 +147,7 @@ class BackgroundGeoService { priority: Priority.high, ticker: 'geo_error', ); - const iosDetails = DarwinNotificationDetails( - presentAlert: true, - sound: 'default', - ); + const iosDetails = DarwinNotificationDetails(); const details = NotificationDetails( android: androidDetails, iOS: iosDetails, @@ -184,3 +166,45 @@ class BackgroundGeoService { print('[Notification] ${response.payload}'); } } + +class BackgroundTaskHandler extends TaskHandler { + int _locationCount = 0; + + @override + Future onStart(DateTime timestamp, TaskStarter starter) async {} + + @override + void onRepeatEvent(DateTime timestamp) { + _locationCount++; + + if (_locationCount % 2 != 0) return; + + _getLocationAndSend(); + } + + @override + void onReceiveData(Object data) {} + + @override + void onNotificationButtonPressed(String id) {} + + @override + void onNotificationPressed() {} + + @override + void onNotificationDismissed() {} + + @override + Future onDestroy(DateTime timestamp) async {} + + Future _getLocationAndSend() async { + try { + final position = await Geolocator.getCurrentPosition(); + + final bgGeo = BackgroundGeoService(); + await bgGeo._sendPosition(position.latitude, position.longitude); + } catch (e) { + print('[BackgroundGeo] Error getting location: $e'); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index 2703b3e..a0e37bf 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.1" - background_fetch: - dependency: transitive - description: - name: background_fetch - sha256: a185b471f6ff93c0074e2da98fbdf1e0c5af5a83adb97dfe924a9ef06c9e0175 - url: "https://pub.dev" - source: hosted - version: "1.7.0" boolean_selector: dependency: transitive description: @@ -118,14 +110,14 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_background_geolocation: + flutter_foreground_task: dependency: "direct main" description: - name: flutter_background_geolocation - sha256: "2c5f23bf35837b31c4e1badadf7fb5da418a1862133b5a671b3e0bfa60c3bf2f" + name: flutter_foreground_task + sha256: "206017ee1bf864f34b8d7bce664a172717caa21af8da23f55866470dfe316644" url: "https://pub.dev" source: hosted - version: "4.18.3" + version: "8.17.0" flutter_lints: dependency: "direct dev" description: @@ -668,4 +660,4 @@ packages: version: "6.6.1" sdks: dart: ">=3.10.1 <4.0.0" - flutter: ">=3.38.0" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 57ae472..9596379 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,7 @@ dependencies: latlong2: ^0.9.1 geolocator: ^14.0.2 shared_preferences: ^2.2.2 - flutter_background_geolocation: ^4.13.0 + flutter_foreground_task: ^8.0.0 flutter_local_notifications: ^18.0.1 dev_dependencies: diff --git a/test/widget_test.dart b/test/widget_test.dart index c2a4a2a..cc8b9da 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -16,7 +16,6 @@ void main() { testWidgets('App loads login screen', (WidgetTester tester) async { final bgGeo = BackgroundGeoService(); await bgGeo.init(); - await bgGeo.initNotifications(); await tester.pumpWidget(MyApp( settingsService: SettingsService(), bgGeo: bgGeo,