From 6ba1e88b09779d4d7642679cf1103ca19ec62db4 Mon Sep 17 00:00:00 2001 From: estevez-dev Date: Wed, 1 Jul 2020 22:59:49 +0300 Subject: [PATCH] WIP #571 --- .../hassclient/LocationUpdatesService.java | 72 ++----- .../hassclient/MainActivity.java | 176 +++++------------ .../com/keyboardcrumbs/hassclient/Utils.java | 26 +-- lib/managers/app_settings.dart | 1 + .../settings/integration_settings.part.dart | 185 +++++++++++------- 5 files changed, 182 insertions(+), 278 deletions(-) diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java index 5166829..d068431 100644 --- a/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java +++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/LocationUpdatesService.java @@ -16,7 +16,6 @@ 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; @@ -27,8 +26,6 @@ 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 @@ -47,14 +44,11 @@ import com.google.android.gms.tasks.Task; public class LocationUpdatesService extends Service { private static final String PACKAGE_NAME = - "com.google.android.gms.location.sample.locationupdatesforegroundservice"; + "com.keyboardcrumbs.hassclient"; private static final String TAG = LocationUpdatesService.class.getSimpleName(); - /** - * The name of the channel for notifications. - */ - private static final String CHANNEL_ID = "channel_01"; + private static final String CHANNEL_ID = "location_service"; static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast"; @@ -64,18 +58,6 @@ public class LocationUpdatesService extends Service { 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. */ @@ -167,7 +149,7 @@ public class LocationUpdatesService extends Service { @Override 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 // when that happens. Log.i(TAG, "in onBind()"); @@ -178,7 +160,7 @@ public class LocationUpdatesService extends Service { @Override 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 // service when that happens. Log.i(TAG, "in onRebind()"); @@ -191,20 +173,10 @@ public class LocationUpdatesService extends Service { 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 + // Called when the last client (MainActivity ) 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. @@ -215,10 +187,6 @@ public class LocationUpdatesService extends Service { 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); @@ -227,15 +195,11 @@ public class LocationUpdatesService extends Service { mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper()); } catch (SecurityException unlikely) { + //When we lost permission 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 { @@ -243,8 +207,8 @@ public class LocationUpdatesService extends Service { Utils.setRequestingLocationUpdates(this, false); stopSelf(); } catch (SecurityException unlikely) { + //When we lost permission 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", servicePendingIntent) .setContentText(text) - .setContentTitle(Utils.getLocationTitle(this)) + .setContentTitle(Utils.getLocationTitle(mLocation)) .setOngoing(true) - .setPriority(Notification.PRIORITY_HIGH) .setSmallIcon(R.mipmap.ic_launcher) - .setTicker(text) .setWhen(System.currentTimeMillis()); return builder.build(); @@ -286,14 +248,11 @@ public class LocationUpdatesService extends Service { 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."); - } + .addOnCompleteListener(task -> { + if (task.isSuccessful() && task.getResult() != null) { + mLocation = task.getResult(); + } else { + Log.w(TAG, "Failed to get location."); } }); } catch (SecurityException unlikely) { @@ -321,9 +280,10 @@ public class LocationUpdatesService extends Service { * Sets the location request parameters. */ private void createLocationRequest() { + long interval = Utils.getLocationUpdateIntervals(getApplicationContext()); mLocationRequest = new LocationRequest(); - mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); - mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); + mLocationRequest.setInterval(interval); + mLocationRequest.setFastestInterval(interval); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } 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 15b4d6e..bfd10ea 100644 --- a/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java +++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/MainActivity.java @@ -16,46 +16,30 @@ 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; -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.ConnectionResult; import com.google.firebase.iid.FirebaseInstanceId; -import com.google.firebase.iid.InstanceIdResult; -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 @@ -76,42 +60,46 @@ public class MainActivity extends FlutterActivity { public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) { GeneratedPluginRegistrant.registerWith(flutterEngine); new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL).setMethodCallHandler( - new MethodChannel.MethodCallHandler() { - @Override - public void onMethodCall(MethodCall call, MethodChannel.Result result) { + (call, result) -> { Context context = getActivity(); - if (call.method.equals("getFCMToken")) { - if (checkPlayServices()) { - FirebaseInstanceId.getInstance().getInstanceId() - .addOnCompleteListener(new OnCompleteListener() { - @Override - public void onComplete(@NonNull Task task) { - if (task.isSuccessful()) { - String token = task.getResult().getToken(); - UpdateTokenTask updateTokenTask = new UpdateTokenTask(context); - updateTokenTask.execute(token); - result.success(token); - } else { - result.error("fcm_error", task.getException().getMessage(), null); - } - } - }); - } 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(""); + switch (call.method) { + case "getFCMToken": + if (checkPlayServices()) { + FirebaseInstanceId.getInstance().getInstanceId() + .addOnCompleteListener(task -> { + if (task.isSuccessful()) { + String token = task.getResult().getToken(); + UpdateTokenTask updateTokenTask = new UpdateTokenTask(context); + updateTokenTask.execute(token); + result.success(token); + } else { + Exception ex = task.getException(); + 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); + } + break; + case "startLocationService": + if (checkPermissions()) { + requestPermissions(); + } else { + 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) { super.onCreate(savedInstanceState); myReceiver = new MyReceiver(); - // Check that the user hasn't revoked permissions by going to Settings. if (Utils.requestingLocationUpdates(this)) { - if (!checkPermissions()) { + if (checkPermissions()) { requestPermissions(); } } @@ -134,8 +121,6 @@ public class MainActivity extends FlutterActivity { @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); } @@ -156,112 +141,39 @@ public class MainActivity extends FlutterActivity { @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, + 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); - } + 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. + if (grantResults[0] == PackageManager.PERMISSION_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(); + //TODO looks like we need to remove this } } } diff --git a/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java index decc132..658a448 100644 --- a/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java +++ b/android/app/src/main/java/com/keyboardcrumbs/hassclient/Utils.java @@ -10,20 +10,16 @@ import java.util.Date; class Utils { 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) { 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 long getLocationUpdateIntervals(Context context) { + return context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE).getLong(KEY_LOCATION_UPDATE_INTERVAL, 90) * 1000; + } + static void setRequestingLocationUpdates(Context context, boolean requestingLocationUpdates) { context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE) .edit() @@ -31,16 +27,12 @@ class Utils { .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() + ")"; + return location == null ? "Accuracy: unknown" : + "Accuracy: " + location.getAccuracy(); } - static String getLocationTitle(Context context) { - return "Location updated: " + DateFormat.getDateTimeInstance().format(new Date()); + static String getLocationTitle(Location location) { + return "Location updated at " + DateFormat.getDateTimeInstance().format(new Date(location.getTime())); } } diff --git a/lib/managers/app_settings.dart b/lib/managers/app_settings.dart index 5d0e9d6..009a03a 100644 --- a/lib/managers/app_settings.dart +++ b/lib/managers/app_settings.dart @@ -32,6 +32,7 @@ class AppSettings { DisplayMode displayMode; AppTheme appTheme; final int defaultLocationUpdateIntervalMinutes = 20; + final int defaultActiveLocationUpdateIntervalSeconds = 90; Duration locationUpdateInterval; bool locationTrackingEnabled = false; diff --git a/lib/pages/settings/integration_settings.part.dart b/lib/pages/settings/integration_settings.part.dart index 671fb87..52fc15e 100644 --- a/lib/pages/settings/integration_settings.part.dart +++ b/lib/pages/settings/integration_settings.part.dart @@ -14,6 +14,7 @@ class _IntegrationSettingsPageState extends State { static const platform = const MethodChannel('com.keyboardcrumbs.hassclient/native'); int _locationInterval = AppSettings().defaultLocationUpdateIntervalMinutes; + int _activeLocationInterval = AppSettings().defaultActiveLocationUpdateIntervalSeconds; bool _locationTrackingEnabled = false; bool _foregroundLocationTrackingEnabled = false; bool _wait = false; @@ -35,7 +36,11 @@ class _IntegrationSettingsPageState extends State { _foregroundLocationTrackingEnabled = prefs.getBool("foreground-location-service") ?? false; _locationInterval = prefs.getInt("location-interval") ?? 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); } }); @@ -52,7 +57,7 @@ class _IntegrationSettingsPageState extends State { } void _decLocationInterval() { - if (_locationInterval > 5) { + if (_locationInterval > 15) { setState(() { _locationInterval = _locationInterval - 5; _changedHere = true; @@ -60,6 +65,24 @@ class _IntegrationSettingsPageState extends State { } } + 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 { if (state) { await LocationManager().updateDeviceLocation(); @@ -71,6 +94,7 @@ class _IntegrationSettingsPageState extends State { } _switchForegroundLocationTrackingState(bool state) async { + await AppSettings().save({'active-location-interval': _activeLocationInterval}); if (state) { await platform.invokeMethod('startLocationService'); } else { @@ -83,81 +107,96 @@ class _IntegrationSettingsPageState extends State { @override Widget build(BuildContext context) { - if (!_wait && !_changedHere) { - _loadSettings(); - } else if (_changedHere) { - _changedHere = false; - } return ListView( scrollDirection: Axis.vertical, padding: const EdgeInsets.all(20.0), children: [ - Text("Location tracking", style: Theme.of(context).textTheme.title), - Container(height: Sizes.rowPadding,), - InkWell( - onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"), - child: Text( - "Please read documentation!", - style: Theme.of(context).textTheme.subhead.copyWith( - color: Colors.blue, - decoration: TextDecoration.underline - ) - ), - ), - Container(height: Sizes.rowPadding,), - Row( - children: [ - Text("Enable location tracking"), - Switch( - value: _locationTrackingEnabled, - onChanged: _wait ? null : (value) { - setState(() { - _locationTrackingEnabled = value; - _wait = true; - }); - _switchLocationTrackingState(value); - }, - ), - ], - ), - 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, - mainAxisSize: MainAxisSize.max, - children: [ - //Expanded(child: Container(),), - FlatButton( - padding: EdgeInsets.all(0.0), - child: Text("-", style: Theme.of(context).textTheme.title), - onPressed: () => _decLocationInterval(), - ), - Text("$_locationInterval", style: Theme.of(context).textTheme.title), - FlatButton( - padding: EdgeInsets.all(0.0), - child: Text("+", style: Theme.of(context).textTheme.title), - onPressed: () => _incLocationInterval(), - ), - ], - ) - ] - ); + Text("Passive 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.", + style: Theme.of(context).textTheme.caption, + softWrap: true, + ), + Container(height: Sizes.rowPadding,), + Row( + children: [ + Text("Enable"), + Switch( + value: _locationTrackingEnabled, + onChanged: _wait ? null : (value) { + setState(() { + _locationTrackingEnabled = value; + _wait = true; + }); + _switchLocationTrackingState(value); + }, + ), + ], + ), + Container(height: Sizes.rowPadding), + Text("Send device location every"), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + //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), + Text("Works in foreground noticeably affecting phone battery. Sends most accurate location getting it from phone if possible. Can be very frequent.", + style: Theme.of(context).textTheme.caption, + softWrap: true, + ), + Container(height: Sizes.rowPadding,), + Row( + children: [ + Text("Enable"), + Switch( + value: _foregroundLocationTrackingEnabled, + onChanged: _wait ? null : (value) { + setState(() { + _foregroundLocationTrackingEnabled = value; + _wait = true; + }); + _switchForegroundLocationTrackingState(value); + }, + ), + ], + ), + Container(height: Sizes.rowPadding), + Text("Update device location every"), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + //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