diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 4193831..9385878 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -56,6 +56,10 @@
+
diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java
new file mode 100644
index 0000000..5166829
--- /dev/null
+++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java
@@ -0,0 +1,358 @@
+package com.keyboardcrumbs.hassclient;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.location.Location;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import android.util.Log;
+
+import com.google.android.gms.location.FusedLocationProviderClient;
+import com.google.android.gms.location.LocationCallback;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationResult;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.Task;
+
+/**
+ * A bound and started service that is promoted to a foreground service when location updates have
+ * been requested and all clients unbind.
+ *
+ * For apps running in the background on "O" devices, location is computed only once every 10
+ * minutes and delivered batched every 30 minutes. This restriction applies even to apps
+ * targeting "N" or lower which are run on "O" devices.
+ *
+ * This sample show how to use a long-running service for location updates. When an activity is
+ * bound to this service, frequent location updates are permitted. When the activity is removed
+ * from the foreground, the service promotes itself to a foreground service, and location updates
+ * continue. When the activity comes back to the foreground, the foreground service stops, and the
+ * notification assocaited with that service is removed.
+ */
+public class LocationUpdatesService extends Service {
+
+ private static final String PACKAGE_NAME =
+ "com.google.android.gms.location.sample.locationupdatesforegroundservice";
+
+ private static final String TAG = LocationUpdatesService.class.getSimpleName();
+
+ /**
+ * The name of the channel for notifications.
+ */
+ private static final String CHANNEL_ID = "channel_01";
+
+ static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
+
+ static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
+ private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
+ ".started_from_notification";
+
+ private final IBinder mBinder = new LocalBinder();
+
+ /**
+ * The desired interval for location updates. Inexact. Updates may be more or less frequent.
+ */
+ private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
+
+ /**
+ * The fastest rate for active location updates. Updates will never be more frequent
+ * than this value.
+ */
+ private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
+ UPDATE_INTERVAL_IN_MILLISECONDS / 2;
+
+ /**
+ * The identifier for the notification displayed for the foreground service.
+ */
+ private static final int NOTIFICATION_ID = 954311;
+
+ /**
+ * Used to check whether the bound activity has really gone away and not unbound as part of an
+ * orientation change. We create a foreground service notification only if the former takes
+ * place.
+ */
+ private boolean mChangingConfiguration = false;
+
+ private NotificationManager mNotificationManager;
+
+ private LocationRequest mLocationRequest;
+
+ /**
+ * Provides access to the Fused Location Provider API.
+ */
+ private FusedLocationProviderClient mFusedLocationClient;
+
+ /**
+ * Callback for changes in location.
+ */
+ private LocationCallback mLocationCallback;
+
+ private Handler mServiceHandler;
+
+ /**
+ * The current location.
+ */
+ private Location mLocation;
+
+ public LocationUpdatesService() {
+ }
+
+ @Override
+ public void onCreate() {
+ mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
+
+ mLocationCallback = new LocationCallback() {
+ @Override
+ public void onLocationResult(LocationResult locationResult) {
+ super.onLocationResult(locationResult);
+ onNewLocation(locationResult.getLastLocation());
+ }
+ };
+
+ createLocationRequest();
+ getLastLocation();
+
+ HandlerThread handlerThread = new HandlerThread(TAG);
+ handlerThread.start();
+ mServiceHandler = new Handler(handlerThread.getLooper());
+ mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+ // Android O requires a Notification Channel.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ CharSequence name = "Location service";
+ // Create the channel for the notification
+ NotificationChannel mChannel =
+ new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
+
+ // Set the Notification Channel for the Notification Manager.
+ mNotificationManager.createNotificationChannel(mChannel);
+ }
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ 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.
+ if (startedFromNotification) {
+ removeLocationUpdates();
+ stopSelf();
+ }
+ // Tells the system to not try to recreate the service after it has been killed.
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ mChangingConfiguration = true;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ // Called when a client (MainActivity in case of this sample) comes to the foreground
+ // and binds with this service. The service should cease to be a foreground service
+ // when that happens.
+ Log.i(TAG, "in onBind()");
+ stopForeground(true);
+ mChangingConfiguration = false;
+ return mBinder;
+ }
+
+ @Override
+ public void onRebind(Intent intent) {
+ // Called when a client (MainActivity in case of this sample) returns to the foreground
+ // and binds once again with this service. The service should cease to be a foreground
+ // service when that happens.
+ Log.i(TAG, "in onRebind()");
+ stopForeground(true);
+ mChangingConfiguration = false;
+ super.onRebind(intent);
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ Log.i(TAG, "Last client unbound from service");
+
+ // Called when the last client (MainActivity in case of this sample) unbinds from this
+ // service. If this method is called due to a configuration change in MainActivity, we
+ // do nothing. Otherwise, we make this service a foreground service.
+ if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) {
+ Log.i(TAG, "Starting foreground service");
+ /*
+ // TODO(developer). If targeting O, use the following code.
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
+ mNotificationManager.startServiceInForeground(new Intent(this,
+ LocationUpdatesService.class), NOTIFICATION_ID, getNotification());
+ } else {
+ startForeground(NOTIFICATION_ID, getNotification());
+ }
+ */
+ startForeground(NOTIFICATION_ID, getNotification());
+ }
+ return true; // Ensures onRebind() is called when a client re-binds.
+ }
+
+ @Override
+ public void onDestroy() {
+ mServiceHandler.removeCallbacksAndMessages(null);
+ }
+
+ /**
+ * Makes a request for location updates. Note that in this sample we merely log the
+ * {@link SecurityException}.
+ */
+ public void requestLocationUpdates() {
+ Log.i(TAG, "Requesting location updates");
+ Utils.setRequestingLocationUpdates(this, true);
+ startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
+ try {
+ mFusedLocationClient.requestLocationUpdates(mLocationRequest,
+ mLocationCallback, Looper.myLooper());
+ } catch (SecurityException unlikely) {
+ Utils.setRequestingLocationUpdates(this, false);
+ Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
+ }
+ }
+
+ /**
+ * Removes location updates. Note that in this sample we merely log the
+ * {@link SecurityException}.
+ */
+ public void removeLocationUpdates() {
+ Log.i(TAG, "Removing location updates");
+ try {
+ mFusedLocationClient.removeLocationUpdates(mLocationCallback);
+ Utils.setRequestingLocationUpdates(this, false);
+ stopSelf();
+ } catch (SecurityException unlikely) {
+ Utils.setRequestingLocationUpdates(this, true);
+ Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
+ }
+ }
+
+ /**
+ * Returns the {@link NotificationCompat} used as part of the foreground service.
+ */
+ private Notification getNotification() {
+ Intent intent = new Intent(this, LocationUpdatesService.class);
+
+ CharSequence text = Utils.getLocationText(mLocation);
+
+ // Extra to help us figure out if we arrived in onStartCommand via the notification or not.
+ intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
+
+ // The PendingIntent that leads to a call to onStartCommand() in this service.
+ PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ // The PendingIntent to launch activity.
+ 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 HA Client",
+ activityPendingIntent)
+ .addAction(R.drawable.blank_icon, "Stop",
+ servicePendingIntent)
+ .setContentText(text)
+ .setContentTitle(Utils.getLocationTitle(this))
+ .setOngoing(true)
+ .setPriority(Notification.PRIORITY_HIGH)
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setTicker(text)
+ .setWhen(System.currentTimeMillis());
+
+ return builder.build();
+ }
+
+ private void getLastLocation() {
+ try {
+ mFusedLocationClient.getLastLocation()
+ .addOnCompleteListener(new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful() && task.getResult() != null) {
+ mLocation = task.getResult();
+ } else {
+ Log.w(TAG, "Failed to get location.");
+ }
+ }
+ });
+ } catch (SecurityException unlikely) {
+ Log.e(TAG, "Lost location permission." + unlikely);
+ }
+ }
+
+ private void onNewLocation(Location location) {
+ Log.i(TAG, "New location: " + location);
+
+ mLocation = location;
+
+ // Notify anyone listening for broadcasts about the new location.
+ Intent intent = new Intent(ACTION_BROADCAST);
+ intent.putExtra(EXTRA_LOCATION, location);
+ LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
+
+ // Update notification content if running as a foreground service.
+ if (serviceIsRunningInForeground(this)) {
+ mNotificationManager.notify(NOTIFICATION_ID, getNotification());
+ }
+ }
+
+ /**
+ * Sets the location request parameters.
+ */
+ private void createLocationRequest() {
+ mLocationRequest = new LocationRequest();
+ mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
+ mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
+ mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ }
+
+ /**
+ * Class used for the client Binder. Since this service runs in the same process as its
+ * clients, we don't need to deal with IPC.
+ */
+ public class LocalBinder extends Binder {
+ LocationUpdatesService getService() {
+ return LocationUpdatesService.this;
+ }
+ }
+
+ /**
+ * Returns true if this is a foreground service.
+ *
+ * @param context The {@link Context}.
+ */
+ public boolean serviceIsRunningInForeground(Context context) {
+ ActivityManager manager = (ActivityManager) context.getSystemService(
+ Context.ACTIVITY_SERVICE);
+ for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
+ Integer.MAX_VALUE)) {
+ if (getClass().getName().equals(service.service.getClassName())) {
+ if (service.foreground) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java
index 189ed57..15b4d6e 100644
--- a/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java
+++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java
@@ -1,15 +1,30 @@
package com.keyboardcrumbs.hassclient;
import androidx.annotation.NonNull;
+import androidx.core.app.ActivityCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugins.GeneratedPluginRegistrant;
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageManager;
+import android.location.Location;
import android.os.Bundle;
+import android.os.IBinder;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.widget.Toast;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
@@ -24,8 +39,39 @@ import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends FlutterActivity {
+ private static final String TAG = MainActivity.class.getSimpleName();
+
private static final String CHANNEL = "com.keyboardcrumbs.hassclient/native";
-
+
+ // Used in checking for runtime permissions.
+ private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
+
+ // The BroadcastReceiver used to listen from broadcasts from the service.
+ private MyReceiver myReceiver;
+
+ // A reference to the service used to get location updates.
+ private LocationUpdatesService mService = null;
+
+ // Tracks the bound state of the service.
+ private boolean mBound = false;
+
+ // Monitors the state of the connection to the service.
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
+ mService = binder.getService();
+ mBound = true;
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ mBound = false;
+ }
+ };
+
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
@@ -53,6 +99,16 @@ public class MainActivity extends FlutterActivity {
} else {
result.error("google_play_service_error", "Google Play Services unavailable", null);
}
+ } else if (call.method.equals("startLocationService")) {
+ if (!checkPermissions()) {
+ requestPermissions();
+ } else {
+ mService.requestLocationUpdates();
+ }
+ result.success("");
+ } else if (call.method.equals("stopLocationService")) {
+ mService.removeLocationUpdates();
+ result.success("");
}
}
}
@@ -66,6 +122,148 @@ public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ myReceiver = new MyReceiver();
+ // Check that the user hasn't revoked permissions by going to Settings.
+ if (Utils.requestingLocationUpdates(this)) {
+ if (!checkPermissions()) {
+ requestPermissions();
+ }
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ // Bind to the service. If the service is in foreground mode, this signals to the service
+ // that since this activity is in the foreground, the service can exit foreground mode.
+ bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver,
+ new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
+ }
+
+ @Override
+ protected void onPause() {
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
+ super.onPause();
+ }
+
+ @Override
+ protected void onStop() {
+ if (mBound) {
+ // Unbind from the service. This signals to the service that this activity is no longer
+ // in the foreground, and the service can respond by promoting itself to a foreground
+ // service.
+ unbindService(mServiceConnection);
+ mBound = false;
+ }
+ super.onStop();
+ }
+
+ /**
+ * Returns the current state of the permissions needed.
+ */
+ private boolean checkPermissions() {
+ return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+
+ private void requestPermissions() {
+ boolean shouldProvideRationale =
+ ActivityCompat.shouldShowRequestPermissionRationale(this,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ // Provide an additional rationale to the user. This would happen if the user denied the
+ // request previously, but didn't check the "Don't ask again" checkbox.
+ if (shouldProvideRationale) {
+ Log.i(TAG, "Displaying permission rationale to provide additional context.");
+ /*
+ Snackbar.make(
+ findViewById(R.id.activity_main),
+ R.string.permission_rationale,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.ok, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Request permission
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ REQUEST_PERMISSIONS_REQUEST_CODE);
+ }
+ })
+ .show();
+ */
+ } else {
+ Log.i(TAG, "Requesting permission");
+ // Request permission. It's possible this can be auto answered if device policy
+ // sets the permission in a given state or the user denied the permission
+ // previously and checked "Never ask again".
+ ActivityCompat.requestPermissions(MainActivity.this,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ REQUEST_PERMISSIONS_REQUEST_CODE);
+ }
+ }
+
+ /**
+ * Callback received when a permissions request has been completed.
+ */
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
+ if (grantResults.length <= 0) {
+ // If user interaction was interrupted, the permission request is cancelled and you
+ // receive empty arrays.
+ Log.i(TAG, "User interaction was cancelled.");
+ } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ // Permission was granted.
+ mService.requestLocationUpdates();
+ } else {
+ // Permission denied.
+ /*
+ setButtonsState(false);
+ Snackbar.make(
+ findViewById(R.id.activity_main),
+ R.string.permission_denied_explanation,
+ Snackbar.LENGTH_INDEFINITE)
+ .setAction(R.string.settings, new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ // Build intent that displays the App settings screen.
+ Intent intent = new Intent();
+ intent.setAction(
+ Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package",
+ BuildConfig.APPLICATION_ID, null);
+ intent.setData(uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+ })
+ .show();
+
+ */
+ }
+ }
+ }
+
+ /**
+ * Receiver for broadcasts sent by {@link LocationUpdatesService}.
+ */
+ private class MyReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
+ if (location != null) {
+ Toast.makeText(MainActivity.this, Utils.getLocationText(location),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
}
}
diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java
new file mode 100644
index 0000000..decc132
--- /dev/null
+++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java
@@ -0,0 +1,46 @@
+package com.keyboardcrumbs.hassclient;
+
+import android.content.Context;
+import android.location.Location;
+import android.preference.PreferenceManager;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+class Utils {
+
+ static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.foreground-location-service";
+
+ /**
+ * Returns true if requesting location updates, otherwise returns false.
+ *
+ * @param context The {@link Context}.
+ */
+ static boolean requestingLocationUpdates(Context context) {
+ return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false);
+ }
+
+ /**
+ * Stores the location updates state in SharedPreferences.
+ * @param requestingLocationUpdates The location updates state.
+ */
+ static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) {
+ context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ .edit()
+ .putBoolean(KEY_REQUESTING_LOCATION_UPDATES, requestingLocationUpdates)
+ .apply();
+ }
+
+ /**
+ * Returns the {@code location} object as a human readable string.
+ * @param location The {@link Location}.
+ */
+ static String getLocationText(Location location) {
+ return location == null ? "Unknown location" :
+ "(" + location.getLatitude() + ", " + location.getLongitude() + ")";
+ }
+
+ static String getLocationTitle(Context context) {
+ return "Location updated: " + DateFormat.getDateTimeInstance().format(new Date());
+ }
+}
diff --git a/lib/pages/settings/integration_settings.part.dart b/lib/pages/settings/integration_settings.part.dart
index e45ae0d..94977a1 100644
--- a/lib/pages/settings/integration_settings.part.dart
+++ b/lib/pages/settings/integration_settings.part.dart
@@ -11,8 +11,11 @@ class IntegrationSettingsPage extends StatefulWidget {
class _IntegrationSettingsPageState extends State {
+ static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native');
+
int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes;
bool _locationTrackingEnabled = false;
+ bool _foregroundLocationTrackingEnabled = false;
bool _wait = false;
@override
@@ -28,6 +31,7 @@ class _IntegrationSettingsPageState extends State {
SharedPreferences.getInstance().then((prefs) {
setState(() {
_locationTrackingEnabled = prefs.getBool("location-enabled") ?? false;
+ _foregroundLocationTrackingEnabled = prefs.getBool("foreground-location-service") ?? false;
_locationInterval = prefs.getInt("location-interval") ??
AppSettings().defaultLocationUpdateIntervalMinutes;
if (_locationInterval % 5 != 0) {
@@ -63,6 +67,17 @@ class _IntegrationSettingsPageState extends State {
});
}
+ _switchForegroundLocationTrackingState(bool state) async {
+ if (state) {
+ await platform.invokeMethod('startLocationService');
+ } else {
+ await platform.invokeMethod('stopLocationService');
+ }
+ setState(() {
+ _wait = false;
+ });
+ }
+
@override
Widget build(BuildContext context) {
return ListView(
@@ -84,7 +99,7 @@ class _IntegrationSettingsPageState extends State {
Container(height: Sizes.rowPadding,),
Row(
children: [
- Text("Enable device location tracking"),
+ Text("Enable location tracking"),
Switch(
value: _locationTrackingEnabled,
onChanged: _wait ? null : (value) {
@@ -97,7 +112,23 @@ class _IntegrationSettingsPageState extends State {
),
],
),
- Container(height: Sizes.rowPadding,),
+ Container(height: Sizes.rowPadding),
+ Row(
+ children: [
+ Text("Foreground tracking"),
+ Switch(
+ value: _foregroundLocationTrackingEnabled,
+ onChanged: _wait ? null : (value) {
+ setState(() {
+ _foregroundLocationTrackingEnabled = value;
+ _wait = true;
+ });
+ _switchForegroundLocationTrackingState(value);
+ },
+ ),
+ ],
+ ),
+ Container(height: Sizes.rowPadding),
Text("Location update interval in minutes:"),
Row(
mainAxisAlignment: MainAxisAlignment.center,