erp-transportistas-v2/mobile/src/services/LocationService.ts
Adrian Flores Cortes 6ed7f9e2ec [BACKUP] Pre-restructure workspace backup 2026-01-29
- Updated docs and inventory files
- Added new architecture docs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 17:35:54 -06:00

196 lines
5.1 KiB
TypeScript

/**
* 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<boolean> {
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<Location.LocationObject | null> {
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<boolean> {
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<void> {
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<void> {
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();