From 63fe60fd8d4b7d8cdc6c780b8874241c1edb0fc4 Mon Sep 17 00:00:00 2001 From: Justin Bassett Date: Sun, 20 Sep 2020 14:31:22 -0400 Subject: [PATCH] Settings for location accuracy! (#964) * Settings for location accuracy! * Review comments. --- .../android/sensors/GeocodeSensorManager.kt | 13 +++- .../android/sensors/LocationSensorManager.kt | 67 +++++++++++++++---- .../android/sensors/SensorDetailFragment.kt | 7 +- app/src/main/res/values/strings.xml | 6 +- 4 files changed, 72 insertions(+), 21 deletions(-) diff --git a/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt b/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt index 1dfbb0454..869ffa38d 100644 --- a/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt +++ b/app/src/full/java/io/homeassistant/companion/android/sensors/GeocodeSensorManager.kt @@ -8,11 +8,15 @@ import android.os.Build import android.util.Log import com.google.android.gms.location.LocationServices import io.homeassistant.companion.android.R +import io.homeassistant.companion.android.database.AppDatabase +import io.homeassistant.companion.android.database.sensor.Setting import java.lang.Exception class GeocodeSensorManager : SensorManager { companion object { + private const val SETTING_ACCURACY = "Minimum Accuracy" + private const val DEFAULT_MINIMUM_ACCURACY = 200 private const val TAG = "GeocodeSM" val geocodedLocation = SensorManager.BasicSensor( "geocoded_location", @@ -58,7 +62,14 @@ class GeocodeSensorManager : SensorManager { return@addOnSuccessListener } - if (location.accuracy <= LocationSensorManager.MINIMUM_ACCURACY) + val sensorDao = AppDatabase.getInstance(context).sensorDao() + val sensorSettings = sensorDao.getSettings(geocodedLocation.id) + val minAccuracy = sensorSettings + .firstOrNull { it.name == SETTING_ACCURACY }?.value?.toIntOrNull() + ?: DEFAULT_MINIMUM_ACCURACY + sensorDao.add(Setting(geocodedLocation.id, SETTING_ACCURACY, minAccuracy.toString(), "number")) + + if (location.accuracy <= minAccuracy) address = Geocoder(context) .getFromLocation(location.latitude, location.longitude, 1) .firstOrNull() diff --git a/app/src/full/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt b/app/src/full/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt index 21e360210..706b74719 100644 --- a/app/src/full/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt +++ b/app/src/full/java/io/homeassistant/companion/android/sensors/LocationSensorManager.kt @@ -23,6 +23,7 @@ import io.homeassistant.companion.android.common.data.integration.IntegrationRep import io.homeassistant.companion.android.common.data.integration.UpdateLocation import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.sensor.Attribute +import io.homeassistant.companion.android.database.sensor.Setting import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -32,7 +33,10 @@ import kotlinx.coroutines.runBlocking class LocationSensorManager : BroadcastReceiver(), SensorManager { companion object { - const val MINIMUM_ACCURACY = 200 + private const val SETTING_ACCURACY = "Minimum Accuracy" + private const val SETTING_ACCURATE_UPDATE_TIME = "Minimum time between updates" + + private const val DEFAULT_MINIMUM_ACCURACY = 200 const val ACTION_REQUEST_LOCATION_UPDATES = "io.homeassistant.companion.android.background.REQUEST_UPDATES" @@ -55,6 +59,12 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { R.string.basic_sensor_name_location_zone, R.string.sensor_description_location_zone ) + val singleAccurateLocation = SensorManager.BasicSensor( + "accurate_location", + "", + R.string.basic_sensor_name_location_accurate, + R.string.sensor_description_location_accurate + ) internal const val TAG = "LocBroadcastReceiver" private var isBackgroundLocationSetup = false @@ -177,11 +187,17 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { private fun handleLocationUpdate(intent: Intent) { Log.d(TAG, "Received location update.") - LocationResult.extractResult(intent)?.lastLocation?.let { - if (it.accuracy > MINIMUM_ACCURACY) { - Log.w(TAG, "Location accuracy didn't meet requirements, disregarding: $it") + LocationResult.extractResult(intent)?.lastLocation?.let { location -> + val sensorDao = AppDatabase.getInstance(latestContext).sensorDao() + val sensorSettings = sensorDao.getSettings(backgroundLocation.id) + val minAccuracy = sensorSettings + .firstOrNull { it.name == SETTING_ACCURACY }?.value?.toIntOrNull() + ?: DEFAULT_MINIMUM_ACCURACY + sensorDao.add(Setting(backgroundLocation.id, SETTING_ACCURACY, minAccuracy.toString(), "number")) + if (location.accuracy > minAccuracy) { + Log.w(TAG, "Location accuracy didn't meet requirements, disregarding: $location") } else { - sendLocationUpdate(it) + sendLocationUpdate(location) } } } @@ -194,7 +210,14 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { return } - if (geofencingEvent.triggeringLocation.accuracy > MINIMUM_ACCURACY) { + val sensorDao = AppDatabase.getInstance(latestContext).sensorDao() + val sensorSettings = sensorDao.getSettings(zoneLocation.id) + val minAccuracy = sensorSettings + .firstOrNull { it.name == SETTING_ACCURACY }?.value?.toIntOrNull() + ?: DEFAULT_MINIMUM_ACCURACY + sensorDao.add(Setting(zoneLocation.id, SETTING_ACCURACY, minAccuracy.toString(), "number")) + + if (geofencingEvent.triggeringLocation.accuracy > minAccuracy) { Log.w( TAG, "Geofence location accuracy didn't meet requirements, requesting new location." @@ -205,7 +228,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { } } - private fun sendLocationUpdate(location: Location) { + private fun sendLocationUpdate(location: Location, name: String = "") { Log.d( TAG, "Last Location: " + "\nCoords:(${location.latitude}, ${location.longitude})" + @@ -217,7 +240,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { accuracy = location.accuracy.toInt() } val updateLocation = UpdateLocation( - "", + name, arrayOf(location.latitude, location.longitude), accuracy, location.speed.toInt(), @@ -289,16 +312,32 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { Log.w(TAG, "Not getting single accurate location because of permissions.") return } + if (!isEnabled(latestContext, singleAccurateLocation.id)) { + Log.w(TAG, "Requested single accurate location but it is not enabled.") + return + } + val now = System.currentTimeMillis() val sensorDao = AppDatabase.getInstance(latestContext).sensorDao() - val fullSensor = sensorDao.getFull(backgroundLocation.id) + val fullSensor = sensorDao.getFull(singleAccurateLocation.id) val latestAccurateLocation = fullSensor?.attributes?.firstOrNull { it.name == "lastAccurateLocationRequest" }?.value?.toLongOrNull() ?: 0L + + val sensorSettings = sensorDao.getSettings(singleAccurateLocation.id) + val minAccuracy = sensorSettings + .firstOrNull { it.name == SETTING_ACCURACY }?.value?.toIntOrNull() + ?: DEFAULT_MINIMUM_ACCURACY + sensorDao.add(Setting(singleAccurateLocation.id, SETTING_ACCURACY, minAccuracy.toString(), "number")) + val minTimeBetweenUpdates = sensorSettings + .firstOrNull { it.name == SETTING_ACCURATE_UPDATE_TIME }?.value?.toIntOrNull() + ?: 60000 + sensorDao.add(Setting(singleAccurateLocation.id, SETTING_ACCURATE_UPDATE_TIME, minTimeBetweenUpdates.toString(), "number")) + // Only update accurate location at most once a minute - if (now < latestAccurateLocation + 60000) { + if (now < latestAccurateLocation + minTimeBetweenUpdates) { Log.d(TAG, "Not requesting accurate location, last accurate location was too recent") return } - sensorDao.add(Attribute(backgroundLocation.id, "lastAccurateLocationRequest", now.toString(), "string")) + sensorDao.add(Attribute(singleAccurateLocation.id, "lastAccurateLocationRequest", now.toString(), "string")) val maxRetries = 5 val request = createLocationRequest() @@ -327,7 +366,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { } when { - locationResult.lastLocation.accuracy <= MINIMUM_ACCURACY -> { + locationResult.lastLocation.accuracy <= minAccuracy -> { Log.d(TAG, "Location accurate enough, all done with high accuracy.") runBlocking { sendLocationUpdate(locationResult.lastLocation) } if (wakeLock?.isHeld == true) wakeLock.release() @@ -337,7 +376,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { TAG, "No location was accurate enough, sending our last location anyway" ) - if (locationResult.lastLocation.accuracy <= MINIMUM_ACCURACY * 2) + if (locationResult.lastLocation.accuracy <= minAccuracy * 2) runBlocking { sendLocationUpdate(locationResult.lastLocation) } if (wakeLock?.isHeld == true) wakeLock.release() } @@ -361,7 +400,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager { get() = R.string.sensor_name_location override val availableSensors: List - get() = listOf(backgroundLocation, zoneLocation) + get() = listOf(singleAccurateLocation, backgroundLocation, zoneLocation) override fun requiredPermissions(): Array { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorDetailFragment.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorDetailFragment.kt index bdece3a90..7c6b785aa 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorDetailFragment.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorDetailFragment.kt @@ -174,10 +174,9 @@ class SensorDetailFragment( val pref = findPreference(key) ?: EditTextPreference(requireContext()) pref.key = key pref.title = setting.name - if (setting.value != "") - pref.summary = setting.value - else - pref.summary = pref.text + pref.dialogTitle = setting.name + pref.text = setting.value + pref.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance() pref.isIconSpaceReserved = false pref.setOnBindEditTextListener { fieldType -> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 53822c2c1..b6bf4abed 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ Interactive Internal Storage Last Reboot + Single Accurate Location Background Location Location Zone Phone State @@ -196,8 +197,9 @@ like to connect to: Information about the total and available storage space internally The date and time of the devices last reboot. The setting below will allow you to adjust the deadband in milliseconds, if you still find the value to jump incorrectly. The default value is 60000 (1 minute). The current level of illuminance - Update your location behind the scenes, periodically. - Import existing Home Assistant zones as geofences for zone based tracking. + Allow Home Assistant to send a notification to request an accurate location along with the application periodically requesting an accurate location. The Minimum Accuracy setting will allow you to decide how accurate the device location (in meters) has to be in order to send to Home Assistant. The Minimum time between updates (in milliseconds) keeps the device from sending the accurate location too often. + Update your location behind the scenes, periodically. The Minimum Accuracy setting will allow you to decide how accurate the device location (in meters) has to be in order to send to Home Assistant. + Import existing Home Assistant zones as geofences for zone based tracking. The Minimum Accuracy setting will allow you to decide how accurate the device location (in meters) has to be in order to send to Home Assistant. Whether or not the microphone is muted on the device Whether or not music is actively playing on the device The date and time of the next scheduled alarm, any app or manufacturer can override the default behavior. The package attribute will tell you which app set the next scheduled alarm.