Custom filtering logic

This commit is contained in:
Anton Tananaev 2025-06-16 22:43:21 -07:00
parent 00599d7b1e
commit 5226e4dd90
6 changed files with 136 additions and 12 deletions

View file

@ -1,7 +1,11 @@
import 'dart:developer' as developer;
import 'dart:io';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:traccar_client/location_cache.dart';
import 'package:traccar_client/preferences.dart';
import 'package:wakelock_partial_android/wakelock_partial_android.dart';
@ -13,6 +17,7 @@ class GeolocationService {
}
bg.BackgroundGeolocation.onEnabledChange(onEnabledChange);
bg.BackgroundGeolocation.onMotionChange(onMotionChange);
bg.BackgroundGeolocation.onLocation(onLocation);
bg.BackgroundGeolocation.onHeartbeat(onHeartbeat);
}
@ -34,9 +39,67 @@ class GeolocationService {
}
}
static Future<void> onLocation(bg.Location location) async {
if (_shouldDelete(location)) {
await bg.BackgroundGeolocation.destroyLocation(location.uuid);
} else {
LocationCache.set(location);
try {
await bg.BackgroundGeolocation.sync();
} catch (error) {
developer.log('Failed to send location', error: error);
}
}
}
static Future<void> onHeartbeat(bg.HeartbeatEvent event) async {
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1, persist: true, extras: {'heartbeat': true});
}
static bool _shouldDelete(bg.Location location) {
if (!location.isMoving) return false;
if (location.extras?.isNotEmpty == true) return false;
final lastLocation = LocationCache.get();
if (lastLocation == null) return false;
final duration = DateTime.parse(location.timestamp).difference(DateTime.parse(lastLocation.timestamp)).inSeconds;
final fastestInterval = Preferences.instance.getInt(Preferences.fastestInterval);
if (fastestInterval != null && duration < fastestInterval) return true;
final distance = _distance(lastLocation, location);
final distanceFilter = Preferences.instance.getInt(Preferences.distance) ?? 0;
if (distanceFilter > 0 && distance >= distanceFilter) return false;
final isHighestAccuracy = Preferences.instance.getString(Preferences.accuracy) == 'highest';
if (distanceFilter == 0 || isHighestAccuracy) {
final intervalFilter = Preferences.instance.getInt(Preferences.interval) ?? 0;
if (intervalFilter > 0 && duration >= intervalFilter) return false;
}
if (isHighestAccuracy && lastLocation.heading >= 0 && location.coords.heading > 0) {
final angle = (location.coords.heading - lastLocation.heading).abs();
final angleFilter = Preferences.instance.getInt(Preferences.angle) ?? 0;
if (angleFilter > 0 && angle >= angleFilter) return false;
}
return true;
}
static double _distance(Location from, bg.Location to) {
const earthRadius = 6371000; // meters
final dLat = _degToRad(to.coords.latitude - from.latitude);
final dLon = _degToRad(to.coords.longitude - from.longitude);
final a = sin(dLat / 2) * sin(dLat / 2) +
cos(_degToRad(from.latitude)) * cos(_degToRad(to.coords.latitude)) * sin(dLon / 2) * sin(dLon / 2);
final c = 2 * atan2(sqrt(a), sqrt(1 - a));
return earthRadius * c;
}
static double _degToRad(double degree) => degree * pi / 180.0;
}
@pragma('vm:entry-point')
@ -49,6 +112,9 @@ void headlessTask(bg.HeadlessEvent headlessEvent) async {
case bg.Event.MOTIONCHANGE:
await GeolocationService.onMotionChange(headlessEvent.event);
break;
case bg.Event.LOCATION:
await GeolocationService.onLocation(headlessEvent.event);
break;
case bg.Event.HEARTBEAT:
await GeolocationService.onHeartbeat(headlessEvent.event);
break;