Created
October 1, 2024 07:28
-
-
Save hasanisaeed/56ee510a8b3ec4d2ca4385cf7173883a to your computer and use it in GitHub Desktop.
Live Location
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ignore_for_file: empty_catches | |
import 'dart:async'; | |
import 'dart:convert'; | |
import 'package:firestation_manager_app/data/helpers/constants/network_constants.dart'; | |
import 'package:latlong2/latlong.dart'; | |
import 'package:web_socket_channel/web_socket_channel.dart'; | |
import 'dart:developer'; | |
import 'dart:io'; | |
import 'dart:isolate'; | |
import 'dart:ui'; | |
import 'package:firestation_manager_app/data/providers/local/shared_prefs_provider.dart'; | |
import 'package:firestation_manager_app/data/repositories/shared_prefs.dart'; | |
import 'package:firestation_manager_app/services/location_service.dart'; | |
import 'package:flutter/cupertino.dart'; | |
import 'package:geolocator/geolocator.dart'; | |
import 'package:intl/intl.dart'; | |
import 'package:flutter_background_service/flutter_background_service.dart'; | |
class LiveLocationWebSocket { | |
WebSocketChannel? liveLocationChannel; | |
final _messageController = StreamController<String>(); | |
Stream<String> get messages => _messageController.stream; | |
LiveLocationWebSocket(String token) { | |
liveLocationChannel = WebSocketChannel.connect( | |
Uri.parse('wss://${rootDomain}wsf/vehicles/locations/?token=$token')); | |
_listen(); | |
} | |
void _listen() { | |
liveLocationChannel?.stream.listen((event) { | |
_messageController.add(event); | |
}); | |
} | |
void disconnect() { | |
liveLocationChannel?.sink.close(); | |
_messageController.close(); | |
} | |
void sendMessage(Map<String, dynamic> locationInfo) { | |
final String messageToSend = json.encode(locationInfo); | |
if (messageToSend.isNotEmpty) { | |
try { | |
liveLocationChannel?.sink.add(messageToSend); | |
} catch (e) {} | |
} | |
} | |
} | |
late LiveLocationWebSocket websocket; | |
LocationService locationService = LocationService(); | |
SharedPrefsRepository sharedPrefsRepository = | |
SharedPrefsRepository(provider: SharedPrefsProvider()); | |
Future<void> initializeLiveLocationService() async { | |
final service = FlutterBackgroundService(); | |
await service.configure( | |
iosConfiguration: IosConfiguration( | |
autoStart: true, | |
onBackground: onIosLiveLocationBackground, | |
onForeground: onLiveLocationStart, | |
), | |
androidConfiguration: AndroidConfiguration( | |
onStart: onLiveLocationStart, | |
isForegroundMode: true, | |
autoStart: true, | |
autoStartOnBoot: true, | |
// notificationChannelId: 'live_location_channel', | |
initialNotificationTitle: 'Live Location Service Running', | |
initialNotificationContent: 'Broadcasting live location...', | |
foregroundServiceNotificationId: 112236 | |
), | |
); | |
} | |
@pragma('vm:entry-point') | |
void onLiveLocationStart(ServiceInstance service) async { | |
DartPluginRegistrant.ensureInitialized(); | |
var vehicleId = await sharedPrefsRepository.getVehicleId(); | |
print(">> VID: $vehicleId"); | |
_setupServiceListeners(service); | |
_startLocationTracking(service, vehicleId); | |
} | |
void _setupServiceListeners(ServiceInstance service) { | |
if (service is AndroidServiceInstance) { | |
service.on('setLiveLocationForeground').listen((event) { | |
service.setAsForegroundService(); | |
}); | |
service.on('setLiveLocationBackground').listen((event) { | |
service.setAsBackgroundService(); | |
}); | |
} | |
service.on('stopLiveLocationService').listen((event) { | |
service.stopSelf(); | |
}); | |
} | |
void _startLocationTracking(ServiceInstance service, int tripId) { | |
LatLng? lastKnownLocation; | |
const movementThreshold = 2.0; // Movement threshold in meters | |
Timer.periodic(const Duration(seconds: 5), (timer) async { | |
if (service is AndroidServiceInstance && | |
await service.isForegroundService()) { | |
final locationData = await locationService.getCurrentLocation(); | |
if (locationData != null) { | |
LatLng currentLocation = | |
LatLng(locationData.latitude, locationData.longitude); | |
bool hasMoved = _hasUserMoved( | |
lastKnownLocation, currentLocation, movementThreshold); | |
if (hasMoved) { | |
await _handleLocationUpdate(locationData, tripId); | |
lastKnownLocation = currentLocation; | |
} | |
service.setForegroundNotificationInfo( | |
title: "آتش نشانی", content: "برنامه در حال اجرا..."); | |
} | |
} | |
service.invoke('updateLiveLocation'); | |
}); | |
} | |
bool _hasUserMoved( | |
LatLng? lastKnownLocation, LatLng currentLocation, double threshold) { | |
if (lastKnownLocation == null) { | |
return true; | |
} | |
double distance = Geolocator.distanceBetween( | |
lastKnownLocation.latitude, | |
lastKnownLocation.longitude, | |
currentLocation.latitude, | |
currentLocation.longitude, | |
); | |
return distance > threshold; | |
} | |
Future<void> _handleLocationUpdate(Position locationData, int tripId) async { | |
DateTime now = DateTime.now(); | |
String createdAt = DateFormat('yyyy-MM-dd HH:mm:ss').format(now); | |
final Map<String, dynamic> messageMap = { | |
"location": { | |
"latitude": locationData.latitude, | |
"longitude": locationData.longitude | |
}, | |
"created_at": createdAt | |
}; | |
if (await checkInternetConnection()) { | |
await _sendLocationToServer(messageMap); | |
} | |
} | |
Future<void> _sendLocationToServer(Map<String, dynamic> locationInfo) async { | |
try { | |
locationInfo['vehicle_id'] = await sharedPrefsRepository.getVehicleId(); | |
websocket = | |
LiveLocationWebSocket(await sharedPrefsRepository.getAccessToken()); | |
websocket.sendMessage(locationInfo); | |
} catch (e) { | |
log("Error sending location data: $e"); | |
} | |
} | |
@pragma('vm:entry-point') | |
Future<bool> onIosLiveLocationBackground(ServiceInstance service) async { | |
WidgetsFlutterBinding.ensureInitialized(); | |
DartPluginRegistrant.ensureInitialized(); | |
return true; | |
} | |
Future<bool> checkInternetConnection() async { | |
try { | |
final result = await InternetAddress.lookup('google.com'); | |
return result.isNotEmpty && result[0].rawAddress.isNotEmpty; | |
} on SocketException { | |
return false; | |
} | |
} | |
@override | |
void onDestroy(DateTime timestamp, SendPort? sendPort) async { | |
websocket.disconnect(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var roleId = 0; | |
final liveLocationService = FlutterBackgroundService(); | |
class SplashScreenPage extends StatefulWidget { | |
static const String route = '/splash-screen'; | |
const SplashScreenPage({super.key}); | |
... | |
... | |
Widget _buildPage(BuildContext ctx) { | |
return MultiBlocProvider( | |
providers: [ | |
BlocProvider( | |
create: (_) => | |
VersionInfoCubit(repository: getIt.get<AppVersionRepository>()) | |
..fetchVersionInfo()), | |
BlocProvider( | |
create: (_) => | |
RefreshTokenCubit(repository: getIt.get<AuthRepository>())), | |
], | |
child: MultiBlocListener( | |
listeners: [ | |
BlocListener<VersionInfoCubit, VersionInfoState>( | |
listener: (context, state) { | |
if (state.status == StateStatus.success) { | |
if (state.info!.result!.mobileAppVersion!.versionCode! >= | |
_versionCode) { | |
_isNewVersion = true; | |
context.go(AppRoutes.updateInfoPage, extra: state.info!); | |
} | |
} | |
context.read<RefreshTokenCubit>().refresh(); | |
}, | |
), | |
BlocListener<RefreshTokenCubit, RefreshTokenState>( | |
listener: (context, state) async { | |
if (!_isNewVersion) { | |
if (state is RefreshTokenSuccess) { | |
try { | |
dev.log(">> RT: CALLED..."); | |
// Initialize the live location service | |
await initializeCombinedService(); | |
// Use the liveLocationService instance to check if it's already running | |
if (await liveLocationService.isRunning()) { | |
dev.log( | |
">> Live location service already running, setting as foreground..."); | |
// Invoke the correct event for setting the service as foreground | |
liveLocationService.invoke('set As Foreground'); | |
} else { | |
dev.log( | |
">> Live location service not running, starting service..."); | |
// Start the service if it's not already running | |
await liveLocationService.startService(); | |
// Set it as foreground after starting | |
liveLocationService.invoke('set As Foreground'); | |
} | |
} catch (e) { | |
dev.log(">> Error starting live location service: $e"); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment