Add wake lock and optimization

This commit is contained in:
Anton Tananaev 2025-06-14 13:26:13 -07:00
parent e0f2bde919
commit 4ac4f1f892
8 changed files with 108 additions and 18 deletions

View file

@ -0,0 +1,56 @@
import 'dart:io';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:traccar_client/preferences.dart';
import 'package:wakelock_partial_android/wakelock_partial_android.dart';
class GeolocationService {
static Future<void> init() async {
await bg.BackgroundGeolocation.ready(Preferences.geolocationConfig());
if (Platform.isAndroid) {
await bg.BackgroundGeolocation.registerHeadlessTask(headlessTask);
}
bg.BackgroundGeolocation.onEnabledChange(onEnabledChange);
bg.BackgroundGeolocation.onMotionChange(onMotionChange);
bg.BackgroundGeolocation.onHeartbeat(onHeartbeat);
}
static Future<void> onEnabledChange(bool enabled) async {
if (Preferences.instance.getBool(Preferences.wakelock) ?? false) {
if (!enabled) {
await WakelockPartialAndroid.release();
}
}
}
static Future<void> onMotionChange(bg.Location location) async {
if (Preferences.instance.getBool(Preferences.wakelock) ?? false) {
if (location.isMoving) {
await WakelockPartialAndroid.acquire();
} else {
await WakelockPartialAndroid.release();
}
}
}
static Future<void> onHeartbeat(bg.HeartbeatEvent event) async {
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1, persist: true);
}
}
@pragma('vm:entry-point')
void headlessTask(bg.HeadlessEvent headlessEvent) async {
await Preferences.init();
switch (headlessEvent.name) {
case bg.Event.ENABLEDCHANGE:
await GeolocationService.onEnabledChange(headlessEvent.event);
break;
case bg.Event.MOTIONCHANGE:
await GeolocationService.onMotionChange(headlessEvent.event);
break;
case bg.Event.HEARTBEAT:
await GeolocationService.onHeartbeat(headlessEvent.event);
break;
}
}

View file

@ -4,6 +4,7 @@
"statusTitle": "Logs",
"saveButton": "Save",
"cancelButton": "Cancel",
"okButton": "OK",
"locationButton": "Send location",
"statusButton": "Show status",
"settingsButton": "Change settings",
@ -25,6 +26,7 @@
"trackingLabel": "Continuous tracking",
"motionLabel": "Active movement",
"advancedLabel": "Advanced settings",
"optimizationMessage": "To ensure reliable tracking, please disable battery optimization for this app.",
"startAction": "Start service",
"stopAction": "Stop service",
"sosAction": "Send SOS"

View file

@ -1,10 +1,8 @@
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:rate_my_app/rate_my_app.dart';
import 'package:traccar_client/geolocation_service.dart';
import 'package:traccar_client/quick_actions.dart';
import 'l10n/app_localizations.dart';
@ -16,23 +14,11 @@ void main() async {
await Firebase.initializeApp();
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterFatalError;
await Preferences.init();
await bg.BackgroundGeolocation.ready(Preferences.geolocationConfig());
if (Platform.isAndroid) {
await bg.BackgroundGeolocation.registerHeadlessTask(headlessTask);
}
bg.BackgroundGeolocation.onHeartbeat((bg.HeartbeatEvent event) async {
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1, persist: true);
});
await Preferences.migrate();
await GeolocationService.init();
runApp(const MainApp());
}
@pragma('vm:entry-point')
void headlessTask(bg.HeadlessEvent headlessEvent) async {
if (headlessEvent.name == bg.Event.HEARTBEAT) {
await bg.BackgroundGeolocation.getCurrentPosition(samples: 1, persist: true);
}
}
class MainApp extends StatefulWidget {
const MainApp({super.key});

View file

@ -45,6 +45,30 @@ class _MainScreenState extends State<MainScreen> {
});
}
Future<void> _checkBatteryOptimizations(BuildContext context) async {
try {
if (!await bg.DeviceSettings.isIgnoringBatteryOptimizations) {
final request = await bg.DeviceSettings.showIgnoreBatteryOptimizations();
if (!request.seen && context.mounted) {
showDialog(
context: context,
builder: (_) => AlertDialog(
content: Text(AppLocalizations.of(context)!.optimizationMessage),
actions: [
TextButton(
onPressed: () => bg.DeviceSettings.show(request),
child: Text(AppLocalizations.of(context)!.okButton),
),
],
),
);
}
}
} catch (error) {
debugPrint(error.toString());
}
}
Widget _buildTrackingCard() {
return Card(
child: Padding(
@ -69,6 +93,7 @@ class _MainScreenState extends State<MainScreen> {
onChanged: (bool value) {
if (value) {
bg.BackgroundGeolocation.start();
_checkBatteryOptimizations(context);
} else {
bg.BackgroundGeolocation.stop();
}

View file

@ -32,6 +32,9 @@ class Preferences {
},
),
);
}
static Future<void> migrate() async {
if (Platform.isAndroid) {
if (instance.get(interval) is String) {
final stringValue = instance.getString(interval);

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background_geolocation/flutter_background_geolocation.dart' as bg;
import 'package:wakelock_partial_android/wakelock_partial_android.dart';
import 'l10n/app_localizations.dart';
import 'preferences.dart';
@ -179,6 +180,14 @@ class _SettingsScreenState extends State<SettingsScreen> {
value: wakelock,
onChanged: (value) async {
await Preferences.instance.setBool(Preferences.wakelock, value);
if (value) {
final state = await bg.BackgroundGeolocation.state;
if (state.isMoving == true) {
WakelockPartialAndroid.acquire();
}
} else {
WakelockPartialAndroid.release();
}
setState(() => wakelock = value);
},
),