Resolves #49 Location tracking
This commit is contained in:
parent
079070071e
commit
d1ec4f36cc
17
android/.project
Normal file
17
android/.project
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>android</name>
|
||||||
|
<comment>Project android created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
6
android/app/.classpath
Normal file
6
android/app/.classpath
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
|
||||||
|
<classpathentry kind="output" path="bin/default"/>
|
||||||
|
</classpath>
|
23
android/app/.project
Normal file
23
android/app/.project
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>app</name>
|
||||||
|
<comment>Project app created by Buildship.</comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
@ -4,11 +4,13 @@ import io.flutter.app.FlutterApplication;
|
|||||||
import io.flutter.plugin.common.PluginRegistry;
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
|
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
|
||||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
import be.tramckrijte.workmanager.WorkmanagerPlugin;
|
||||||
|
|
||||||
public class Application extends FlutterApplication implements PluginRegistrantCallback {
|
public class Application extends FlutterApplication implements PluginRegistrantCallback {
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
WorkmanagerPlugin.setPluginRegistrantCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,6 +27,8 @@ import 'package:share/share.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:uni_links/uni_links.dart';
|
import 'package:uni_links/uni_links.dart';
|
||||||
|
import 'package:workmanager/workmanager.dart' as workManager;
|
||||||
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
|
||||||
import 'utils/logger.dart';
|
import 'utils/logger.dart';
|
||||||
|
|
||||||
|
@ -163,8 +163,6 @@ class ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Future _disconnect() {
|
Future _disconnect() {
|
||||||
Completer completer = Completer();
|
Completer completer = Completer();
|
||||||
if (!isConnected) {
|
if (!isConnected) {
|
||||||
|
@ -2,4 +2,157 @@ part of '../main.dart';
|
|||||||
|
|
||||||
class LocationManager {
|
class LocationManager {
|
||||||
|
|
||||||
|
static final LocationManager _instance = LocationManager
|
||||||
|
._internal();
|
||||||
|
|
||||||
|
factory LocationManager() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocationManager._internal() {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
final int defaultUpdateIntervalMinutes = 15;
|
||||||
|
final String alarmId = "ha_location_background";
|
||||||
|
Duration _updateInterval;
|
||||||
|
bool _isEnabled;
|
||||||
|
|
||||||
|
void init() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.reload();
|
||||||
|
_updateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
||||||
|
defaultUpdateIntervalMinutes);
|
||||||
|
_isEnabled = prefs.getBool("location-enabled") ?? false;
|
||||||
|
if (_isEnabled) {
|
||||||
|
_startLocationService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSettings(bool enabled, int interval) async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
if (interval != _updateInterval.inMinutes) {
|
||||||
|
prefs.setInt("location-interval", interval);
|
||||||
|
_updateInterval = Duration(minutes: interval);
|
||||||
|
}
|
||||||
|
if (enabled && !_isEnabled) {
|
||||||
|
Logger.d("Enabling location service");
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.setBool("location-enabled", enabled);
|
||||||
|
_isEnabled = true;
|
||||||
|
_startLocationService();
|
||||||
|
updateDeviceLocation();
|
||||||
|
} else if (!enabled && _isEnabled) {
|
||||||
|
Logger.d("Disabling location service");
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
prefs.setBool("location-enabled", enabled);
|
||||||
|
_isEnabled = false;
|
||||||
|
_stopLocationService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startLocationService() async {
|
||||||
|
Logger.d("Scheduling location update for every ${_updateInterval
|
||||||
|
.inMinutes} minutes...");
|
||||||
|
await workManager.Workmanager.registerPeriodicTask(
|
||||||
|
alarmId,
|
||||||
|
"simplePeriodicTask",
|
||||||
|
frequency: _updateInterval,
|
||||||
|
existingWorkPolicy: workManager.ExistingWorkPolicy.replace,
|
||||||
|
constraints: workManager.Constraints(
|
||||||
|
networkType: workManager.NetworkType.connected
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _stopLocationService() async {
|
||||||
|
Logger.d("Canceling previous schedule if any...");
|
||||||
|
await workManager.Workmanager.cancelByUniqueName(alarmId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDeviceLocation() async {
|
||||||
|
if (_isEnabled) {
|
||||||
|
if (ConnectionManager().webhookId != null &&
|
||||||
|
ConnectionManager().webhookId.isNotEmpty) {
|
||||||
|
String url = "${ConnectionManager()
|
||||||
|
.httpWebHost}/api/webhook/${ConnectionManager().webhookId}";
|
||||||
|
Map<String, String> headers = {};
|
||||||
|
Logger.d("[Location] Getting device location...");
|
||||||
|
Position location = await Geolocator().getCurrentPosition(
|
||||||
|
desiredAccuracy: LocationAccuracy.medium);
|
||||||
|
Logger.d("[Location] Got location: ${location.latitude} ${location
|
||||||
|
.longitude}. Sending home...");
|
||||||
|
int battery = DateTime
|
||||||
|
.now()
|
||||||
|
.hour;
|
||||||
|
var data = {
|
||||||
|
"type": "update_location",
|
||||||
|
"data": {
|
||||||
|
"gps": [location.latitude, location.longitude],
|
||||||
|
"gps_accuracy": location.accuracy,
|
||||||
|
"battery": battery
|
||||||
|
}
|
||||||
|
};
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
await http.post(
|
||||||
|
url,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(data)
|
||||||
|
);
|
||||||
|
Logger.d("[Location] ...done.");
|
||||||
|
} else {
|
||||||
|
Logger.d("[Location] No webhook id. Aborting");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.d("[Location] Location tracking is disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDeviceLocationIsolate() {
|
||||||
|
workManager.Workmanager.executeTask((backgroundTask, _) {
|
||||||
|
print("[Location isolate] Started: $backgroundTask");
|
||||||
|
//Completer completer = Completer();
|
||||||
|
|
||||||
|
SharedPreferences.getInstance().then((prefs){
|
||||||
|
print("[Location isolate] loading settings");
|
||||||
|
String webhookId = prefs.getString('app-webhook-id');
|
||||||
|
String domain = prefs.getString('hassio-domain');
|
||||||
|
String port = prefs.getString('hassio-port');
|
||||||
|
String httpWebHost =
|
||||||
|
"${prefs.getString('hassio-res-protocol')}://$domain:$port";
|
||||||
|
if (webhookId != null && webhookId.isNotEmpty) {
|
||||||
|
Logger.d("[Location isolate] Getting device location...");
|
||||||
|
Geolocator().getCurrentPosition(desiredAccuracy: LocationAccuracy.medium).then((location) {
|
||||||
|
Logger.d("[Location isolate] Got location: ${location.latitude} ${location.longitude}. Sending home...");
|
||||||
|
int battery = DateTime.now().hour;
|
||||||
|
String url = "$httpWebHost/api/webhook/$webhookId";
|
||||||
|
Map<String, String> headers = {};
|
||||||
|
headers["Content-Type"] = "application/json";
|
||||||
|
var data = {
|
||||||
|
"type": "update_location",
|
||||||
|
"data": {
|
||||||
|
"gps": [location.latitude, location.longitude],
|
||||||
|
"gps_accuracy": location.accuracy,
|
||||||
|
"battery": battery
|
||||||
|
}
|
||||||
|
};
|
||||||
|
http.post(
|
||||||
|
url,
|
||||||
|
headers: headers,
|
||||||
|
body: json.encode(data)
|
||||||
|
).catchError((e) {
|
||||||
|
print("[Location isolate] Error sending data: ${e.toString()}");
|
||||||
|
}).then((_) {
|
||||||
|
print("[Location isolate] done!");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
print("[Location isolate] No webhook id. Aborting");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Future.value(true);
|
||||||
|
});
|
||||||
}
|
}
|
@ -109,6 +109,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
|||||||
_subscribe().then((_) {
|
_subscribe().then((_) {
|
||||||
ConnectionManager().init(loadSettings: true, forceReconnect: true).then((__){
|
ConnectionManager().init(loadSettings: true, forceReconnect: true).then((__){
|
||||||
_fetchData();
|
_fetchData();
|
||||||
|
LocationManager();
|
||||||
StartupUserMessagesManager().checkMessagesToShow();
|
StartupUserMessagesManager().checkMessagesToShow();
|
||||||
}, onError: (e) {
|
}, onError: (e) {
|
||||||
_setErrorState(e);
|
_setErrorState(e);
|
||||||
|
@ -9,9 +9,40 @@ class ConfigPanelWidget extends StatefulWidget {
|
|||||||
|
|
||||||
class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
||||||
|
|
||||||
|
int _locationInterval = LocationManager().defaultUpdateIntervalMinutes;
|
||||||
|
bool _locationTrackingEnabled = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_loadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
_loadSettings() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
await prefs.reload();
|
||||||
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
|
setState(() {
|
||||||
|
_locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
||||||
|
_locationInterval = prefs.getInt("location-interval") ?? LocationManager().defaultUpdateIntervalMinutes;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void incLocationInterval() {
|
||||||
|
if (_locationInterval < 720) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void decLocationInterval() {
|
||||||
|
if (_locationInterval > 1) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval - 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
restart() {
|
restart() {
|
||||||
@ -92,17 +123,55 @@ class _ConfigPanelWidgetState extends State<ConfigPanelWidget> {
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Divider(),
|
||||||
|
Text("Location tracking", style: TextStyle(fontSize: Sizes.largeFontSize-2)),
|
||||||
|
Container(height: Sizes.rowPadding,),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Enable device location tracking"),
|
||||||
|
Switch(
|
||||||
|
value: _locationTrackingEnabled,
|
||||||
|
onChanged: (value) {
|
||||||
|
SharedPreferences.getInstance().then((prefs) => prefs.setBool("location-enabled", value));
|
||||||
|
setState(() {
|
||||||
|
_locationTrackingEnabled = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: Sizes.rowPadding,),
|
||||||
|
Text("Location update interval in minutes:"),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
//Expanded(child: Container(),),
|
||||||
|
FlatButton(
|
||||||
|
padding: EdgeInsets.all(0.0),
|
||||||
|
child: Text("+", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
||||||
|
onPressed: () => incLocationInterval(),
|
||||||
|
),
|
||||||
|
Text("$_locationInterval", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
||||||
|
FlatButton(
|
||||||
|
padding: EdgeInsets.all(0.0),
|
||||||
|
child: Text("-", style: TextStyle(fontSize: Sizes.largeFontSize)),
|
||||||
|
onPressed: () => decLocationInterval(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
LinkToWebConfig(name: "Home AssistantConfiguration", url: ConnectionManager().httpWebHost+"/config"),
|
LinkToWebConfig(name: "Home Assistant configuration", url: ConnectionManager().httpWebHost+"/config"),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@ dependencies:
|
|||||||
flutter_secure_storage: ^3.3.1+1
|
flutter_secure_storage: ^3.3.1+1
|
||||||
device_info: ^0.4.0+3
|
device_info: ^0.4.0+3
|
||||||
flutter_local_notifications: ^0.8.4
|
flutter_local_notifications: ^0.8.4
|
||||||
|
geolocator: ^5.1.4+2
|
||||||
|
workmanager: ^0.1.3
|
||||||
share:
|
share:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/d-silveira/flutter-share.git
|
url: https://github.com/d-silveira/flutter-share.git
|
||||||
|
Reference in New Issue
Block a user