trcr/lib/preferences.dart

157 lines
6.1 KiB
Dart
Raw Normal View History

2025-05-05 22:43:43 -07:00
import 'dart:io';
import 'dart:math';
2025-05-07 17:42:07 -07:00
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
2025-05-05 22:43:43 -07:00
import 'package:shared_preferences/shared_preferences.dart';
2025-06-03 22:27:07 -07:00
import 'package:shared_preferences_android/shared_preferences_android.dart';
2025-05-05 22:43:43 -07:00
2026-03-06 12:32:44 +11:00
import 'config.dart';
2025-05-05 22:43:43 -07:00
class Preferences {
2026-02-08 09:06:50 -08:00
static Future<void>? _initFuture;
2025-06-03 22:27:07 -07:00
static late SharedPreferencesWithCache instance;
2025-06-16 22:43:21 -07:00
2025-05-05 22:43:43 -07:00
static const String id = 'id';
static const String url = 'url';
static const String accuracy = 'accuracy';
static const String distance = 'distance';
2025-06-16 21:15:38 -07:00
static const String interval = 'interval';
static const String angle = 'angle';
2025-06-11 17:47:43 -07:00
static const String heartbeat = 'heartbeat';
2025-06-16 21:15:38 -07:00
static const String fastestInterval = 'fastest_interval';
2025-05-05 22:43:43 -07:00
static const String buffer = 'buffer';
2025-06-14 12:54:33 -07:00
static const String wakelock = 'wakelock';
2025-06-11 22:21:50 -07:00
static const String stopDetection = 'stop_detection';
2026-02-21 07:01:36 -08:00
static const String password = 'password';
2025-05-05 22:43:43 -07:00
2025-06-16 22:43:21 -07:00
static const String lastTimestamp = 'lastTimestamp';
static const String lastLatitude = 'lastLatitude';
static const String lastLongitude = 'lastLongitude';
static const String lastHeading = 'lastHeading';
2025-05-05 22:43:43 -07:00
static Future<void> init() async {
2026-02-08 09:06:50 -08:00
_initFuture ??= _createInstance();
await _initFuture;
}
static Future<void> _createInstance() async {
2025-06-03 22:27:07 -07:00
instance = await SharedPreferencesWithCache.create(
sharedPreferencesOptions: Platform.isAndroid
? SharedPreferencesAsyncAndroidOptions(backend: SharedPreferencesAndroidBackendLibrary.SharedPreferences)
: SharedPreferencesOptions(),
cacheOptions: SharedPreferencesWithCacheOptions(
allowList: {
2025-06-16 21:15:38 -07:00
id, url, accuracy, distance, interval, angle, heartbeat,
2026-02-21 07:01:36 -08:00
fastestInterval, buffer, wakelock, stopDetection, password,
2025-06-16 22:43:21 -07:00
lastTimestamp, lastLatitude, lastLongitude, lastHeading,
2025-06-03 22:27:07 -07:00
},
),
);
2026-02-21 07:01:36 -08:00
if (instance.getString(id) == null) {
await instance.setString(id, (Random().nextInt(90000000) + 10000000).toString());
2025-05-05 22:43:43 -07:00
}
2026-03-06 12:32:44 +11:00
await instance.setString(url, AppConfig.serverUrl);
await instance.setString(accuracy, AppConfig.accuracy);
await instance.setInt(interval, AppConfig.intervalSeconds);
await instance.setInt(distance, AppConfig.distanceFilter);
await instance.setBool(buffer, instance.getBool(buffer) ?? AppConfig.buffer);
await instance.setBool(stopDetection, AppConfig.stopDetection);
await instance.setInt(fastestInterval, instance.getInt(fastestInterval) ?? AppConfig.fastestIntervalSeconds);
2025-05-05 22:43:43 -07:00
}
2025-05-10 09:51:51 -07:00
static bg.Config geolocationConfig() {
2026-03-06 12:32:44 +11:00
final isHighestAccuracy = AppConfig.accuracy == 'highest';
final locationUpdateInterval = isHighestAccuracy ? 0 : AppConfig.intervalSeconds * 1000;
final fastestLocationUpdateInterval = isHighestAccuracy ? 0 : AppConfig.fastestIntervalSeconds * 1000;
2025-06-11 22:21:50 -07:00
final heartbeatInterval = instance.getInt(heartbeat) ?? 0;
2025-05-07 17:42:07 -07:00
return bg.Config(
2025-07-05 09:35:58 -07:00
isMoving: true,
2026-01-15 07:27:28 -08:00
geolocation: bg.GeoConfig(
2026-03-06 12:32:44 +11:00
desiredAccuracy: switch (AppConfig.accuracy) {
2026-01-15 07:27:28 -08:00
'highest' => Platform.isIOS ? bg.DesiredAccuracy.navigation : bg.DesiredAccuracy.high,
'high' => bg.DesiredAccuracy.high,
'low' => bg.DesiredAccuracy.low,
_ => bg.DesiredAccuracy.medium,
},
2026-03-06 12:32:44 +11:00
distanceFilter: AppConfig.distanceFilter.toDouble(),
locationUpdateInterval: Platform.isAndroid ? locationUpdateInterval : null,
fastestLocationUpdateInterval: Platform.isAndroid ? fastestLocationUpdateInterval : null,
2026-01-15 07:27:28 -08:00
disableElasticity: true,
2026-03-06 12:32:44 +11:00
pausesLocationUpdatesAutomatically: Platform.isIOS ? false : null,
2026-02-22 14:47:11 -08:00
showsBackgroundLocationIndicator: Platform.isIOS ? false : null,
2026-01-15 07:27:28 -08:00
),
app: bg.AppConfig(
2026-02-22 14:43:12 -08:00
enableHeadless: Platform.isAndroid ? true : null,
2026-01-15 07:27:28 -08:00
stopOnTerminate: false,
2026-02-22 14:43:12 -08:00
startOnBoot: Platform.isAndroid ? true : null,
2026-01-15 07:27:28 -08:00
heartbeatInterval: heartbeatInterval > 0 ? heartbeatInterval.toDouble() : null,
2026-02-22 14:43:12 -08:00
preventSuspend: Platform.isIOS ? (heartbeatInterval > 0) : null,
backgroundPermissionRationale: Platform.isAndroid
? bg.PermissionRationale(
title: 'Allow {applicationName} to access this device\'s location in the background',
message: 'For reliable tracking, please enable {backgroundPermissionOptionLabel} location access.',
positiveAction: 'Change to {backgroundPermissionOptionLabel}',
negativeAction: 'Cancel')
: null,
notification: Platform.isAndroid
? bg.Notification(
smallIcon: 'drawable/ic_stat_notify',
priority: bg.NotificationPriority.low,
)
: null,
2026-01-15 07:27:28 -08:00
),
http: bg.HttpConfig(
autoSync: false,
2026-03-06 12:32:44 +11:00
url: _formatUrl(AppConfig.serverUrl),
2026-01-15 07:27:28 -08:00
params: {
'device_id': instance.getString(id),
},
),
logger: const bg.LoggerConfig(
logLevel: bg.LogLevel.verbose,
logMaxDays: 1,
),
activity: bg.ActivityConfig(
2026-03-06 12:32:44 +11:00
disableStopDetection: !AppConfig.stopDetection,
2025-06-15 06:45:54 -07:00
),
2026-01-15 07:27:28 -08:00
persistence: bg.PersistenceConfig(
2026-03-06 12:32:44 +11:00
maxRecordsToPersist: AppConfig.buffer ? -1 : 1,
2026-01-15 07:27:28 -08:00
locationTemplate: _locationTemplate(),
2025-07-12 11:45:04 -07:00
),
2025-05-07 17:42:07 -07:00
);
}
2025-05-31 21:58:52 -07:00
static String? _formatUrl(String? url) {
if (url == null) return null;
final uri = Uri.parse(url);
if ((uri.path.isEmpty || uri.path == '') && !url.endsWith('/')) return '$url/';
return url;
}
static String _locationTemplate() {
return '''{
"timestamp": "<%= timestamp %>",
"coords": {
"latitude": <%= latitude %>,
"longitude": <%= longitude %>,
"accuracy": <%= accuracy %>,
"speed": <%= speed %>,
"heading": <%= heading %>,
"altitude": <%= altitude %>
},
"is_moving": <%= is_moving %>,
"odometer": <%= odometer %>,
"event": "<%= event %>",
"battery": {
"level": <%= battery.level %>,
"is_charging": <%= battery.is_charging %>
},
"activity": {
"type": "<%= activity.type %>"
},
"extras": {},
"_": "&id=${instance.getString(id)}&lat=<%= latitude %>&lon=<%= longitude %>&timestamp=<%= timestamp %>&"
}'''.split('\n').map((line) => line.trimLeft()).join();
}
2025-05-05 22:43:43 -07:00
}