Settings for location accuracy! (#964)

* Settings for location accuracy!

* Review comments.
This commit is contained in:
Justin Bassett 2020-09-20 14:31:22 -04:00 committed by GitHub
parent 553d5e5cc3
commit 63fe60fd8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 72 additions and 21 deletions

View file

@ -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()

View file

@ -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<SensorManager.BasicSensor>
get() = listOf(backgroundLocation, zoneLocation)
get() = listOf(singleAccurateLocation, backgroundLocation, zoneLocation)
override fun requiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

View file

@ -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 ->

View file

@ -40,6 +40,7 @@
<string name="basic_sensor_name_interactive">Interactive</string>
<string name="basic_sensor_name_internal_storage">Internal Storage</string>
<string name="basic_sensor_name_last_reboot">Last Reboot</string>
<string name="basic_sensor_name_location_accurate">Single Accurate Location</string>
<string name="basic_sensor_name_location_background">Background Location</string>
<string name="basic_sensor_name_location_zone">Location Zone</string>
<string name="basic_sensor_name_phone">Phone State</string>
@ -196,8 +197,9 @@ like to connect to:</string>
<string name="sensor_description_internal_storage">Information about the total and available storage space internally</string>
<string name="sensor_description_last_reboot">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).</string>
<string name="sensor_description_light_sensor">The current level of illuminance</string>
<string name="sensor_description_location_background">Update your location behind the scenes, periodically.</string>
<string name="sensor_description_location_zone">Import existing Home Assistant zones as geofences for zone based tracking.</string>
<string name="sensor_description_location_accurate">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.</string>
<string name="sensor_description_location_background">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.</string>
<string name="sensor_description_location_zone">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.</string>
<string name="sensor_description_mic_muted">Whether or not the microphone is muted on the device</string>
<string name="sensor_description_music_active">Whether or not music is actively playing on the device</string>
<string name="sensor_description_next_alarm">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.</string>