Skip to content

Instantly share code, notes, and snippets.

@hasanisaeed
Created October 1, 2024 07:28
Show Gist options
  • Save hasanisaeed/56ee510a8b3ec4d2ca4385cf7173883a to your computer and use it in GitHub Desktop.
Save hasanisaeed/56ee510a8b3ec4d2ca4385cf7173883a to your computer and use it in GitHub Desktop.
Live Location
// 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();
}
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