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 android.util.Log
import com.google.android.gms.location.LocationServices import com.google.android.gms.location.LocationServices
import io.homeassistant.companion.android.R 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 import java.lang.Exception
class GeocodeSensorManager : SensorManager { class GeocodeSensorManager : SensorManager {
companion object { companion object {
private const val SETTING_ACCURACY = "Minimum Accuracy"
private const val DEFAULT_MINIMUM_ACCURACY = 200
private const val TAG = "GeocodeSM" private const val TAG = "GeocodeSM"
val geocodedLocation = SensorManager.BasicSensor( val geocodedLocation = SensorManager.BasicSensor(
"geocoded_location", "geocoded_location",
@ -58,7 +62,14 @@ class GeocodeSensorManager : SensorManager {
return@addOnSuccessListener 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) address = Geocoder(context)
.getFromLocation(location.latitude, location.longitude, 1) .getFromLocation(location.latitude, location.longitude, 1)
.firstOrNull() .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.common.data.integration.UpdateLocation
import io.homeassistant.companion.android.database.AppDatabase import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.sensor.Attribute import io.homeassistant.companion.android.database.sensor.Attribute
import io.homeassistant.companion.android.database.sensor.Setting
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -32,7 +33,10 @@ import kotlinx.coroutines.runBlocking
class LocationSensorManager : BroadcastReceiver(), SensorManager { class LocationSensorManager : BroadcastReceiver(), SensorManager {
companion object { 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 = const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_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.basic_sensor_name_location_zone,
R.string.sensor_description_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" internal const val TAG = "LocBroadcastReceiver"
private var isBackgroundLocationSetup = false private var isBackgroundLocationSetup = false
@ -177,11 +187,17 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
private fun handleLocationUpdate(intent: Intent) { private fun handleLocationUpdate(intent: Intent) {
Log.d(TAG, "Received location update.") Log.d(TAG, "Received location update.")
LocationResult.extractResult(intent)?.lastLocation?.let { LocationResult.extractResult(intent)?.lastLocation?.let { location ->
if (it.accuracy > MINIMUM_ACCURACY) { val sensorDao = AppDatabase.getInstance(latestContext).sensorDao()
Log.w(TAG, "Location accuracy didn't meet requirements, disregarding: $it") 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 { } else {
sendLocationUpdate(it) sendLocationUpdate(location)
} }
} }
} }
@ -194,7 +210,14 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
return 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( Log.w(
TAG, TAG,
"Geofence location accuracy didn't meet requirements, requesting new location." "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( Log.d(
TAG, "Last Location: " + TAG, "Last Location: " +
"\nCoords:(${location.latitude}, ${location.longitude})" + "\nCoords:(${location.latitude}, ${location.longitude})" +
@ -217,7 +240,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
accuracy = location.accuracy.toInt() accuracy = location.accuracy.toInt()
} }
val updateLocation = UpdateLocation( val updateLocation = UpdateLocation(
"", name,
arrayOf(location.latitude, location.longitude), arrayOf(location.latitude, location.longitude),
accuracy, accuracy,
location.speed.toInt(), location.speed.toInt(),
@ -289,16 +312,32 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
Log.w(TAG, "Not getting single accurate location because of permissions.") Log.w(TAG, "Not getting single accurate location because of permissions.")
return return
} }
if (!isEnabled(latestContext, singleAccurateLocation.id)) {
Log.w(TAG, "Requested single accurate location but it is not enabled.")
return
}
val now = System.currentTimeMillis() val now = System.currentTimeMillis()
val sensorDao = AppDatabase.getInstance(latestContext).sensorDao() 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 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 // 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") Log.d(TAG, "Not requesting accurate location, last accurate location was too recent")
return return
} }
sensorDao.add(Attribute(backgroundLocation.id, "lastAccurateLocationRequest", now.toString(), "string")) sensorDao.add(Attribute(singleAccurateLocation.id, "lastAccurateLocationRequest", now.toString(), "string"))
val maxRetries = 5 val maxRetries = 5
val request = createLocationRequest() val request = createLocationRequest()
@ -327,7 +366,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
} }
when { when {
locationResult.lastLocation.accuracy <= MINIMUM_ACCURACY -> { locationResult.lastLocation.accuracy <= minAccuracy -> {
Log.d(TAG, "Location accurate enough, all done with high accuracy.") Log.d(TAG, "Location accurate enough, all done with high accuracy.")
runBlocking { sendLocationUpdate(locationResult.lastLocation) } runBlocking { sendLocationUpdate(locationResult.lastLocation) }
if (wakeLock?.isHeld == true) wakeLock.release() if (wakeLock?.isHeld == true) wakeLock.release()
@ -337,7 +376,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
TAG, TAG,
"No location was accurate enough, sending our last location anyway" "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) } runBlocking { sendLocationUpdate(locationResult.lastLocation) }
if (wakeLock?.isHeld == true) wakeLock.release() if (wakeLock?.isHeld == true) wakeLock.release()
} }
@ -361,7 +400,7 @@ class LocationSensorManager : BroadcastReceiver(), SensorManager {
get() = R.string.sensor_name_location get() = R.string.sensor_name_location
override val availableSensors: List<SensorManager.BasicSensor> override val availableSensors: List<SensorManager.BasicSensor>
get() = listOf(backgroundLocation, zoneLocation) get() = listOf(singleAccurateLocation, backgroundLocation, zoneLocation)
override fun requiredPermissions(): Array<String> { override fun requiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {

View file

@ -174,10 +174,9 @@ class SensorDetailFragment(
val pref = findPreference(key) ?: EditTextPreference(requireContext()) val pref = findPreference(key) ?: EditTextPreference(requireContext())
pref.key = key pref.key = key
pref.title = setting.name pref.title = setting.name
if (setting.value != "") pref.dialogTitle = setting.name
pref.summary = setting.value pref.text = setting.value
else pref.summaryProvider = EditTextPreference.SimpleSummaryProvider.getInstance()
pref.summary = pref.text
pref.isIconSpaceReserved = false pref.isIconSpaceReserved = false
pref.setOnBindEditTextListener { fieldType -> pref.setOnBindEditTextListener { fieldType ->

View file

@ -40,6 +40,7 @@
<string name="basic_sensor_name_interactive">Interactive</string> <string name="basic_sensor_name_interactive">Interactive</string>
<string name="basic_sensor_name_internal_storage">Internal Storage</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_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_background">Background Location</string>
<string name="basic_sensor_name_location_zone">Location Zone</string> <string name="basic_sensor_name_location_zone">Location Zone</string>
<string name="basic_sensor_name_phone">Phone State</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_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_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_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_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_zone">Import existing Home Assistant zones as geofences for zone based tracking.</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_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_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> <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>