This commit is contained in:
estevez-dev 2020-07-01 22:59:49 +03:00
parent 08e1327a29
commit 6ba1e88b09
5 changed files with 182 additions and 278 deletions

View File

@ -16,7 +16,6 @@ import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
@ -27,8 +26,6 @@ import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult; import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices; 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 * A bound and started service that is promoted to a foreground service when location updates have
@ -47,14 +44,11 @@ import com.google.android.gms.tasks.Task;
public class LocationUpdatesService extends Service { public class LocationUpdatesService extends Service {
private static final String PACKAGE_NAME = private static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationupdatesforegroundservice"; "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";
* 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 ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
@ -64,18 +58,6 @@ public class LocationUpdatesService extends Service {
private final IBinder mBinder = new LocalBinder(); 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. * The identifier for the notification displayed for the foreground service.
*/ */
@ -167,7 +149,7 @@ public class LocationUpdatesService extends Service {
@Override @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
// Called when a client (MainActivity in case of this sample) comes to the foreground // Called when MainActivity comes to the foreground
// and binds with this service. The service should cease to be a foreground service // and binds with this service. The service should cease to be a foreground service
// when that happens. // when that happens.
Log.i(TAG, "in onBind()"); Log.i(TAG, "in onBind()");
@ -178,7 +160,7 @@ public class LocationUpdatesService extends Service {
@Override @Override
public void onRebind(Intent intent) { public void onRebind(Intent intent) {
// Called when a client (MainActivity in case of this sample) returns to the foreground // Called when MainActivity returns to the foreground
// and binds once again with this service. The service should cease to be a foreground // and binds once again with this service. The service should cease to be a foreground
// service when that happens. // service when that happens.
Log.i(TAG, "in onRebind()"); Log.i(TAG, "in onRebind()");
@ -191,20 +173,10 @@ public class LocationUpdatesService extends Service {
public boolean onUnbind(Intent intent) { public boolean onUnbind(Intent intent) {
Log.i(TAG, "Last client unbound from service"); Log.i(TAG, "Last client unbound from service");
// Called when the last client (MainActivity in case of this sample) unbinds from this // Called when the last client (MainActivity ) unbinds from this
// service. If this method is called due to a configuration change in MainActivity, we // service. If this method is called due to a configuration change in MainActivity, we
// do nothing. Otherwise, we make this service a foreground service. // do nothing. Otherwise, we make this service a foreground service.
if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) { 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()); startForeground(NOTIFICATION_ID, getNotification());
} }
return true; // Ensures onRebind() is called when a client re-binds. return true; // Ensures onRebind() is called when a client re-binds.
@ -215,10 +187,6 @@ public class LocationUpdatesService extends Service {
mServiceHandler.removeCallbacksAndMessages(null); mServiceHandler.removeCallbacksAndMessages(null);
} }
/**
* Makes a request for location updates. Note that in this sample we merely log the
* {@link SecurityException}.
*/
public void requestLocationUpdates() { public void requestLocationUpdates() {
Log.i(TAG, "Requesting location updates"); Log.i(TAG, "Requesting location updates");
Utils.setRequestingLocationUpdates(this, true); Utils.setRequestingLocationUpdates(this, true);
@ -227,15 +195,11 @@ public class LocationUpdatesService extends Service {
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper()); mLocationCallback, Looper.myLooper());
} catch (SecurityException unlikely) { } catch (SecurityException unlikely) {
//When we lost permission
Utils.setRequestingLocationUpdates(this, false); 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() { public void removeLocationUpdates() {
Log.i(TAG, "Removing location updates"); Log.i(TAG, "Removing location updates");
try { try {
@ -243,8 +207,8 @@ public class LocationUpdatesService extends Service {
Utils.setRequestingLocationUpdates(this, false); Utils.setRequestingLocationUpdates(this, false);
stopSelf(); stopSelf();
} catch (SecurityException unlikely) { } catch (SecurityException unlikely) {
//When we lost permission
Utils.setRequestingLocationUpdates(this, true); Utils.setRequestingLocationUpdates(this, true);
Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
} }
} }
@ -273,11 +237,9 @@ public class LocationUpdatesService extends Service {
.addAction(R.drawable.blank_icon, "Stop", .addAction(R.drawable.blank_icon, "Stop",
servicePendingIntent) servicePendingIntent)
.setContentText(text) .setContentText(text)
.setContentTitle(Utils.getLocationTitle(this)) .setContentTitle(Utils.getLocationTitle(mLocation))
.setOngoing(true) .setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher) .setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis()); .setWhen(System.currentTimeMillis());
return builder.build(); return builder.build();
@ -286,14 +248,11 @@ public class LocationUpdatesService extends Service {
private void getLastLocation() { private void getLastLocation() {
try { try {
mFusedLocationClient.getLastLocation() mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() { .addOnCompleteListener(task -> {
@Override if (task.isSuccessful() && task.getResult() != null) {
public void onComplete(@NonNull Task<Location> task) { mLocation = task.getResult();
if (task.isSuccessful() && task.getResult() != null) { } else {
mLocation = task.getResult(); Log.w(TAG, "Failed to get location.");
} else {
Log.w(TAG, "Failed to get location.");
}
} }
}); });
} catch (SecurityException unlikely) { } catch (SecurityException unlikely) {
@ -321,9 +280,10 @@ public class LocationUpdatesService extends Service {
* Sets the location request parameters. * Sets the location request parameters.
*/ */
private void createLocationRequest() { private void createLocationRequest() {
long interval = Utils.getLocationUpdateIntervals(getApplicationContext());
mLocationRequest = new LocationRequest(); mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setInterval(interval);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setFastestInterval(interval);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
} }

View File

@ -16,46 +16,30 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.location.Location; import android.location.Location;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast; import android.widget.Toast;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodChannel;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.common.GoogleApiAvailability; import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.ConnectionResult;
import com.google.firebase.iid.FirebaseInstanceId; import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.InstanceIdResult;
import com.google.firebase.messaging.FirebaseMessaging;
public class MainActivity extends FlutterActivity { public class MainActivity extends FlutterActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String CHANNEL = "com.keyboardcrumbs.hassclient/native"; private static final String CHANNEL = "com.keyboardcrumbs.hassclient/native";
// Used in checking for runtime permissions.
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34; private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
// The BroadcastReceiver used to listen from broadcasts from the service.
private MyReceiver myReceiver; private MyReceiver myReceiver;
// A reference to the service used to get location updates.
private LocationUpdatesService mService = null; private LocationUpdatesService mService = null;
// Tracks the bound state of the service.
private boolean mBound = false; private boolean mBound = false;
// Monitors the state of the connection to the service.
private final ServiceConnection mServiceConnection = new ServiceConnection() { private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override @Override
@ -76,42 +60,46 @@ public class MainActivity extends FlutterActivity {
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine); GeneratedPluginRegistrant.registerWith(flutterEngine);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler( new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler(
new MethodChannel.MethodCallHandler() { (call, result) -> {
@Override
public void onMethodCall(MethodCall call, MethodChannel.Result result) {
Context context = getActivity(); Context context = getActivity();
if (call.method.equals("getFCMToken")) { switch (call.method) {
if (checkPlayServices()) { case "getFCMToken":
FirebaseInstanceId.getInstance().getInstanceId() if (checkPlayServices()) {
.addOnCompleteListener(new OnCompleteListener<InstanceIdResult>() { FirebaseInstanceId.getInstance().getInstanceId()
@Override .addOnCompleteListener(task -> {
public void onComplete(@NonNull Task<InstanceIdResult> task) { if (task.isSuccessful()) {
if (task.isSuccessful()) { String token = task.getResult().getToken();
String token = task.getResult().getToken(); UpdateTokenTask updateTokenTask = new UpdateTokenTask(context);
UpdateTokenTask updateTokenTask = new UpdateTokenTask(context); updateTokenTask.execute(token);
updateTokenTask.execute(token); result.success(token);
result.success(token); } else {
} else { Exception ex = task.getException();
result.error("fcm_error", task.getException().getMessage(), null); if (ex != null) {
} result.error("fcm_error", ex.getMessage(), null);
} } else {
}); result.error("fcm_error", "Unknown", null);
} else { }
result.error("google_play_service_error", "Google Play Services unavailable", null);
} }
} else if (call.method.equals("startLocationService")) { });
if (!checkPermissions()) { } else {
requestPermissions(); result.error("google_play_service_error", "Google Play Services unavailable", null);
} else { }
mService.requestLocationUpdates(); break;
} case "startLocationService":
result.success(""); if (checkPermissions()) {
} else if (call.method.equals("stopLocationService")) { requestPermissions();
mService.removeLocationUpdates(); } else {
result.success(""); mService.requestLocationUpdates();
}
result.success("");
break;
case "stopLocationService":
mService.removeLocationUpdates();
result.success("");
break;
} }
} }
}
); );
} }
@ -123,9 +111,8 @@ public class MainActivity extends FlutterActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
myReceiver = new MyReceiver(); myReceiver = new MyReceiver();
// Check that the user hasn't revoked permissions by going to Settings.
if (Utils.requestingLocationUpdates(this)) { if (Utils.requestingLocationUpdates(this)) {
if (!checkPermissions()) { if (checkPermissions()) {
requestPermissions(); requestPermissions();
} }
} }
@ -134,8 +121,6 @@ public class MainActivity extends FlutterActivity {
@Override @Override
protected void onStart() { protected void onStart() {
super.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, bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE); Context.BIND_AUTO_CREATE);
} }
@ -156,112 +141,39 @@ public class MainActivity extends FlutterActivity {
@Override @Override
protected void onStop() { protected void onStop() {
if (mBound) { 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); unbindService(mServiceConnection);
mBound = false; mBound = false;
} }
super.onStop(); super.onStop();
} }
/**
* Returns the current state of the permissions needed.
*/
private boolean checkPermissions() { private boolean checkPermissions() {
return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this, return PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION); Manifest.permission.ACCESS_FINE_LOCATION);
} }
private void requestPermissions() { private void requestPermissions() {
boolean shouldProvideRationale = ActivityCompat.requestPermissions(MainActivity.this,
ActivityCompat.shouldShowRequestPermissionRationale(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
Manifest.permission.ACCESS_FINE_LOCATION); REQUEST_PERMISSIONS_REQUEST_CODE);
// 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 @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) { @NonNull int[] grantResults) {
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) { if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 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(); 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 { private class MyReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION); Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
if (location != null) { if (location != null) {
Toast.makeText(MainActivity.this, Utils.getLocationText(location), //TODO looks like we need to remove this
Toast.LENGTH_SHORT).show();
} }
} }
} }

View File

@ -10,20 +10,16 @@ import java.util.Date;
class Utils { class Utils {
static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.foreground-location-service"; static final String KEY_REQUESTING_LOCATION_UPDATES = "flutter.foreground-location-service";
static final String KEY_LOCATION_UPDATE_INTERVAL = "flutter.active-location-interval";
/**
* Returns true if requesting location updates, otherwise returns false.
*
* @param context The {@link Context}.
*/
static boolean requestingLocationUpdates(Context context) { static boolean requestingLocationUpdates(Context context) {
return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false); return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getBoolean(KEY_REQUESTING_LOCATION_UPDATES, false);
} }
/** static long getLocationUpdateIntervals(Context context) {
* Stores the location updates state in SharedPreferences. return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_INTERVAL, 90) * 1000;
* @param requestingLocationUpdates The location updates state. }
*/
static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) { static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) {
context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
.edit() .edit()
@ -31,16 +27,12 @@ class Utils {
.apply(); .apply();
} }
/**
* Returns the {@code location} object as a human readable string.
* @param location The {@link Location}.
*/
static String getLocationText(Location location) { static String getLocationText(Location location) {
return location == null ? "Unknown location" : return location == null ? "Accuracy: unknown" :
"(" + location.getLatitude() + ", " + location.getLongitude() + ")"; "Accuracy: " + location.getAccuracy();
} }
static String getLocationTitle(Context context) { static String getLocationTitle(Location location) {
return "Location updated: " + DateFormat.getDateTimeInstance().format(new Date()); return "Location updated at " + DateFormat.getDateTimeInstance().format(new Date(location.getTime()));
} }
} }

View File

@ -32,6 +32,7 @@ class AppSettings {
DisplayMode displayMode; DisplayMode displayMode;
AppTheme appTheme; AppTheme appTheme;
final int defaultLocationUpdateIntervalMinutes = 20; final int defaultLocationUpdateIntervalMinutes = 20;
final int defaultActiveLocationUpdateIntervalSeconds = 90;
Duration locationUpdateInterval; Duration locationUpdateInterval;
bool locationTrackingEnabled = false; bool locationTrackingEnabled = false;

View File

@ -14,6 +14,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native'); static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native');
int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes; int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes;
int _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds;
bool _locationTrackingEnabled = false; bool _locationTrackingEnabled = false;
bool _foregroundLocationTrackingEnabled = false; bool _foregroundLocationTrackingEnabled = false;
bool _wait = false; bool _wait = false;
@ -35,7 +36,11 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
_foregroundLocationTrackingEnabled = prefs.getBool("foreground-location-service") ?? false; _foregroundLocationTrackingEnabled = prefs.getBool("foreground-location-service") ?? false;
_locationInterval = prefs.getInt("location-interval") ?? _locationInterval = prefs.getInt("location-interval") ??
AppSettings().defaultLocationUpdateIntervalMinutes; AppSettings().defaultLocationUpdateIntervalMinutes;
if (_locationInterval % 5 != 0) { _activeLocationInterval = prefs.getInt("active-location-interval") ??
AppSettings().defaultActiveLocationUpdateIntervalSeconds;
if (_locationInterval < 15) {
_locationInterval = 15;
} else if (_locationInterval % 5 != 0) {
_locationInterval = 5 * (_locationInterval ~/ 5); _locationInterval = 5 * (_locationInterval ~/ 5);
} }
}); });
@ -52,7 +57,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
} }
void _decLocationInterval() { void _decLocationInterval() {
if (_locationInterval > 5) { if (_locationInterval > 15) {
setState(() { setState(() {
_locationInterval = _locationInterval - 5; _locationInterval = _locationInterval - 5;
_changedHere = true; _changedHere = true;
@ -60,6 +65,24 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
} }
} }
void _incActiveLocationInterval() {
if (_activeLocationInterval < 7200) {
setState(() {
_activeLocationInterval = _activeLocationInterval + 5;
_changedHere = true;
});
}
}
void _decActiveLocationInterval() {
if (_activeLocationInterval > 5) {
setState(() {
_activeLocationInterval = _activeLocationInterval - 5;
_changedHere = true;
});
}
}
_switchLocationTrackingState(bool state) async { _switchLocationTrackingState(bool state) async {
if (state) { if (state) {
await LocationManager().updateDeviceLocation(); await LocationManager().updateDeviceLocation();
@ -71,6 +94,7 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
} }
_switchForegroundLocationTrackingState(bool state) async { _switchForegroundLocationTrackingState(bool state) async {
await AppSettings().save({'active-location-interval': _activeLocationInterval});
if (state) { if (state) {
await platform.invokeMethod('startLocationService'); await platform.invokeMethod('startLocationService');
} else { } else {
@ -83,81 +107,96 @@ class _IntegrationSettingsPageState extends State<IntegrationSettingsPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!_wait && !_changedHere) {
_loadSettings();
} else if (_changedHere) {
_changedHere = false;
}
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("Location tracking", style: Theme.of(context).textTheme.title), Text("Passive location tracking", style: Theme.of(context).textTheme.title),
Container(height: Sizes.rowPadding,), Text("Works in background not affecting phone battery. Usually sends last known device location. Can't be more frequent than once in 15 minutes.",
InkWell( style: Theme.of(context).textTheme.caption,
onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"), softWrap: true,
child: Text( ),
"Please read documentation!", Container(height: Sizes.rowPadding,),
style: Theme.of(context).textTheme.subhead.copyWith( Row(
color: Colors.blue, children: <Widget>[
decoration: TextDecoration.underline Text("Enable"),
) Switch(
), value: _locationTrackingEnabled,
), onChanged: _wait ? null : (value) {
Container(height: Sizes.rowPadding,), setState(() {
Row( _locationTrackingEnabled = value;
children: <Widget>[ _wait = true;
Text("Enable location tracking"), });
Switch( _switchLocationTrackingState(value);
value: _locationTrackingEnabled, },
onChanged: _wait ? null : (value) { ),
setState(() { ],
_locationTrackingEnabled = value; ),
_wait = true; Container(height: Sizes.rowPadding),
}); Text("Send device location every"),
_switchLocationTrackingState(value); Row(
}, mainAxisAlignment: MainAxisAlignment.center,
), mainAxisSize: MainAxisSize.max,
], children: <Widget>[
), //Expanded(child: Container(),),
Container(height: Sizes.rowPadding), FlatButton(
Row( padding: EdgeInsets.all(0.0),
children: <Widget>[ child: Text("-", style: Theme.of(context).textTheme.title),
Text("Foreground tracking"), onPressed: () => _decLocationInterval(),
Switch( ),
value: _foregroundLocationTrackingEnabled, Text("$_locationInterval minutes", style: Theme.of(context).textTheme.title),
onChanged: _wait ? null : (value) { FlatButton(
setState(() { padding: EdgeInsets.all(0.0),
_foregroundLocationTrackingEnabled = value; child: Text("+", style: Theme.of(context).textTheme.title),
_wait = true; onPressed: () => _incLocationInterval(),
}); ),
_switchForegroundLocationTrackingState(value); ],
}, ),
), Container(height: Sizes.rowPadding),
], Text("Active location tracking", style: Theme.of(context).textTheme.title),
), Text("Works in foreground noticeably affecting phone battery. Sends most accurate location getting it from phone if possible. Can be very frequent.",
Container(height: Sizes.rowPadding), style: Theme.of(context).textTheme.caption,
Text("Location update interval in minutes:"), softWrap: true,
Row( ),
mainAxisAlignment: MainAxisAlignment.center, Container(height: Sizes.rowPadding,),
mainAxisSize: MainAxisSize.max, Row(
children: <Widget>[ children: <Widget>[
//Expanded(child: Container(),), Text("Enable"),
FlatButton( Switch(
padding: EdgeInsets.all(0.0), value: _foregroundLocationTrackingEnabled,
child: Text("-", style: Theme.of(context).textTheme.title), onChanged: _wait ? null : (value) {
onPressed: () => _decLocationInterval(), setState(() {
), _foregroundLocationTrackingEnabled = value;
Text("$_locationInterval", style: Theme.of(context).textTheme.title), _wait = true;
FlatButton( });
padding: EdgeInsets.all(0.0), _switchForegroundLocationTrackingState(value);
child: Text("+", style: Theme.of(context).textTheme.title), },
onPressed: () => _incLocationInterval(), ),
), ],
], ),
) Container(height: Sizes.rowPadding),
] Text("Update 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: _foregroundLocationTrackingEnabled ? null : () => _decActiveLocationInterval(),
),
Text("$_activeLocationInterval seconds",
style: _foregroundLocationTrackingEnabled ? Theme.of(context).textTheme.title.copyWith(color: HAClientTheme().getDisabledStateColor(context)) : Theme.of(context).textTheme.title),
FlatButton(
padding: EdgeInsets.all(0.0),
child: Text("+", style: Theme.of(context).textTheme.title),
onPressed: _foregroundLocationTrackingEnabled ? null : () => _incActiveLocationInterval(),
),
],
),
]
);
} }
@override @override