@@ -9,9 +9,13 @@ import com.kylecorry.trailsensecore.domain.Accuracy
9
9
import com.kylecorry.trailsensecore.domain.geo.Coordinate
10
10
import com.kylecorry.trailsensecore.infrastructure.sensors.AbstractSensor
11
11
import com.kylecorry.trailsensecore.infrastructure.sensors.SensorChecker
12
+ import com.kylecorry.trailsensecore.infrastructure.system.PermissionUtils
13
+ import java.time.Duration
12
14
import java.time.Instant
13
15
14
- class GPS (context : Context ) : AbstractSensor(), IGPS {
16
+
17
+ @SuppressLint(" MissingPermission" )
18
+ class GPS (private val context : Context ) : AbstractSensor(), IGPS {
15
19
16
20
override val hasValidReading: Boolean
17
21
get() = hadRecentValidReading()
@@ -34,14 +38,14 @@ class GPS(context: Context) : AbstractSensor(), IGPS {
34
38
override val speed: Float
35
39
get() = _speed
36
40
37
- override val altitude: Float
38
- get() = _altitude
39
-
40
41
override val time: Instant
41
42
get() = _time
42
43
43
- private val locationManager = context.getSystemService<LocationManager >()
44
- private val sensorChecker = SensorChecker (context)
44
+ override val altitude: Float
45
+ get() = _altitude
46
+
47
+ private val locationManager by lazy { context.getSystemService<LocationManager >() }
48
+ private val sensorChecker by lazy { SensorChecker (context) }
45
49
private val locationListener = SimpleLocationListener { updateLastLocation(it, true ) }
46
50
47
51
private var _altitude = 0f
@@ -54,19 +58,25 @@ class GPS(context: Context) : AbstractSensor(), IGPS {
54
58
private var _location = Coordinate .zero
55
59
56
60
private var lastLocation: Location ? = null
57
- private var lastUpdate = 0L
58
61
59
- private var fixStart: Long = 0L
60
- private val maxFixTime = 8000L
62
+ init {
63
+ try {
64
+ if (PermissionUtils .isLocationEnabled(context)) {
65
+ updateLastLocation(
66
+ locationManager?.getLastKnownLocation(LocationManager .GPS_PROVIDER ),
67
+ false
68
+ )
69
+ }
70
+ } catch (e: Exception ) {
71
+ // Do nothing
72
+ }
73
+ }
61
74
62
- @SuppressLint(" MissingPermission" )
63
75
override fun startImpl () {
64
76
if (! sensorChecker.hasGPS()) {
65
77
return
66
78
}
67
79
68
- fixStart = System .currentTimeMillis()
69
-
70
80
if (lastLocation == null ) {
71
81
updateLastLocation(
72
82
locationManager?.getLastKnownLocation(LocationManager .GPS_PROVIDER ),
@@ -91,104 +101,47 @@ class GPS(context: Context) : AbstractSensor(), IGPS {
91
101
return
92
102
}
93
103
104
+ _location = Coordinate (location.latitude, location.longitude)
94
105
_time = Instant .ofEpochMilli(location.time)
95
-
96
- val satellites = location.extras.getInt(" satellites" )
97
- val dt = System .currentTimeMillis() - fixStart
98
-
99
- if (useNewLocation(lastLocation, location) &&
100
- location.hasAltitude() && location.altitude != 0.0
101
- ) {
102
- // Forces an altitude update irrespective of the satellite count - helps when the GPS is being polled in the background
103
- _altitude = location.altitude.toFloat()
104
- }
105
-
106
- if (satellites < 4 && dt < maxFixTime) {
107
- return
108
- }
109
-
110
- if (! useNewLocation(lastLocation, location)) {
111
- if (notify) notifyListeners()
112
- return
113
- }
114
-
115
- fixStart = System .currentTimeMillis()
116
- lastUpdate = fixStart
117
- _satellites = satellites
118
- lastLocation = location
119
-
120
- if (location.hasAccuracy()) {
121
- this ._accuracy = when {
122
- location.accuracy < 8 -> Accuracy .High
123
- location.accuracy < 16 -> Accuracy .Medium
124
- else -> Accuracy .Low
125
- }
126
- this ._horizontalAccuracy = location.accuracy
106
+ _satellites =
107
+ if (location.extras?.containsKey(" satellites" ) == true ) location.extras.getInt(" satellites" ) else 0
108
+ _altitude = if (location.hasAltitude()) location.altitude.toFloat() else 0f
109
+ val accuracy = if (location.hasAccuracy()) location.accuracy else null
110
+ _accuracy = when {
111
+ accuracy != null && accuracy < 8 -> Accuracy .High
112
+ accuracy != null && accuracy < 16 -> Accuracy .Medium
113
+ accuracy != null -> Accuracy .Low
114
+ else -> Accuracy .Unknown
127
115
}
128
-
129
- if (android.os.Build .VERSION .SDK_INT >= android.os.Build .VERSION_CODES .O ) {
130
- if (location.hasVerticalAccuracy()) {
131
- this ._verticalAccuracy = location.verticalAccuracyMeters
116
+ _horizontalAccuracy = accuracy ? : 0f
117
+ _verticalAccuracy =
118
+ if (android.os.Build .VERSION .SDK_INT >= android.os.Build .VERSION_CODES .O && location.hasVerticalAccuracy()) {
119
+ location.verticalAccuracyMeters
120
+ } else {
121
+ null
132
122
}
133
- }
134
-
135
- if (location.hasSpeed()) {
136
- _speed =
137
- if (android.os.Build .VERSION .SDK_INT >= android.os.Build .VERSION_CODES .O && location.hasSpeedAccuracy()) {
138
- if (location.speed < location.speedAccuracyMetersPerSecond * 0.68 ) {
139
- 0f
140
- } else {
141
- location.speed
142
- }
143
-
123
+ // TODO: Add speed accuracy to IGPS
124
+ _speed = if (location.hasSpeed()) {
125
+ if (android.os.Build .VERSION .SDK_INT >= android.os.Build .VERSION_CODES .O && location.hasSpeedAccuracy()) {
126
+ if (location.speed < location.speedAccuracyMetersPerSecond * 0.68 ) {
127
+ 0f
144
128
} else {
145
129
location.speed
146
130
}
147
- }
148
-
149
- this ._location = Coordinate (
150
- location.latitude,
151
- location.longitude
152
- )
153
-
154
- if (location.hasAltitude() && location.altitude != 0.0 ) {
155
- _altitude = location.altitude.toFloat()
131
+ } else {
132
+ location.speed
133
+ }
134
+ } else {
135
+ 0f
156
136
}
157
137
158
138
if (notify) notifyListeners()
159
139
}
160
140
161
141
private fun hadRecentValidReading (): Boolean {
162
- val now = System .currentTimeMillis()
163
- val recentThreshold = 1000 * 60 * 2L
164
- return now - lastUpdate <= recentThreshold
165
- }
166
-
167
- private fun useNewLocation (current : Location ? , newLocation : Location ): Boolean {
168
- // Modified from https://stackoverflow.com/questions/10588982/retrieving-of-satellites-used-in-gps-fix-from-android
169
- if (current == null ) {
170
- return true
171
- }
172
-
173
- val timeDelta = newLocation.time - current.time
174
- val isSignificantlyNewer: Boolean = timeDelta > 1000 * 60 * 2
175
- val isSignificantlyOlder: Boolean = timeDelta < - 1000 * 60 * 2
176
- val isNewer = timeDelta > 0
177
-
178
- if (isSignificantlyNewer) {
179
- return true
180
- } else if (isSignificantlyOlder) {
181
- return false
182
- }
183
-
184
- val accuracyDelta = (newLocation.accuracy - current.accuracy).toInt()
185
- val isMoreAccurate = accuracyDelta < 0
186
- val isSignificantlyLessAccurate = accuracyDelta > 30
187
-
188
- if (isMoreAccurate) {
189
- return true
190
- }
191
-
192
- return isNewer && ! isSignificantlyLessAccurate
142
+ val last = time
143
+ val now = Instant .now()
144
+ val recentThreshold = Duration .ofMinutes(2 )
145
+ return Duration .between(last, now) <= recentThreshold && location != Coordinate .zero
193
146
}
194
147
}
0 commit comments