WIP: Location tracking using foreground service
This commit is contained in:
parent
24d42c9597
commit
c844e21e76
@ -6,7 +6,12 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
<!--
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||||
|
-->
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
|
|
||||||
|
|
||||||
@ -53,6 +58,17 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<receiver android:name="rekab.app.background_locator.LocatorBroadcastReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true"/>
|
||||||
|
<service android:name="rekab.app.background_locator.LocatorService"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
|
android:exported="true"/>
|
||||||
|
<service android:name="rekab.app.background_locator.IsolateHolderService"
|
||||||
|
android:permission="android.permission.FOREGROUND_SERVICE"
|
||||||
|
android:exported="true"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
<service
|
<service
|
||||||
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
android:name="io.flutter.plugins.androidalarmmanager.AlarmService"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||||
@ -67,5 +83,6 @@
|
|||||||
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
|
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
-->
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:isolate';
|
||||||
|
import 'dart:ui';
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
@ -23,8 +25,11 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|||||||
import 'package:in_app_purchase/in_app_purchase.dart';
|
import 'package:in_app_purchase/in_app_purchase.dart';
|
||||||
import 'plugins/dynamic_multi_column_layout.dart';
|
import 'plugins/dynamic_multi_column_layout.dart';
|
||||||
import 'plugins/spoiler_card.dart';
|
import 'plugins/spoiler_card.dart';
|
||||||
import 'package:workmanager/workmanager.dart' as workManager;
|
//import 'package:workmanager/workmanager.dart' as workManager;
|
||||||
import 'package:geolocator/geolocator.dart';
|
//import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:background_locator/background_locator.dart';
|
||||||
|
import 'package:background_locator/location_dto.dart';
|
||||||
|
import 'package:background_locator/location_settings.dart';
|
||||||
import 'package:battery/battery.dart';
|
import 'package:battery/battery.dart';
|
||||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||||
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview;
|
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart' as standaloneWebview;
|
||||||
@ -214,8 +219,11 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
StreamSubscription _themeChangeSubscription;
|
StreamSubscription _themeChangeSubscription;
|
||||||
AppTheme _currentTheme = AppTheme.defaultTheme;
|
AppTheme _currentTheme = AppTheme.defaultTheme;
|
||||||
|
|
||||||
|
ReceivePort port = ReceivePort();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
||||||
InAppPurchaseConnection.enablePendingPurchases();
|
InAppPurchaseConnection.enablePendingPurchases();
|
||||||
final Stream purchaseUpdates =
|
final Stream purchaseUpdates =
|
||||||
InAppPurchaseConnection.instance.purchaseUpdatedStream;
|
InAppPurchaseConnection.instance.purchaseUpdatedStream;
|
||||||
@ -228,11 +236,18 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
_currentTheme = event.theme;
|
_currentTheme = event.theme;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
/*
|
||||||
workManager.Workmanager.initialize(
|
workManager.Workmanager.initialize(
|
||||||
updateDeviceLocationIsolate,
|
updateDeviceLocationIsolate,
|
||||||
isInDebugMode: false
|
isInDebugMode: false
|
||||||
);
|
);
|
||||||
|
*/
|
||||||
super.initState();
|
super.initState();
|
||||||
|
IsolateNameServer.registerPortWithName(port.sendPort, LocationManager.isolateName);
|
||||||
|
port.listen((dynamic data) {
|
||||||
|
// do something with data
|
||||||
|
});
|
||||||
|
initPlatformState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handlePurchaseUpdates(purchase) {
|
void _handlePurchaseUpdates(purchase) {
|
||||||
@ -253,6 +268,10 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> initPlatformState() async {
|
||||||
|
await BackgroundLocator.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -26,7 +26,7 @@ class LocationManager {
|
|||||||
defaultUpdateIntervalMinutes);
|
defaultUpdateIntervalMinutes);
|
||||||
_isRunning = prefs.getBool("location-enabled") ?? false;
|
_isRunning = prefs.getBool("location-enabled") ?? false;
|
||||||
if (_isRunning) {
|
if (_isRunning) {
|
||||||
await _startLocationService();
|
//await _startLocationService();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +57,20 @@ class LocationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_startLocationService() async {
|
_startLocationService() async {
|
||||||
|
Logger.d('Starting location tracking');
|
||||||
|
BackgroundLocator.registerLocationUpdate(
|
||||||
|
locationCallback,
|
||||||
|
//optional
|
||||||
|
androidNotificationCallback: locationNotificationCallback,
|
||||||
|
settings: LocationSettings(
|
||||||
|
notificationTitle: "HA Client location tracking",
|
||||||
|
notificationMsg: "HA Client is updating your device location",
|
||||||
|
wakeLockTime: 20,
|
||||||
|
autoStop: false,
|
||||||
|
interval: 10
|
||||||
|
),
|
||||||
|
);
|
||||||
|
/*
|
||||||
String webhookId = ConnectionManager().webhookId;
|
String webhookId = ConnectionManager().webhookId;
|
||||||
String httpWebHost = ConnectionManager().httpWebHost;
|
String httpWebHost = ConnectionManager().httpWebHost;
|
||||||
if (webhookId != null && webhookId.isNotEmpty) {
|
if (webhookId != null && webhookId.isNotEmpty) {
|
||||||
@ -100,14 +114,81 @@ class LocationManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
_stopLocationService() async {
|
_stopLocationService() async {
|
||||||
Logger.d("Canceling previous schedule if any...");
|
Logger.d('Stopping location tracking');
|
||||||
await workManager.Workmanager.cancelAll();
|
IsolateNameServer.removePortNameMapping(isolateName);
|
||||||
|
BackgroundLocator.unRegisterLocationUpdate();
|
||||||
|
/*Logger.d("Canceling previous schedule if any...");
|
||||||
|
await workManager.Workmanager.cancelAll();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
static const String isolateName = "HAClientLocatorIsolate";
|
||||||
|
|
||||||
|
static void locationCallback(LocationDto locationDto) async {
|
||||||
|
print('[Background location] Got location: $locationDto');
|
||||||
|
sendLocationData(locationDto);
|
||||||
|
final SendPort send = IsolateNameServer.lookupPortByName(isolateName);
|
||||||
|
send?.send(locationDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> sendLocationData(LocationDto location) async {
|
||||||
|
print('[Background location] Loading settings...');
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
String domain = prefs.getString('hassio-domain');
|
||||||
|
String port = prefs.getString('hassio-port');
|
||||||
|
String webhookId = prefs.getString('app-webhook-id');
|
||||||
|
String httpWebHost =
|
||||||
|
"${prefs.getString('hassio-res-protocol')}://$domain:$port";
|
||||||
|
if (webhookId != null && webhookId.isNotEmpty) {
|
||||||
|
String url = "$httpWebHost/api/webhook/$webhookId";
|
||||||
|
Map<String, String> headers = {};
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
Map data = {
|
||||||
|
"type": "update_location",
|
||||||
|
"data": {
|
||||||
|
"gps": [],
|
||||||
|
"gps_accuracy": 0,
|
||||||
|
"battery": 100
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
if (location.longitude != null && location.latitude != null) {
|
||||||
|
data["data"]["gps"] = [location.latitude, location.longitude];
|
||||||
|
data["data"]["gps_accuracy"] = location.accuracy;
|
||||||
|
print('[Background location] Sending...');
|
||||||
|
try {
|
||||||
|
http.Response response = await http.post(
|
||||||
|
url,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(data)
|
||||||
|
);
|
||||||
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
|
print('[Background location] Success!');
|
||||||
|
} else {
|
||||||
|
print('[Background location] Error sending data: ${response.statusCode}');
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
print('[Background location] Error sending data: $e');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print('[Background location] Error. Location is null');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
print('[Background location] Error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void locationNotificationCallback() {
|
||||||
|
print('[Background location] User clicked on the notification');
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDeviceLocation() async {
|
updateDeviceLocation() async {
|
||||||
|
/*
|
||||||
try {
|
try {
|
||||||
Logger.d("[Foreground location] Started");
|
Logger.d("[Foreground location] Started");
|
||||||
Geolocator geolocator = Geolocator();
|
Geolocator geolocator = Geolocator();
|
||||||
@ -150,10 +231,12 @@ class LocationManager {
|
|||||||
} catch (e, stack) {
|
} catch (e, stack) {
|
||||||
Logger.e('Foreground location error: ${e.toSTring()}', stacktrace: stack);
|
Logger.e('Foreground location error: ${e.toSTring()}', stacktrace: stack);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void updateDeviceLocationIsolate() {
|
void updateDeviceLocationIsolate() {
|
||||||
workManager.Workmanager.executeTask((backgroundTask, data) async {
|
workManager.Workmanager.executeTask((backgroundTask, data) async {
|
||||||
//print("[Background $backgroundTask] Started");
|
//print("[Background $backgroundTask] Started");
|
||||||
@ -242,3 +325,4 @@ void updateDeviceLocationIsolate() {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
*/
|
@ -53,9 +53,11 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_switchLocationTrackingState(bool state) async {
|
_switchLocationTrackingState(bool state) async {
|
||||||
|
/*
|
||||||
if (state) {
|
if (state) {
|
||||||
await LocationManager().updateDeviceLocation();
|
await LocationManager().updateDeviceLocation();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
|
await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
|
||||||
setState(() {
|
setState(() {
|
||||||
_wait = false;
|
_wait = false;
|
||||||
|
@ -27,8 +27,9 @@ dependencies:
|
|||||||
flutter_secure_storage: ^3.3.3
|
flutter_secure_storage: ^3.3.3
|
||||||
device_info: ^0.4.1+4
|
device_info: ^0.4.1+4
|
||||||
flutter_local_notifications: ^1.1.6
|
flutter_local_notifications: ^1.1.6
|
||||||
geolocator: ^5.3.1
|
#geolocator: ^5.3.1
|
||||||
workmanager: ^0.2.2
|
background_locator: ^1.1.3+1
|
||||||
|
#workmanager: ^0.2.2
|
||||||
battery: ^1.0.0
|
battery: ^1.0.0
|
||||||
firebase_crashlytics: ^0.1.3+3
|
firebase_crashlytics: ^0.1.3+3
|
||||||
syncfusion_flutter_core: ^18.1.48
|
syncfusion_flutter_core: ^18.1.48
|
||||||
|
Reference in New Issue
Block a user