/** * Location Service * ERP Transportistas * Sprint S8 - TASK-007 * * Background GPS tracking with expo-location and expo-task-manager. */ import * as Location from 'expo-location'; import * as TaskManager from 'expo-task-manager'; import { syncService } from './SyncService'; const LOCATION_TASK_NAME = 'erp-transportistas-location-tracking'; const LOCATION_UPDATE_INTERVAL = 30000; // 30 seconds const LOCATION_DISTANCE_FILTER = 50; // 50 meters // Define the background task TaskManager.defineTask(LOCATION_TASK_NAME, async ({ data, error }) => { if (error) { console.error('Location task error:', error); return; } if (data) { const { locations } = data as { locations: Location.LocationObject[] }; for (const location of locations) { try { await syncService.guardarPosicionGPS( location.coords.latitude, location.coords.longitude, location.coords.speed || 0, location.coords.heading || 0, location.coords.accuracy || 0 ); } catch (err) { console.error('Error saving GPS position:', err); } } } }); class LocationService { private isTracking = false; private foregroundSubscription: Location.LocationSubscription | null = null; async requestPermissions(): Promise { const { status: foregroundStatus } = await Location.requestForegroundPermissionsAsync(); if (foregroundStatus !== 'granted') { return false; } const { status: backgroundStatus } = await Location.requestBackgroundPermissionsAsync(); return backgroundStatus === 'granted'; } async checkPermissions(): Promise<{ foreground: boolean; background: boolean; }> { const foreground = await Location.getForegroundPermissionsAsync(); const background = await Location.getBackgroundPermissionsAsync(); return { foreground: foreground.status === 'granted', background: background.status === 'granted', }; } async getCurrentPosition(): Promise { try { const permissions = await this.checkPermissions(); if (!permissions.foreground) { return null; } return await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High, }); } catch (error) { console.error('Error getting current position:', error); return null; } } async startTracking(): Promise { if (this.isTracking) { return true; } const permissions = await this.checkPermissions(); if (!permissions.foreground) { console.warn('Foreground location permission not granted'); return false; } // Start foreground tracking this.foregroundSubscription = await Location.watchPositionAsync( { accuracy: Location.Accuracy.High, timeInterval: LOCATION_UPDATE_INTERVAL, distanceInterval: LOCATION_DISTANCE_FILTER, }, async (location) => { try { await syncService.guardarPosicionGPS( location.coords.latitude, location.coords.longitude, location.coords.speed || 0, location.coords.heading || 0, location.coords.accuracy || 0 ); } catch (err) { console.error('Error saving foreground GPS position:', err); } } ); // Start background tracking if permitted if (permissions.background) { await this.startBackgroundTracking(); } this.isTracking = true; return true; } private async startBackgroundTracking(): Promise { const isTaskRegistered = await TaskManager.isTaskRegisteredAsync( LOCATION_TASK_NAME ); if (isTaskRegistered) { return; } await Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, { accuracy: Location.Accuracy.High, timeInterval: LOCATION_UPDATE_INTERVAL, distanceInterval: LOCATION_DISTANCE_FILTER, foregroundService: { notificationTitle: 'ERP Transportistas', notificationBody: 'Rastreo de ubicación activo', notificationColor: '#2563eb', }, pausesUpdatesAutomatically: false, showsBackgroundLocationIndicator: true, }); } async stopTracking(): Promise { if (this.foregroundSubscription) { this.foregroundSubscription.remove(); this.foregroundSubscription = null; } const isTaskRegistered = await TaskManager.isTaskRegisteredAsync( LOCATION_TASK_NAME ); if (isTaskRegistered) { await Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME); } this.isTracking = false; } isCurrentlyTracking(): boolean { return this.isTracking; } async getTrackingStatus(): Promise<{ isTracking: boolean; hasPermissions: boolean; backgroundEnabled: boolean; }> { const permissions = await this.checkPermissions(); const isTaskRegistered = await TaskManager.isTaskRegisteredAsync( LOCATION_TASK_NAME ); return { isTracking: this.isTracking, hasPermissions: permissions.foreground, backgroundEnabled: isTaskRegistered, }; } } export const locationService = new LocationService();