diff --git a/android/.project b/android/.project
new file mode 100644
index 0000000..3964dd3
--- /dev/null
+++ b/android/.project
@@ -0,0 +1,17 @@
+
+
+ android
+ Project android created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/android/app/.classpath b/android/app/.classpath
new file mode 100644
index 0000000..eb19361
--- /dev/null
+++ b/android/app/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/android/app/.project b/android/app/.project
new file mode 100644
index 0000000..ac485d7
--- /dev/null
+++ b/android/app/.project
@@ -0,0 +1,23 @@
+
+
+ app
+ Project app created by Buildship.
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/Application.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Application.java
index 857bda2..5fde4fb 100644
--- a/android/app/src/main/java/com/keyboardcrumbs/hassclient/Application.java
+++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Application.java
@@ -4,11 +4,13 @@ import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
+import be.tramckrijte.workmanager.WorkmanagerPlugin;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
@Override
public void onCreate() {
super.onCreate();
+ WorkmanagerPlugin.setPluginRegistrantCallback(this);
}
@Override
diff --git a/lib/main.dart b/lib/main.dart
index 9905126..62386ef 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -27,6 +27,8 @@ import 'package:share/share.dart';
import 'plugins/dynamic_multi_column_layout.dart';
import 'plugins/spoiler_card.dart';
import 'package:uni_links/uni_links.dart';
+import 'package:workmanager/workmanager.dart' as workManager;
+import 'package:geolocator/geolocator.dart';
import 'utils/logger.dart';
diff --git a/lib/managers/connection_manager.class.dart b/lib/managers/connection_manager.class.dart
index b3a2765..b09efdf 100644
--- a/lib/managers/connection_manager.class.dart
+++ b/lib/managers/connection_manager.class.dart
@@ -163,8 +163,6 @@ class ConnectionManager {
}
}
-
-
Future _disconnect() {
Completer completer = Completer();
if (!isConnected) {
diff --git a/lib/managers/location_manager.class.dart b/lib/managers/location_manager.class.dart
index 4b3d35f..d79cbf1 100644
--- a/lib/managers/location_manager.class.dart
+++ b/lib/managers/location_manager.class.dart
@@ -2,4 +2,157 @@ part of '../main.dart';
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 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 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);
+ });
}
\ No newline at end of file
diff --git a/lib/pages/main.page.dart b/lib/pages/main.page.dart
index 29232fd..2e748d2 100644
--- a/lib/pages/main.page.dart
+++ b/lib/pages/main.page.dart
@@ -109,6 +109,7 @@ class _MainPageState extends ReceiveShareState with WidgetsBindingObse
_subscribe().then((_) {
ConnectionManager().init(loadSettings: true, forceReconnect: true).then((__){
_fetchData();
+ LocationManager();
StartupUserMessagesManager().checkMessagesToShow();
}, onError: (e) {
_setErrorState(e);
diff --git a/lib/panels/config_panel_widget.dart b/lib/panels/config_panel_widget.dart
index d3133bd..4da87ce 100644
--- a/lib/panels/config_panel_widget.dart
+++ b/lib/panels/config_panel_widget.dart
@@ -9,9 +9,40 @@ class ConfigPanelWidget extends StatefulWidget {
class _ConfigPanelWidgetState extends State {
+ int _locationInterval = LocationManager().defaultUpdateIntervalMinutes;
+ bool _locationTrackingEnabled = false;
+
@override
void 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() {
@@ -92,17 +123,55 @@ class _ConfigPanelWidgetState extends State {
)
],
),
+ Divider(),
+ Text("Location tracking", style: TextStyle(fontSize: Sizes.largeFontSize-2)),
+ Container(height: Sizes.rowPadding,),
+ Row(
+ children: [
+ 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: [
+ //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
void dispose() {
+ LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
super.dispose();
}
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 63a1684..6d454c9 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -25,6 +25,8 @@ dependencies:
flutter_secure_storage: ^3.3.1+1
device_info: ^0.4.0+3
flutter_local_notifications: ^0.8.4
+ geolocator: ^5.1.4+2
+ workmanager: ^0.1.3
share:
git:
url: https://github.com/d-silveira/flutter-share.git