parent
3a7f3db6cd
commit
dce93966e3
@ -80,6 +80,7 @@ flutter {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.firebase:firebase-analytics:17.2.2'
|
implementation 'com.google.firebase:firebase-analytics:17.2.2'
|
||||||
implementation 'com.google.firebase:firebase-messaging:20.2.0'
|
implementation 'com.google.firebase:firebase-messaging:20.2.0'
|
||||||
|
implementation 'com.google.android.gms:play-services-location:17.0.0'
|
||||||
implementation 'androidx.work:work-runtime:2.3.4'
|
implementation 'androidx.work:work-runtime:2.3.4'
|
||||||
implementation "androidx.concurrent:concurrent-futures:1.0.0"
|
implementation "androidx.concurrent:concurrent-futures:1.0.0"
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
import android.app.Notification;
|
|
||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
@ -14,7 +12,6 @@ import android.os.IBinder;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
import androidx.work.BackoffPolicy;
|
import androidx.work.BackoffPolicy;
|
||||||
import androidx.work.Constraints;
|
import androidx.work.Constraints;
|
||||||
import androidx.work.Data;
|
import androidx.work.Data;
|
||||||
@ -35,20 +32,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
|
|
||||||
public class LocationUpdatesService extends Service {
|
public class LocationUpdatesService extends Service {
|
||||||
|
|
||||||
private static final String PACKAGE_NAME =
|
|
||||||
"com.keyboardcrumbs.hassclient";
|
|
||||||
|
|
||||||
private static final String TAG = LocationUpdatesService.class.getSimpleName();
|
private static final String TAG = LocationUpdatesService.class.getSimpleName();
|
||||||
|
|
||||||
private static final String CHANNEL_ID = "location_service";
|
|
||||||
|
|
||||||
private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
|
|
||||||
".started_from_notification";
|
|
||||||
|
|
||||||
//private final IBinder mBinder = new LocalBinder();
|
|
||||||
|
|
||||||
private static final int NOTIFICATION_ID = 954311;
|
|
||||||
|
|
||||||
private NotificationManager mNotificationManager;
|
private NotificationManager mNotificationManager;
|
||||||
|
|
||||||
private LocationRequest mLocationRequest;
|
private LocationRequest mLocationRequest;
|
||||||
@ -59,8 +44,6 @@ public class LocationUpdatesService extends Service {
|
|||||||
|
|
||||||
private Handler mServiceHandler;
|
private Handler mServiceHandler;
|
||||||
|
|
||||||
private Location mLocation;
|
|
||||||
|
|
||||||
public LocationUpdatesService() {
|
public LocationUpdatesService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +69,7 @@ public class LocationUpdatesService extends Service {
|
|||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
CharSequence name = "Location updates";
|
CharSequence name = "Location updates";
|
||||||
NotificationChannel mChannel =
|
NotificationChannel mChannel =
|
||||||
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
|
new NotificationChannel(LocationUtils.SERVICE_NOTIFICATION_CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
|
||||||
|
|
||||||
mNotificationManager.createNotificationChannel(mChannel);
|
mNotificationManager.createNotificationChannel(mChannel);
|
||||||
}
|
}
|
||||||
@ -95,15 +78,8 @@ public class LocationUpdatesService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||||
Log.i(TAG, "Service started");
|
Log.i(TAG, "Service started");
|
||||||
boolean startedFromNotification = intent.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION,
|
|
||||||
false);
|
|
||||||
|
|
||||||
// We got here because the user decided to remove location updates from the notification.
|
requestLocationUpdates();
|
||||||
if (startedFromNotification) {
|
|
||||||
stopSelf();
|
|
||||||
} else {
|
|
||||||
requestLocationUpdates();
|
|
||||||
}
|
|
||||||
|
|
||||||
return START_STICKY;
|
return START_STICKY;
|
||||||
}
|
}
|
||||||
@ -132,7 +108,7 @@ public class LocationUpdatesService extends Service {
|
|||||||
mLocationRequest.setPriority(priority);
|
mLocationRequest.setPriority(priority);
|
||||||
mLocationRequest.setInterval(requestInterval);
|
mLocationRequest.setInterval(requestInterval);
|
||||||
mLocationRequest.setFastestInterval(requestInterval);
|
mLocationRequest.setFastestInterval(requestInterval);
|
||||||
startForeground(NOTIFICATION_ID, getNotification());
|
startForeground(LocationUtils.SERVICE_NOTIFICATION_ID, LocationUtils.getNotification(this, null, LocationUtils.SERVICE_NOTIFICATION_CHANNEL_ID));
|
||||||
try {
|
try {
|
||||||
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
|
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
|
||||||
mLocationCallback, Looper.myLooper());
|
mLocationCallback, Looper.myLooper());
|
||||||
@ -141,40 +117,10 @@ public class LocationUpdatesService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification getNotification() {
|
|
||||||
Intent intent = new Intent(this, LocationUpdatesService.class);
|
|
||||||
|
|
||||||
CharSequence text = LocationUtils.getLocationText(mLocation);
|
|
||||||
|
|
||||||
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
|
|
||||||
|
|
||||||
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
|
|
||||||
new Intent(this, MainActivity.class), 0);
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
|
|
||||||
.addAction(R.drawable.blank_icon, "Open app",
|
|
||||||
activityPendingIntent)
|
|
||||||
.addAction(R.drawable.blank_icon, "Stop tracking",
|
|
||||||
servicePendingIntent)
|
|
||||||
.setContentText(text)
|
|
||||||
.setPriority(-1)
|
|
||||||
.setContentTitle(LocationUtils.getLocationTitle(mLocation))
|
|
||||||
.setOngoing(true)
|
|
||||||
.setSmallIcon(R.drawable.mini_icon)
|
|
||||||
.setWhen(System.currentTimeMillis());
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onNewLocation(Location location) {
|
private void onNewLocation(Location location) {
|
||||||
Log.i(TAG, "New location: " + location);
|
Log.i(TAG, "New location: " + location);
|
||||||
|
|
||||||
mLocation = location;
|
mNotificationManager.notify(LocationUtils.SERVICE_NOTIFICATION_ID, LocationUtils.getNotification(this, location, LocationUtils.SERVICE_NOTIFICATION_CHANNEL_ID));
|
||||||
|
|
||||||
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
|
|
||||||
|
|
||||||
Constraints constraints = new Constraints.Builder()
|
Constraints constraints = new Constraints.Builder()
|
||||||
.setRequiredNetworkType(NetworkType.CONNECTED)
|
.setRequiredNetworkType(NetworkType.CONNECTED)
|
||||||
@ -182,9 +128,9 @@ public class LocationUpdatesService extends Service {
|
|||||||
|
|
||||||
Data locationData = new Data.Builder()
|
Data locationData = new Data.Builder()
|
||||||
.putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_LOCATION)
|
.putInt(SendDataHomeWorker.DATA_TYPE_KEY, SendDataHomeWorker.DATA_TYPE_LOCATION)
|
||||||
.putDouble("Lat", mLocation.getLatitude())
|
.putDouble("Lat", location.getLatitude())
|
||||||
.putDouble("Long", mLocation.getLongitude())
|
.putDouble("Long", location.getLongitude())
|
||||||
.putFloat("Acc", mLocation.getAccuracy())
|
.putFloat("Acc", location.getAccuracy())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.app.AlarmManager;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
@ -25,6 +29,8 @@ import com.google.common.util.concurrent.ListenableFuture;
|
|||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||||
|
|
||||||
public class LocationUpdatesWorker extends ListenableWorker {
|
public class LocationUpdatesWorker extends ListenableWorker {
|
||||||
|
|
||||||
private Context currentContext;
|
private Context currentContext;
|
||||||
@ -78,6 +84,22 @@ public class LocationUpdatesWorker extends ListenableWorker {
|
|||||||
WorkManager
|
WorkManager
|
||||||
.getInstance(getApplicationContext())
|
.getInstance(getApplicationContext())
|
||||||
.enqueueUniqueWork("SendLocationUpdate", ExistingWorkPolicy.REPLACE, uploadWorkRequest);
|
.enqueueUniqueWork("SendLocationUpdate", ExistingWorkPolicy.REPLACE, uploadWorkRequest);
|
||||||
|
if (LocationUtils.showNotification(currentContext)) {
|
||||||
|
NotificationManager notificationManager;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||||
|
notificationManager = currentContext.getSystemService(NotificationManager.class);
|
||||||
|
} else {
|
||||||
|
notificationManager = (NotificationManager)currentContext.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
CharSequence name = "Location updates";
|
||||||
|
NotificationChannel mChannel =
|
||||||
|
new NotificationChannel(LocationUtils.WORKER_NOTIFICATION_CHANNEL_ID, name, NotificationManager.IMPORTANCE_LOW);
|
||||||
|
|
||||||
|
notificationManager.createNotificationChannel(mChannel);
|
||||||
|
}
|
||||||
|
notificationManager.notify(LocationUtils.WORKER_NOTIFICATION_ID, LocationUtils.getNotification(currentContext, location, LocationUtils.WORKER_NOTIFICATION_CHANNEL_ID));
|
||||||
|
}
|
||||||
finish();
|
finish();
|
||||||
completer.set(Result.success());
|
completer.set(Result.success());
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package com.keyboardcrumbs.hassclient;
|
package com.keyboardcrumbs.hassclient;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||||
import androidx.work.PeriodicWorkRequest;
|
import androidx.work.PeriodicWorkRequest;
|
||||||
import androidx.work.WorkManager;
|
import androidx.work.WorkManager;
|
||||||
@ -18,6 +21,12 @@ class LocationUtils {
|
|||||||
static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.location-updates-state";
|
static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.location-updates-state";
|
||||||
static final String KEY_LOCATION_UPDATE_INTERVAL = "flutter.location-updates-interval";
|
static final String KEY_LOCATION_UPDATE_INTERVAL = "flutter.location-updates-interval";
|
||||||
static final String KEY_LOCATION_UPDATE_PRIORITY = "flutter.location-updates-priority";
|
static final String KEY_LOCATION_UPDATE_PRIORITY = "flutter.location-updates-priority";
|
||||||
|
static final String KEY_LOCATION_SHOW_NOTIFICATION = "flutter.location-updates-show-notification";
|
||||||
|
|
||||||
|
static final String WORKER_NOTIFICATION_CHANNEL_ID = "location_worker";
|
||||||
|
static final int WORKER_NOTIFICATION_ID = 954322;
|
||||||
|
static final String SERVICE_NOTIFICATION_CHANNEL_ID = "location_service";
|
||||||
|
static final int SERVICE_NOTIFICATION_ID = 954311;
|
||||||
|
|
||||||
static final String LOCATION_WORK_NAME = "HALocationWorker";
|
static final String LOCATION_WORK_NAME = "HALocationWorker";
|
||||||
|
|
||||||
@ -40,6 +49,10 @@ class LocationUtils {
|
|||||||
return (int) context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_PRIORITY, 102);
|
return (int) context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_PRIORITY, 102);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean showNotification(Context context) {
|
||||||
|
return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getBoolean(KEY_LOCATION_SHOW_NOTIFICATION, true);
|
||||||
|
}
|
||||||
|
|
||||||
static void setLocationUpdatesState(Context context, int locationUpdatesState) {
|
static void setLocationUpdatesState(Context context, int locationUpdatesState) {
|
||||||
context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
|
||||||
.edit()
|
.edit()
|
||||||
@ -70,6 +83,25 @@ class LocationUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Notification getNotification(Context context, Location location, String channelId) {
|
||||||
|
CharSequence text = LocationUtils.getLocationText(location);
|
||||||
|
|
||||||
|
PendingIntent activityPendingIntent = PendingIntent.getActivity(context, 0,
|
||||||
|
new Intent(context, MainActivity.class), 0);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId)
|
||||||
|
.addAction(R.drawable.blank_icon, "Open HA Client",
|
||||||
|
activityPendingIntent)
|
||||||
|
.setContentText(text)
|
||||||
|
.setPriority(-1)
|
||||||
|
.setContentTitle(LocationUtils.getLocationTitle(location))
|
||||||
|
.setOngoing(true)
|
||||||
|
.setSmallIcon(R.drawable.mini_icon)
|
||||||
|
.setWhen(System.currentTimeMillis());
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
static void startWorker(Context context, long interval) {
|
static void startWorker(Context context, long interval) {
|
||||||
PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(LocationUpdatesWorker.class, interval, TimeUnit.MILLISECONDS)
|
PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(LocationUpdatesWorker.class, interval, TimeUnit.MILLISECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
@ -9,10 +9,10 @@ import io.flutter.embedding.engine.FlutterEngine;
|
|||||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
@ -82,6 +82,10 @@ public class MainActivity extends FlutterActivity {
|
|||||||
stopLocationUpdates();
|
stopLocationUpdates();
|
||||||
result.success("");
|
result.success("");
|
||||||
break;
|
break;
|
||||||
|
case "cancelOldLocationWorker":
|
||||||
|
WorkManager.getInstance(this).cancelAllWorkByTag("haclocation");
|
||||||
|
result.success("");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -107,6 +111,13 @@ public class MainActivity extends FlutterActivity {
|
|||||||
Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class);
|
Intent myService = new Intent(MainActivity.this, LocationUpdatesService.class);
|
||||||
stopService(myService);
|
stopService(myService);
|
||||||
WorkManager.getInstance(this).cancelUniqueWork(LocationUtils.LOCATION_WORK_NAME);
|
WorkManager.getInstance(this).cancelUniqueWork(LocationUtils.LOCATION_WORK_NAME);
|
||||||
|
NotificationManager notificationManager;
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 23) {
|
||||||
|
notificationManager = getSystemService(NotificationManager.class);
|
||||||
|
} else {
|
||||||
|
notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||||
|
}
|
||||||
|
notificationManager.cancel(LocationUtils.WORKER_NOTIFICATION_ID);
|
||||||
LocationUtils.setLocationUpdatesState(this, LocationUtils.LOCATION_UPDATES_DISABLED);
|
LocationUtils.setLocationUpdatesState(this, LocationUtils.LOCATION_UPDATES_DISABLED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,9 +22,6 @@ import 'package:device_info/device_info.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:geolocator/geolocator.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;
|
||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
@ -117,7 +114,6 @@ part 'pages/entity.page.dart';
|
|||||||
part 'utils/mdi.class.dart';
|
part 'utils/mdi.class.dart';
|
||||||
part 'entity_collection.class.dart';
|
part 'entity_collection.class.dart';
|
||||||
part 'managers/auth_manager.class.dart';
|
part 'managers/auth_manager.class.dart';
|
||||||
part 'managers/location_manager.class.dart';
|
|
||||||
part 'managers/mobile_app_integration_manager.class.dart';
|
part 'managers/mobile_app_integration_manager.class.dart';
|
||||||
part 'managers/connection_manager.class.dart';
|
part 'managers/connection_manager.class.dart';
|
||||||
part 'managers/device_info_manager.class.dart';
|
part 'managers/device_info_manager.class.dart';
|
||||||
@ -230,10 +226,6 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
_currentTheme = event.theme;
|
_currentTheme = event.theme;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
workManager.Workmanager.initialize(
|
|
||||||
updateDeviceLocationIsolate,
|
|
||||||
isInDebugMode: false
|
|
||||||
);
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,8 @@ class AppSettings {
|
|||||||
|
|
||||||
static const AUTH_TOKEN_KEY = 'llt';
|
static const AUTH_TOKEN_KEY = 'llt';
|
||||||
|
|
||||||
|
static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native');
|
||||||
|
|
||||||
static final AppSettings _instance = AppSettings._internal();
|
static final AppSettings _instance = AppSettings._internal();
|
||||||
|
|
||||||
factory AppSettings() {
|
factory AppSettings() {
|
||||||
@ -31,10 +33,7 @@ class AppSettings {
|
|||||||
bool nextAlarmSensorCreated = false;
|
bool nextAlarmSensorCreated = false;
|
||||||
DisplayMode displayMode;
|
DisplayMode displayMode;
|
||||||
AppTheme appTheme;
|
AppTheme appTheme;
|
||||||
final int defaultLocationUpdateIntervalMinutes = 20;
|
final int defaultLocationUpdateIntervalSeconds = 900;
|
||||||
final int defaultActiveLocationUpdateIntervalSeconds = 900;
|
|
||||||
Duration locationUpdateInterval;
|
|
||||||
bool locationTrackingEnabled = false;
|
|
||||||
|
|
||||||
bool get isAuthenticated => longLivedToken != null;
|
bool get isAuthenticated => longLivedToken != null;
|
||||||
bool get isTempAuthenticated => tempToken != null;
|
bool get isTempAuthenticated => tempToken != null;
|
||||||
@ -50,6 +49,7 @@ class AppSettings {
|
|||||||
await Hive.openBox(DEFAULT_HIVE_BOX);
|
await Hive.openBox(DEFAULT_HIVE_BOX);
|
||||||
Logger.d('Loading settings...');
|
Logger.d('Loading settings...');
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
await migrate(prefs);
|
||||||
_domain = prefs.getString('hassio-domain');
|
_domain = prefs.getString('hassio-domain');
|
||||||
_port = prefs.getString('hassio-port');
|
_port = prefs.getString('hassio-port');
|
||||||
webhookId = prefs.getString('app-webhook-id');
|
webhookId = prefs.getString('app-webhook-id');
|
||||||
@ -60,10 +60,6 @@ class AppSettings {
|
|||||||
"${prefs.getString('hassio-protocol')}://$_domain:$_port/api/websocket";
|
"${prefs.getString('hassio-protocol')}://$_domain:$_port/api/websocket";
|
||||||
httpWebHost =
|
httpWebHost =
|
||||||
"${prefs.getString('hassio-res-protocol')}://$_domain:$_port";
|
"${prefs.getString('hassio-res-protocol')}://$_domain:$_port";
|
||||||
locationUpdateInterval = Duration(minutes: prefs.getInt("location-interval") ??
|
|
||||||
defaultLocationUpdateIntervalMinutes);
|
|
||||||
locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
|
||||||
nextAlarmSensorCreated = prefs.getBool("next-alarm-sensor-created") ?? false;
|
|
||||||
longLivedToken = Hive.box(DEFAULT_HIVE_BOX).get(AUTH_TOKEN_KEY);
|
longLivedToken = Hive.box(DEFAULT_HIVE_BOX).get(AUTH_TOKEN_KEY);
|
||||||
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent(
|
oauthUrl = "$httpWebHost/auth/authorize?client_id=${Uri.encodeComponent(
|
||||||
'https://ha-client.app')}&redirect_uri=${Uri
|
'https://ha-client.app')}&redirect_uri=${Uri
|
||||||
@ -72,6 +68,37 @@ class AppSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future migrate(SharedPreferences prefs) async {
|
||||||
|
//Migrating to new location tracking. TODO: Remove when no version 1.2.0 (and older) in the wild
|
||||||
|
if (prefs.getBool("location-tracking-migrated") == null) {
|
||||||
|
Logger.d("[MIGRATION] Migrating to new location tracking...");
|
||||||
|
bool oldLocationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
||||||
|
if (oldLocationTrackingEnabled) {
|
||||||
|
await platform.invokeMethod('cancelOldLocationWorker');
|
||||||
|
await prefs.setInt("location-updates-state", 2); //Setting new location tracking mode to worker
|
||||||
|
await prefs.setInt("location-updates-priority", 100); //Setting location accuracy to high
|
||||||
|
int oldLocationTrackingInterval = prefs.getInt("location-interval") ?? 0;
|
||||||
|
if (oldLocationTrackingInterval < 15) {
|
||||||
|
oldLocationTrackingInterval = 15;
|
||||||
|
}
|
||||||
|
await prefs.setInt("location-updates-interval", oldLocationTrackingInterval * 60); //moving old interval in minutes to new interval in seconds
|
||||||
|
try {
|
||||||
|
await platform.invokeMethod('startLocationService');
|
||||||
|
} catch (e) {
|
||||||
|
await prefs.setInt("location-updates-state", 0); //Disabling location tracking if can't start
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Logger.d("[MIGRATION] Old location tracking was disabled");
|
||||||
|
await prefs.setInt("location-updates-state", 0); //Setting new location tracking mode to disabled
|
||||||
|
}
|
||||||
|
await prefs.setBool("location-tracking-migrated", true);
|
||||||
|
}
|
||||||
|
//Migrating from integration without next alarm sensor. TODO: remove when no version 1.1.2 (and older) in the wild
|
||||||
|
nextAlarmSensorCreated = prefs.getBool("next-alarm-sensor-created") ?? false;
|
||||||
|
//Done
|
||||||
|
Logger.d("[MIGRATION] Done.");
|
||||||
|
}
|
||||||
|
|
||||||
Future<dynamic> loadSingle(String key) async {
|
Future<dynamic> loadSingle(String key) async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
return prefs.get('$key');
|
return prefs.get('$key');
|
||||||
|
@ -1,229 +0,0 @@
|
|||||||
part of '../main.dart';
|
|
||||||
|
|
||||||
class LocationManager {
|
|
||||||
|
|
||||||
static final LocationManager _instance = LocationManager
|
|
||||||
._internal();
|
|
||||||
|
|
||||||
factory LocationManager() {
|
|
||||||
return _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocationManager._internal() {
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
final String backgroundTaskId = "haclocationtask0";
|
|
||||||
final String backgroundTaskTag = "haclocation";
|
|
||||||
|
|
||||||
void init() async {
|
|
||||||
if (AppSettings().locationTrackingEnabled) {
|
|
||||||
await _startLocationService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSettings(bool enabled, int interval) async {
|
|
||||||
if (interval != AppSettings().locationUpdateInterval.inMinutes) {
|
|
||||||
await _stopLocationService();
|
|
||||||
}
|
|
||||||
AppSettings().save({
|
|
||||||
'location-interval': interval,
|
|
||||||
'location-enabled': enabled
|
|
||||||
});
|
|
||||||
AppSettings().locationUpdateInterval = Duration(minutes: interval);
|
|
||||||
AppSettings().locationTrackingEnabled = enabled;
|
|
||||||
if (enabled && !AppSettings().locationTrackingEnabled) {
|
|
||||||
Logger.d("Starting location tracking");
|
|
||||||
await _startLocationService();
|
|
||||||
} else if (!enabled && AppSettings().locationTrackingEnabled) {
|
|
||||||
Logger.d("Stopping location tracking...");
|
|
||||||
await _stopLocationService();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_startLocationService() async {
|
|
||||||
String webhookId = AppSettings().webhookId;
|
|
||||||
String httpWebHost = AppSettings().httpWebHost;
|
|
||||||
if (webhookId != null && webhookId.isNotEmpty) {
|
|
||||||
Duration interval;
|
|
||||||
int delayFactor;
|
|
||||||
int taskCount;
|
|
||||||
Logger.d("Starting location update for every ${AppSettings().locationUpdateInterval
|
|
||||||
.inMinutes} minutes...");
|
|
||||||
if (AppSettings().locationUpdateInterval.inMinutes == 10) {
|
|
||||||
interval = Duration(minutes: 20);
|
|
||||||
taskCount = 2;
|
|
||||||
delayFactor = 10;
|
|
||||||
} else if (AppSettings().locationUpdateInterval.inMinutes == 5) {
|
|
||||||
interval = Duration(minutes: 15);
|
|
||||||
taskCount = 3;
|
|
||||||
delayFactor = 5;
|
|
||||||
} else {
|
|
||||||
interval = AppSettings().locationUpdateInterval;
|
|
||||||
taskCount = 1;
|
|
||||||
delayFactor = 0;
|
|
||||||
}
|
|
||||||
for (int i = 1; i <= taskCount; i++) {
|
|
||||||
int delay = i*delayFactor;
|
|
||||||
Logger.d("Scheduling location update task #$i for every ${interval.inMinutes} minutes in $delay minutes...");
|
|
||||||
await workManager.Workmanager.registerPeriodicTask(
|
|
||||||
"$backgroundTaskId$i",
|
|
||||||
"haClientLocationTracking-0$i",
|
|
||||||
tag: backgroundTaskTag,
|
|
||||||
inputData: {
|
|
||||||
"webhookId": webhookId,
|
|
||||||
"httpWebHost": httpWebHost
|
|
||||||
},
|
|
||||||
frequency: interval,
|
|
||||||
initialDelay: Duration(minutes: delay),
|
|
||||||
existingWorkPolicy: workManager.ExistingWorkPolicy.keep,
|
|
||||||
backoffPolicy: workManager.BackoffPolicy.linear,
|
|
||||||
backoffPolicyDelay: interval,
|
|
||||||
constraints: workManager.Constraints(
|
|
||||||
networkType: workManager.NetworkType.connected,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_stopLocationService() async {
|
|
||||||
Logger.d("Canceling previous schedule if any...");
|
|
||||||
await workManager.Workmanager.cancelAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDeviceLocation() async {
|
|
||||||
try {
|
|
||||||
Logger.d("[Foreground location] Started");
|
|
||||||
Geolocator geolocator = Geolocator();
|
|
||||||
var battery = Battery();
|
|
||||||
String webhookId = AppSettings().webhookId;
|
|
||||||
String httpWebHost = AppSettings().httpWebHost;
|
|
||||||
if (webhookId != null && webhookId.isNotEmpty) {
|
|
||||||
Logger.d("[Foreground location] Getting battery level...");
|
|
||||||
int batteryLevel = await battery.batteryLevel;
|
|
||||||
Logger.d("[Foreground location] Getting device location...");
|
|
||||||
Position position = await geolocator.getCurrentPosition(
|
|
||||||
desiredAccuracy: LocationAccuracy.high,
|
|
||||||
locationPermissionLevel: GeolocationPermission.locationAlways
|
|
||||||
);
|
|
||||||
if (position != null) {
|
|
||||||
Logger.d("[Foreground location] Location: ${position.latitude} ${position.longitude}. Accuracy: ${position.accuracy}. (${position.timestamp})");
|
|
||||||
String url = "$httpWebHost/api/webhook/$webhookId";
|
|
||||||
Map data = {
|
|
||||||
"type": "update_location",
|
|
||||||
"data": {
|
|
||||||
"gps": [position.latitude, position.longitude],
|
|
||||||
"gps_accuracy": position.accuracy,
|
|
||||||
"battery": batteryLevel ?? 100
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Logger.d("[Foreground location] Sending data home...");
|
|
||||||
http.Response response = await http.post(
|
|
||||||
url,
|
|
||||||
headers: {"Content-Type": "application/json"},
|
|
||||||
body: json.encode(data)
|
|
||||||
);
|
|
||||||
if (response.statusCode >= 300) {
|
|
||||||
Logger.e('Foreground location update error: ${response.body}');
|
|
||||||
}
|
|
||||||
Logger.d("[Foreground location] Got HTTP ${response.statusCode}");
|
|
||||||
} else {
|
|
||||||
Logger.d("[Foreground location] No location. Aborting.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e, stack) {
|
|
||||||
Logger.e('Foreground location error: ${e.toSTring()}', stacktrace: stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void updateDeviceLocationIsolate() {
|
|
||||||
workManager.Workmanager.executeTask((backgroundTask, data) async {
|
|
||||||
//print("[Background $backgroundTask] Started");
|
|
||||||
Geolocator geolocator = Geolocator();
|
|
||||||
var battery = Battery();
|
|
||||||
String webhookId = data["webhookId"];
|
|
||||||
String httpWebHost = data["httpWebHost"];
|
|
||||||
//String logData = '==> ${DateTime.now()} [Background $backgroundTask]:';
|
|
||||||
//print("[Background $backgroundTask] Getting path for log file...");
|
|
||||||
//final logFileDirectory = await getExternalStorageDirectory();
|
|
||||||
//print("[Background $backgroundTask] Opening log file...");
|
|
||||||
//File logFile = File('${logFileDirectory.path}/ha-client-background-log.txt');
|
|
||||||
//print("[Background $backgroundTask] Log file path: ${logFile.path}");
|
|
||||||
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
|
|
||||||
}
|
|
||||||
};
|
|
||||||
//print("[Background $backgroundTask] Getting battery level...");
|
|
||||||
int batteryLevel;
|
|
||||||
try {
|
|
||||||
batteryLevel = await battery.batteryLevel;
|
|
||||||
//print("[Background $backgroundTask] Got battery level: $batteryLevel");
|
|
||||||
} catch(e) {
|
|
||||||
//print("[Background $backgroundTask] Error getting battery level: $e. Setting zero");
|
|
||||||
batteryLevel = 0;
|
|
||||||
//logData += 'Battery: error, $e';
|
|
||||||
}
|
|
||||||
if (batteryLevel != null) {
|
|
||||||
data["data"]["battery"] = batteryLevel;
|
|
||||||
//logData += 'Battery: success, $batteryLevel';
|
|
||||||
}/* else {
|
|
||||||
logData += 'Battery: error, level is null';
|
|
||||||
}*/
|
|
||||||
Position location;
|
|
||||||
try {
|
|
||||||
location = await geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high, locationPermissionLevel: GeolocationPermission.locationAlways);
|
|
||||||
if (location != null && location.latitude != null) {
|
|
||||||
//logData += ' || Location: success, ${location.latitude} ${location.longitude} (${location.timestamp})';
|
|
||||||
data["data"]["gps"] = [location.latitude, location.longitude];
|
|
||||||
data["data"]["gps_accuracy"] = location.accuracy;
|
|
||||||
try {
|
|
||||||
http.Response response = await http.post(
|
|
||||||
url,
|
|
||||||
headers: headers,
|
|
||||||
body: json.encode(data)
|
|
||||||
);
|
|
||||||
/*if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
||||||
logData += ' || Post: success, ${response.statusCode}';
|
|
||||||
} else {
|
|
||||||
logData += ' || Post: error, ${response.statusCode}';
|
|
||||||
}*/
|
|
||||||
} catch(e) {
|
|
||||||
//logData += ' || Post: error, $e';
|
|
||||||
}
|
|
||||||
}/* else {
|
|
||||||
logData += ' || Location: error, location is null';
|
|
||||||
}*/
|
|
||||||
} catch (e) {
|
|
||||||
//print("[Background $backgroundTask] Location error: $e");
|
|
||||||
//logData += ' || Location: error, $e';
|
|
||||||
}
|
|
||||||
}/* else {
|
|
||||||
logData += 'Not configured';
|
|
||||||
}*/
|
|
||||||
//print("[Background $backgroundTask] Writing log data...");
|
|
||||||
/*try {
|
|
||||||
var fileMode;
|
|
||||||
if (logFile.existsSync() && logFile.lengthSync() < 5000000) {
|
|
||||||
fileMode = FileMode.append;
|
|
||||||
} else {
|
|
||||||
fileMode = FileMode.write;
|
|
||||||
}
|
|
||||||
await logFile.writeAsString('$logData\n', mode: fileMode);
|
|
||||||
} catch (e) {
|
|
||||||
print("[Background $backgroundTask] Error writing log: $e");
|
|
||||||
}
|
|
||||||
print("[Background $backgroundTask] Finished.");*/
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
@ -56,7 +56,6 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
SharedPreferences.getInstance().then((prefs) {
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
HomeAssistant().currentDashboardPath = prefs.getString('lovelace_dashboard_url') ?? HomeAssistant.DEFAULT_DASHBOARD;
|
HomeAssistant().currentDashboardPath = prefs.getString('lovelace_dashboard_url') ?? HomeAssistant.DEFAULT_DASHBOARD;
|
||||||
_fetchData(useCache: true);
|
_fetchData(useCache: true);
|
||||||
LocationManager();
|
|
||||||
StartupUserMessagesManager().checkMessagesToShow();
|
StartupUserMessagesManager().checkMessagesToShow();
|
||||||
MobileAppIntegrationManager.checkAppRegistration();
|
MobileAppIntegrationManager.checkAppRegistration();
|
||||||
});
|
});
|
||||||
|
@ -19,12 +19,10 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
105: "Passive (last known location)",
|
105: "Passive (last known location)",
|
||||||
};
|
};
|
||||||
|
|
||||||
int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes;
|
Duration _locationInterval;
|
||||||
int _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds;
|
|
||||||
bool _locationTrackingEnabled = false;
|
bool _locationTrackingEnabled = false;
|
||||||
bool _foregroundLocationTrackingEnabled = false;
|
|
||||||
bool _wait = false;
|
bool _wait = false;
|
||||||
bool _changedHere = false;
|
bool _showNotification = true;
|
||||||
int _accuracy = 102;
|
int _accuracy = 102;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -39,75 +37,72 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
await prefs.reload();
|
await prefs.reload();
|
||||||
SharedPreferences.getInstance().then((prefs) {
|
SharedPreferences.getInstance().then((prefs) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
|
|
||||||
_accuracy = prefs.getInt("location-updates-priority") ?? 102;
|
_accuracy = prefs.getInt("location-updates-priority") ?? 102;
|
||||||
_foregroundLocationTrackingEnabled = (prefs.getInt("location-updates-state") ?? 0) > 0;
|
_locationTrackingEnabled = (prefs.getInt("location-updates-state") ?? 0) > 0;
|
||||||
_locationInterval = prefs.getInt("location-interval") ??
|
_showNotification = prefs.getBool("location-updates-show-notification") ?? true;
|
||||||
AppSettings().defaultLocationUpdateIntervalMinutes;
|
_locationInterval = Duration(seconds: prefs.getInt("location-updates-interval") ??
|
||||||
_activeLocationInterval = prefs.getInt("location-updates-interval") ??
|
AppSettings().defaultLocationUpdateIntervalSeconds);
|
||||||
AppSettings().defaultActiveLocationUpdateIntervalSeconds;
|
|
||||||
if (_locationInterval < 15) {
|
|
||||||
_locationInterval = 15;
|
|
||||||
} else if (_locationInterval % 5 != 0) {
|
|
||||||
_locationInterval = 5 * (_locationInterval ~/ 5);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _incLocationInterval() {
|
void _incLocationInterval() {
|
||||||
if (_locationInterval < 720) {
|
if (_locationInterval.inSeconds < 60) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_locationInterval = _locationInterval + 5;
|
_locationInterval = _locationInterval + Duration(seconds: 5);
|
||||||
_changedHere = true;
|
});
|
||||||
|
} else if (_locationInterval.inMinutes < 15) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval + Duration(minutes: 1);
|
||||||
|
});
|
||||||
|
} else if (_locationInterval.inMinutes < 60) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval + Duration(minutes: 5);
|
||||||
|
});
|
||||||
|
} else if (_locationInterval.inHours < 4) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval + Duration(minutes: 10);
|
||||||
|
});
|
||||||
|
} else if (_locationInterval.inHours < 48) {
|
||||||
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval + Duration(hours: 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _decLocationInterval() {
|
void _decLocationInterval() {
|
||||||
if (_locationInterval > 15) {
|
if (_locationInterval.inSeconds > 5) {
|
||||||
setState(() {
|
if (_locationInterval.inSeconds <= 60) {
|
||||||
_locationInterval = _locationInterval - 5;
|
setState(() {
|
||||||
_changedHere = true;
|
_locationInterval = _locationInterval - Duration(seconds: 5);
|
||||||
});
|
});
|
||||||
}
|
} else if (_locationInterval.inMinutes <= 15) {
|
||||||
}
|
setState(() {
|
||||||
|
_locationInterval = _locationInterval - Duration(minutes: 1);
|
||||||
void _incActiveLocationInterval() {
|
});
|
||||||
if (_activeLocationInterval < 7200) {
|
} else if (_locationInterval.inMinutes <= 60) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_activeLocationInterval = _activeLocationInterval + 5;
|
_locationInterval = _locationInterval - Duration(minutes: 5);
|
||||||
_changedHere = true;
|
});
|
||||||
});
|
} else if (_locationInterval.inHours <= 4) {
|
||||||
}
|
setState(() {
|
||||||
}
|
_locationInterval = _locationInterval - Duration(minutes: 10);
|
||||||
|
});
|
||||||
void _decActiveLocationInterval() {
|
} else if (_locationInterval.inHours > 4) {
|
||||||
if (_activeLocationInterval > 5) {
|
setState(() {
|
||||||
setState(() {
|
_locationInterval = _locationInterval - Duration(hours: 1);
|
||||||
_activeLocationInterval = _activeLocationInterval - 5;
|
});
|
||||||
_changedHere = true;
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchLocationTrackingState(bool state) async {
|
_switchLocationTrackingState(bool state) async {
|
||||||
if (state) {
|
await AppSettings().save({'location-updates-interval': _locationInterval.inSeconds, 'location-updates-priority': _accuracy, 'location-updates-show-notification': _showNotification});
|
||||||
await LocationManager().updateDeviceLocation();
|
|
||||||
}
|
|
||||||
await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
|
|
||||||
setState(() {
|
|
||||||
_wait = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
_switchForegroundLocationTrackingState(bool state) async {
|
|
||||||
await AppSettings().save({'location-updates-interval': _activeLocationInterval, 'location-updates-priority': _accuracy});
|
|
||||||
if (state) {
|
if (state) {
|
||||||
try {
|
try {
|
||||||
await platform.invokeMethod('startLocationService');
|
await platform.invokeMethod('startLocationService');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_foregroundLocationTrackingEnabled = false;
|
_locationTrackingEnabled = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await platform.invokeMethod('stopLocationService');
|
await platform.invokeMethod('stopLocationService');
|
||||||
@ -117,18 +112,61 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _formatInterval() {
|
||||||
|
String result = "";
|
||||||
|
Duration leftToShow = Duration(seconds: _locationInterval?.inSeconds ?? 0);
|
||||||
|
if (leftToShow.inHours > 0) {
|
||||||
|
result += "${leftToShow.inHours} h ";
|
||||||
|
leftToShow -= Duration(hours: leftToShow.inHours);
|
||||||
|
}
|
||||||
|
if (leftToShow.inMinutes > 0) {
|
||||||
|
result += "${leftToShow.inMinutes} m";
|
||||||
|
leftToShow -= Duration(hours: leftToShow.inMinutes);
|
||||||
|
}
|
||||||
|
if (leftToShow.inSeconds > 0) {
|
||||||
|
result += "${leftToShow.inSeconds} s";
|
||||||
|
leftToShow -= Duration(hours: leftToShow.inSeconds);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getNoteWidget(String text, bool important) {
|
||||||
|
return Text(
|
||||||
|
text,
|
||||||
|
style: important ? Theme.of(context).textTheme.caption.copyWith(color: Theme.of(context).errorColor) : Theme.of(context).textTheme.caption,
|
||||||
|
softWrap: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getNotes() {
|
||||||
|
List<Widget> notes = [];
|
||||||
|
if (_locationTrackingEnabled) {
|
||||||
|
notes.add(_getNoteWidget('* Stop location tracking to change settings', false));
|
||||||
|
}
|
||||||
|
if ((_locationInterval?.inMinutes ?? 15) < 15) {
|
||||||
|
notes.add(_getNoteWidget('* Notification is mandatory for location updates with interval less than every 15 minutes', false));
|
||||||
|
if (_accuracy < 102) {
|
||||||
|
notes.add(_getNoteWidget('* Battery consumption will be noticeable', true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (notes.isEmpty) {
|
||||||
|
return Container(width: 0, height: 0);
|
||||||
|
}
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: notes,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ListView(
|
return ListView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Passive location tracking", style: Theme.of(context).textTheme.title),
|
Text("Location tracking", style: Theme.of(context).textTheme.title),
|
||||||
Text("Works in background not affecting phone battery. Usually sends last known device location. Can't be more frequent than once in 15 minutes.",
|
Container(height: Sizes.rowPadding),
|
||||||
style: Theme.of(context).textTheme.caption,
|
|
||||||
softWrap: true,
|
|
||||||
),
|
|
||||||
Container(height: Sizes.rowPadding,),
|
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Text("Enable"),
|
Text("Enable"),
|
||||||
@ -145,64 +183,27 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Container(height: Sizes.rowPadding),
|
Container(height: Sizes.rowPadding),
|
||||||
Text("Send device location every"),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
|
||||||
//Expanded(child: Container(),),
|
|
||||||
FlatButton(
|
|
||||||
padding: EdgeInsets.all(0.0),
|
|
||||||
child: Text("-", style: Theme.of(context).textTheme.title),
|
|
||||||
onPressed: () => _decLocationInterval(),
|
|
||||||
),
|
|
||||||
Text("$_locationInterval minutes", style: Theme.of(context).textTheme.title),
|
|
||||||
FlatButton(
|
|
||||||
padding: EdgeInsets.all(0.0),
|
|
||||||
child: Text("+", style: Theme.of(context).textTheme.title),
|
|
||||||
onPressed: () => _incLocationInterval(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(height: Sizes.rowPadding),
|
|
||||||
Text("Active location tracking", style: Theme.of(context).textTheme.title),
|
|
||||||
Container(height: Sizes.rowPadding),
|
|
||||||
Row(
|
|
||||||
children: <Widget>[
|
|
||||||
Text("Enable"),
|
|
||||||
Switch(
|
|
||||||
value: _foregroundLocationTrackingEnabled,
|
|
||||||
onChanged: _wait ? null : (value) {
|
|
||||||
setState(() {
|
|
||||||
_foregroundLocationTrackingEnabled = value;
|
|
||||||
_wait = true;
|
|
||||||
});
|
|
||||||
_switchForegroundLocationTrackingState(value);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Container(height: Sizes.rowPadding),
|
|
||||||
Text("Accuracy:", style: Theme.of(context).textTheme.body2),
|
Text("Accuracy:", style: Theme.of(context).textTheme.body2),
|
||||||
Container(height: Sizes.rowPadding),
|
Container(height: Sizes.rowPadding),
|
||||||
DropdownButton<int>(
|
DropdownButton<int>(
|
||||||
value: _accuracy,
|
value: _accuracy,
|
||||||
iconSize: 30.0,
|
iconSize: 30.0,
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
|
disabledHint: Text(locationAccuracy[_accuracy]),
|
||||||
items: locationAccuracy.keys.map((value) {
|
items: locationAccuracy.keys.map((value) {
|
||||||
return new DropdownMenuItem<int>(
|
return new DropdownMenuItem<int>(
|
||||||
value: value,
|
value: value,
|
||||||
child: Text('${locationAccuracy[value]}'),
|
child: Text('${locationAccuracy[value]}'),
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
onChanged: _foregroundLocationTrackingEnabled ? null : (val) {
|
onChanged: _locationTrackingEnabled ? null : (val) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_accuracy = val;
|
_accuracy = val;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Container(height: Sizes.rowPadding),
|
Container(height: Sizes.rowPadding),
|
||||||
Text("Update intervals"),
|
Text("Update interval"),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
@ -210,25 +211,43 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
|
|||||||
//Expanded(child: Container(),),
|
//Expanded(child: Container(),),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
child: Text("-", style: Theme.of(context).textTheme.title),
|
child: Text("-", style: Theme.of(context).textTheme.headline4),
|
||||||
onPressed: _foregroundLocationTrackingEnabled ? null : () => _decActiveLocationInterval(),
|
onPressed: _locationTrackingEnabled ? null : () => _decLocationInterval(),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(_formatInterval(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: _locationTrackingEnabled ? Theme.of(context).textTheme.title.copyWith(color: HAClientTheme().getDisabledStateColor(context)) : Theme.of(context).textTheme.title),
|
||||||
),
|
),
|
||||||
Text("$_activeLocationInterval seconds",
|
|
||||||
style: _foregroundLocationTrackingEnabled ? Theme.of(context).textTheme.title.copyWith(color: HAClientTheme().getDisabledStateColor(context)) : Theme.of(context).textTheme.title),
|
|
||||||
FlatButton(
|
FlatButton(
|
||||||
padding: EdgeInsets.all(0.0),
|
padding: EdgeInsets.all(0.0),
|
||||||
child: Text("+", style: Theme.of(context).textTheme.title),
|
child: Text("+", style: Theme.of(context).textTheme.headline4),
|
||||||
onPressed: _foregroundLocationTrackingEnabled ? null : () => _incActiveLocationInterval(),
|
onPressed: _locationTrackingEnabled ? null : () => _incLocationInterval(),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
Container(height: Sizes.rowPadding),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Text("Show notification"),
|
||||||
|
Switch(
|
||||||
|
value: _showNotification,
|
||||||
|
onChanged: (_locationTrackingEnabled || (_locationInterval?.inMinutes ?? 0) < 15) ? null : (value) {
|
||||||
|
setState(() {
|
||||||
|
_showNotification = value;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: Sizes.rowPadding),
|
||||||
|
_getNotes()
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
LocationManager().setSettings(_locationTrackingEnabled, _locationInterval);
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
name: hass_client
|
name: hass_client
|
||||||
description: Home Assistant Android Client
|
description: Home Assistant Android Client
|
||||||
|
|
||||||
version: 1.2.0+1200
|
version: 1.3.0+1300
|
||||||
|
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
@ -26,9 +26,6 @@ dependencies:
|
|||||||
hive: ^1.4.1+1
|
hive: ^1.4.1+1
|
||||||
hive_flutter: ^0.3.0+2
|
hive_flutter: ^0.3.0+2
|
||||||
device_info: ^0.4.2+4
|
device_info: ^0.4.2+4
|
||||||
geolocator: ^5.3.1
|
|
||||||
workmanager: ^0.2.2
|
|
||||||
battery: ^1.0.0
|
|
||||||
firebase_crashlytics: ^0.1.3+3
|
firebase_crashlytics: ^0.1.3+3
|
||||||
syncfusion_flutter_core: ^18.1.52
|
syncfusion_flutter_core: ^18.1.52
|
||||||
syncfusion_flutter_gauges: ^18.1.52
|
syncfusion_flutter_gauges: ^18.1.52
|
||||||
|
Reference in New Issue
Block a user