@nitra/geolocation 7.1.5 → 8.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/NitraGeolocation.podspec +18 -0
- package/Package.swift +6 -10
- package/README.md +102 -11
- package/android/build.gradle +51 -22
- package/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationErrors.kt +10 -0
- package/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt +236 -32
- package/dist/docs.json +37 -5
- package/dist/esm/definitions.d.ts +87 -7
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +4 -0
- package/dist/esm/web.js +62 -2
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +66 -9
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +66 -9
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/GeolocationPlugin/GeolocationCallbackManager.swift +9 -0
- package/ios/Sources/GeolocationPlugin/GeolocationConstants.swift +5 -0
- package/ios/Sources/GeolocationPlugin/GeolocationError.swift +4 -1
- package/ios/Sources/GeolocationPlugin/GeolocationPlugin.swift +66 -19
- package/ios/Sources/GeolocationPlugin/IONGLOCPositionModel+JSONTransformer.swift +8 -3
- package/package.json +24 -22
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
package com.capacitorjs.plugins.geolocation
|
|
2
2
|
|
|
3
3
|
import android.Manifest
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.location.Location
|
|
6
|
+
import android.location.LocationListener
|
|
7
|
+
import android.location.LocationManager
|
|
8
|
+
import android.location.OnNmeaMessageListener
|
|
4
9
|
import android.os.Build
|
|
10
|
+
import android.os.Handler
|
|
11
|
+
import android.os.Looper
|
|
5
12
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
6
13
|
import com.getcapacitor.JSObject
|
|
7
14
|
import com.getcapacitor.PermissionState
|
|
@@ -11,15 +18,22 @@ import com.getcapacitor.PluginMethod
|
|
|
11
18
|
import com.getcapacitor.annotation.CapacitorPlugin
|
|
12
19
|
import com.getcapacitor.annotation.Permission
|
|
13
20
|
import com.getcapacitor.annotation.PermissionCallback
|
|
14
|
-
import com.google.android.gms.location.LocationServices
|
|
15
21
|
import io.ionic.libs.iongeolocationlib.controller.IONGLOCController
|
|
16
22
|
import io.ionic.libs.iongeolocationlib.model.IONGLOCException
|
|
17
23
|
import io.ionic.libs.iongeolocationlib.model.IONGLOCLocationOptions
|
|
18
24
|
import io.ionic.libs.iongeolocationlib.model.IONGLOCLocationResult
|
|
25
|
+
import kotlinx.coroutines.async
|
|
19
26
|
import kotlinx.coroutines.CoroutineScope
|
|
20
27
|
import kotlinx.coroutines.Dispatchers
|
|
21
28
|
import kotlinx.coroutines.cancel
|
|
22
29
|
import kotlinx.coroutines.launch
|
|
30
|
+
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
31
|
+
import kotlinx.coroutines.withTimeout
|
|
32
|
+
import java.util.Calendar
|
|
33
|
+
import java.util.Locale
|
|
34
|
+
import java.util.TimeZone
|
|
35
|
+
import kotlin.coroutines.resume
|
|
36
|
+
import org.json.JSONObject
|
|
23
37
|
|
|
24
38
|
@CapacitorPlugin(
|
|
25
39
|
name = "Geolocation",
|
|
@@ -37,9 +51,16 @@ class GeolocationPlugin : Plugin() {
|
|
|
37
51
|
private lateinit var coroutineScope: CoroutineScope
|
|
38
52
|
private val watchingCalls: MutableMap<String, PluginCall> = mutableMapOf()
|
|
39
53
|
|
|
54
|
+
private data class GnssTimeResult(
|
|
55
|
+
val timestamp: Long,
|
|
56
|
+
val status: String?,
|
|
57
|
+
val nmea: String
|
|
58
|
+
)
|
|
59
|
+
|
|
40
60
|
companion object {
|
|
41
61
|
const val LOCATION_ALIAS: String = "location"
|
|
42
62
|
const val COARSE_LOCATION_ALIAS: String = "coarseLocation"
|
|
63
|
+
const val WATCH_SATELLITE_TIME_TIMEOUT_MS: Long = 1000
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
override fun load() {
|
|
@@ -54,11 +75,7 @@ class GeolocationPlugin : Plugin() {
|
|
|
54
75
|
}
|
|
55
76
|
}
|
|
56
77
|
|
|
57
|
-
this.controller = IONGLOCController(
|
|
58
|
-
LocationServices.getFusedLocationProviderClient(context),
|
|
59
|
-
activityLauncher
|
|
60
|
-
)
|
|
61
|
-
|
|
78
|
+
this.controller = IONGLOCController(context, activityLauncher)
|
|
62
79
|
}
|
|
63
80
|
|
|
64
81
|
override fun handleOnDestroy() {
|
|
@@ -82,7 +99,7 @@ class GeolocationPlugin : Plugin() {
|
|
|
82
99
|
* @param onLocationEnabled lambda function to use in case location services are enabled
|
|
83
100
|
*/
|
|
84
101
|
private fun checkLocationState(call: PluginCall, onLocationEnabled: () -> Unit) {
|
|
85
|
-
if (controller.areLocationServicesEnabled(
|
|
102
|
+
if (controller.areLocationServicesEnabled()) {
|
|
86
103
|
onLocationEnabled()
|
|
87
104
|
} else {
|
|
88
105
|
call.sendError(GeolocationErrors.LOCATION_DISABLED)
|
|
@@ -120,7 +137,10 @@ class GeolocationPlugin : Plugin() {
|
|
|
120
137
|
callbackName: String,
|
|
121
138
|
onPermissionGranted: () -> Unit
|
|
122
139
|
) {
|
|
123
|
-
val alias = getAlias(call)
|
|
140
|
+
val alias = getAlias(call) ?: run {
|
|
141
|
+
call.sendError(GeolocationErrors.LOCATION_MANIFEST_PERMISSIONS_MISSING)
|
|
142
|
+
return
|
|
143
|
+
}
|
|
124
144
|
if (getPermissionState(alias) != PermissionState.GRANTED) {
|
|
125
145
|
requestPermissionForAlias(alias, call, callbackName)
|
|
126
146
|
} else {
|
|
@@ -161,6 +181,37 @@ class GeolocationPlugin : Plugin() {
|
|
|
161
181
|
}
|
|
162
182
|
}
|
|
163
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Requests precise location permission. GNSS NMEA data should not be exposed through coarse permission.
|
|
186
|
+
* @param call the PluginCall to use in case we want to send an error
|
|
187
|
+
* @param callbackName a string identifying the callback to call once the permission prompt is answered
|
|
188
|
+
* @param onPermissionGranted lambda function to use in case the permission is enabled
|
|
189
|
+
*/
|
|
190
|
+
private fun handlePrecisePermissionRequest(
|
|
191
|
+
call: PluginCall,
|
|
192
|
+
callbackName: String,
|
|
193
|
+
onPermissionGranted: () -> Unit
|
|
194
|
+
) {
|
|
195
|
+
if (getPermissionState(LOCATION_ALIAS) != PermissionState.GRANTED) {
|
|
196
|
+
requestPermissionForAlias(LOCATION_ALIAS, call, callbackName)
|
|
197
|
+
} else {
|
|
198
|
+
onPermissionGranted()
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Handles precise location permission results.
|
|
204
|
+
* @param call the PluginCall to use in case we want to send an error
|
|
205
|
+
* @param onPermissionGranted lambda function to use in case the permission was granted
|
|
206
|
+
*/
|
|
207
|
+
private fun handlePrecisePermissionResult(call: PluginCall, onPermissionGranted: () -> Unit) {
|
|
208
|
+
if (getPermissionState(LOCATION_ALIAS) == PermissionState.GRANTED) {
|
|
209
|
+
onPermissionGranted()
|
|
210
|
+
} else {
|
|
211
|
+
call.sendError(GeolocationErrors.LOCATION_PERMISSIONS_DENIED)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
164
215
|
/**
|
|
165
216
|
* Clears the watch, removing location updates.
|
|
166
217
|
* @param call the plugin call
|
|
@@ -182,19 +233,20 @@ class GeolocationPlugin : Plugin() {
|
|
|
182
233
|
}
|
|
183
234
|
|
|
184
235
|
/**
|
|
185
|
-
* Gets the appropriate permission alias
|
|
236
|
+
* Gets the appropriate permission alias, based on the Android version,
|
|
237
|
+
* the permissions declared in the manifest
|
|
238
|
+
* and the enableHighAccuracy option provided by the caller.
|
|
186
239
|
* @param call the plugin call
|
|
187
|
-
* @return String with correct alias
|
|
240
|
+
* @return String with correct alias or null if no permissions can be requested
|
|
188
241
|
*/
|
|
189
|
-
private fun getAlias(call: PluginCall): String {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return alias
|
|
242
|
+
private fun getAlias(call: PluginCall): String? {
|
|
243
|
+
val hasFine = isPermissionDeclared(LOCATION_ALIAS)
|
|
244
|
+
val hasCoarse = isPermissionDeclared(COARSE_LOCATION_ALIAS)
|
|
245
|
+
if (!hasFine && !hasCoarse) return null
|
|
246
|
+
val enableHighAccuracy = call.getBoolean("enableHighAccuracy") ?: false
|
|
247
|
+
val shouldRequestFine =
|
|
248
|
+
hasFine && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || enableHighAccuracy)
|
|
249
|
+
return if (shouldRequestFine) LOCATION_ALIAS else COARSE_LOCATION_ALIAS
|
|
198
250
|
}
|
|
199
251
|
|
|
200
252
|
/**
|
|
@@ -204,20 +256,152 @@ class GeolocationPlugin : Plugin() {
|
|
|
204
256
|
private fun getPosition(call: PluginCall) {
|
|
205
257
|
coroutineScope.launch {
|
|
206
258
|
val locationOptions = createOptions(call)
|
|
259
|
+
val gnssTime = async {
|
|
260
|
+
getGnssTimeOrNull(locationOptions.timeout)
|
|
261
|
+
}
|
|
207
262
|
|
|
208
263
|
// call getCurrentPosition method from controller
|
|
209
264
|
val locationResult = controller.getCurrentPosition(activity, locationOptions)
|
|
210
265
|
|
|
211
266
|
locationResult
|
|
212
267
|
.onSuccess { location ->
|
|
213
|
-
call.sendSuccess(getJSObjectForLocation(location))
|
|
268
|
+
call.sendSuccess(getJSObjectForLocation(location, gnssTime.await()))
|
|
214
269
|
}
|
|
215
270
|
.onFailure { exception ->
|
|
271
|
+
gnssTime.cancel()
|
|
216
272
|
onLocationError(exception, call)
|
|
217
273
|
}
|
|
218
274
|
}
|
|
219
275
|
}
|
|
220
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Waits until the GNSS receiver emits an RMC NMEA sentence containing date and UTC time.
|
|
279
|
+
* @return satellite time parsed from NMEA
|
|
280
|
+
*/
|
|
281
|
+
private suspend fun awaitSatelliteTime(): GnssTimeResult {
|
|
282
|
+
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
|
283
|
+
|
|
284
|
+
if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
|
|
285
|
+
throw IllegalStateException("GPS provider is disabled.")
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return suspendCancellableCoroutine { continuation ->
|
|
289
|
+
lateinit var listener: OnNmeaMessageListener
|
|
290
|
+
val locationListener = object : LocationListener {
|
|
291
|
+
override fun onLocationChanged(location: Location) = Unit
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
fun cleanup() {
|
|
295
|
+
locationManager.removeNmeaListener(listener)
|
|
296
|
+
locationManager.removeUpdates(locationListener)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
listener = OnNmeaMessageListener { message, _ ->
|
|
300
|
+
val gnssTime = parseSatelliteTime(message)
|
|
301
|
+
if (gnssTime != null && continuation.isActive) {
|
|
302
|
+
cleanup()
|
|
303
|
+
continuation.resume(gnssTime)
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
val registered = locationManager.addNmeaListener(listener, Handler(Looper.getMainLooper()))
|
|
308
|
+
if (!registered && continuation.isActive) {
|
|
309
|
+
continuation.cancel(IllegalStateException("Unable to register NMEA listener."))
|
|
310
|
+
return@suspendCancellableCoroutine
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
try {
|
|
314
|
+
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000L, 0f, locationListener)
|
|
315
|
+
} catch (exception: Throwable) {
|
|
316
|
+
cleanup()
|
|
317
|
+
continuation.cancel(exception)
|
|
318
|
+
return@suspendCancellableCoroutine
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
continuation.invokeOnCancellation {
|
|
322
|
+
cleanup()
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Returns satellite time if GNSS RMC NMEA data is available within the timeout.
|
|
329
|
+
* Position responses use null instead of failing when GNSS time is unavailable.
|
|
330
|
+
* @param timeout maximum wait time in milliseconds
|
|
331
|
+
* @return satellite time parsed from NMEA or null
|
|
332
|
+
*/
|
|
333
|
+
private suspend fun getGnssTimeOrNull(timeout: Long): GnssTimeResult? {
|
|
334
|
+
if (timeout <= 0) {
|
|
335
|
+
return null
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return try {
|
|
339
|
+
withTimeout(timeout) {
|
|
340
|
+
awaitSatelliteTime()
|
|
341
|
+
}
|
|
342
|
+
} catch (_: Throwable) {
|
|
343
|
+
null
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Parses UTC date/time from RMC NMEA sentences such as GPRMC, GNRMC, or GARMC.
|
|
349
|
+
* @param message raw NMEA sentence
|
|
350
|
+
* @return satellite time parsed from NMEA, or null if the sentence does not contain time
|
|
351
|
+
*/
|
|
352
|
+
private fun parseSatelliteTime(message: String): GnssTimeResult? {
|
|
353
|
+
val sentence = message.trim()
|
|
354
|
+
val withoutChecksum = sentence.substringBefore("*")
|
|
355
|
+
val fields = withoutChecksum.split(",")
|
|
356
|
+
if (fields.size <= 9 || !fields[0].endsWith("RMC")) {
|
|
357
|
+
return null
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
val time = fields[1]
|
|
361
|
+
val status = fields[2].ifBlank { null }
|
|
362
|
+
val date = fields[9]
|
|
363
|
+
|
|
364
|
+
if (status != "A") {
|
|
365
|
+
return null
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (time.length < 6 || date.length != 6) {
|
|
369
|
+
return null
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
val hour = time.substring(0, 2).toIntOrNull() ?: return null
|
|
373
|
+
val minute = time.substring(2, 4).toIntOrNull() ?: return null
|
|
374
|
+
val second = time.substring(4, 6).toIntOrNull() ?: return null
|
|
375
|
+
val millisecond = time.substringAfter(".", "").take(3).padEnd(3, '0').toIntOrNull() ?: 0
|
|
376
|
+
val day = date.substring(0, 2).toIntOrNull() ?: return null
|
|
377
|
+
val month = date.substring(2, 4).toIntOrNull() ?: return null
|
|
378
|
+
val yearSuffix = date.substring(4, 6).toIntOrNull() ?: return null
|
|
379
|
+
val year = if (yearSuffix >= 80) 1900 + yearSuffix else 2000 + yearSuffix
|
|
380
|
+
|
|
381
|
+
val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US).apply {
|
|
382
|
+
clear()
|
|
383
|
+
isLenient = false
|
|
384
|
+
set(Calendar.YEAR, year)
|
|
385
|
+
set(Calendar.MONTH, month - 1)
|
|
386
|
+
set(Calendar.DAY_OF_MONTH, day)
|
|
387
|
+
set(Calendar.HOUR_OF_DAY, hour)
|
|
388
|
+
set(Calendar.MINUTE, minute)
|
|
389
|
+
set(Calendar.SECOND, second)
|
|
390
|
+
set(Calendar.MILLISECOND, millisecond)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
val timestamp = try {
|
|
394
|
+
calendar.timeInMillis
|
|
395
|
+
} catch (_: IllegalArgumentException) {
|
|
396
|
+
return null
|
|
397
|
+
}
|
|
398
|
+
return GnssTimeResult(
|
|
399
|
+
timestamp = timestamp,
|
|
400
|
+
status = status,
|
|
401
|
+
nmea = sentence
|
|
402
|
+
)
|
|
403
|
+
}
|
|
404
|
+
|
|
221
405
|
/**
|
|
222
406
|
* Starts watching the device's location by requesting location updates
|
|
223
407
|
* @param call the plugin call
|
|
@@ -232,8 +416,9 @@ class GeolocationPlugin : Plugin() {
|
|
|
232
416
|
controller.addWatch(activity, locationOptions, watchId).collect { result ->
|
|
233
417
|
result.onSuccess { locationList ->
|
|
234
418
|
locationList.forEach { locationResult ->
|
|
419
|
+
val gnssTime = getGnssTimeOrNull(WATCH_SATELLITE_TIME_TIMEOUT_MS)
|
|
235
420
|
call.sendSuccess(
|
|
236
|
-
result = getJSObjectForLocation(locationResult),
|
|
421
|
+
result = getJSObjectForLocation(locationResult, gnssTime),
|
|
237
422
|
keepCallback = true)
|
|
238
423
|
}
|
|
239
424
|
}
|
|
@@ -250,7 +435,10 @@ class GeolocationPlugin : Plugin() {
|
|
|
250
435
|
* @param locationResult IONGLOCLocationResult object with the location to convert
|
|
251
436
|
* @return JSObject with converted JSON object
|
|
252
437
|
*/
|
|
253
|
-
private fun getJSObjectForLocation(
|
|
438
|
+
private fun getJSObjectForLocation(
|
|
439
|
+
locationResult: IONGLOCLocationResult,
|
|
440
|
+
gnssTime: GnssTimeResult?
|
|
441
|
+
): JSObject {
|
|
254
442
|
val coords = JSObject().apply {
|
|
255
443
|
put("latitude", locationResult.latitude)
|
|
256
444
|
put("longitude", locationResult.longitude)
|
|
@@ -258,12 +446,12 @@ class GeolocationPlugin : Plugin() {
|
|
|
258
446
|
put("altitude", locationResult.altitude)
|
|
259
447
|
locationResult.altitudeAccuracy?.let { put("altitudeAccuracy", it) }
|
|
260
448
|
put("speed", locationResult.speed)
|
|
261
|
-
put("heading", locationResult.heading)
|
|
449
|
+
put("heading", if (locationResult.heading != -1f) locationResult.heading else null)
|
|
262
450
|
put("isMock", locationResult.isMock)
|
|
263
451
|
put("provider", locationResult.provider)
|
|
264
452
|
}
|
|
265
453
|
return JSObject().apply {
|
|
266
|
-
put("timestamp",
|
|
454
|
+
put("timestamp", gnssTime?.timestamp ?: JSONObject.NULL)
|
|
267
455
|
put("coords", coords)
|
|
268
456
|
}
|
|
269
457
|
}
|
|
@@ -294,6 +482,9 @@ class GeolocationPlugin : Plugin() {
|
|
|
294
482
|
is IONGLOCException.IONGLOCLocationRetrievalTimeoutException -> {
|
|
295
483
|
call.sendError(GeolocationErrors.GET_LOCATION_TIMEOUT)
|
|
296
484
|
}
|
|
485
|
+
is IONGLOCException.IONGLOCLocationAndNetworkDisabledException -> {
|
|
486
|
+
call.sendError(GeolocationErrors.NETWORK_LOCATION_DISABLED_ERROR)
|
|
487
|
+
}
|
|
297
488
|
else -> {
|
|
298
489
|
call.sendError(GeolocationErrors.POSITION_UNAVAILABLE)
|
|
299
490
|
}
|
|
@@ -315,7 +506,7 @@ class GeolocationPlugin : Plugin() {
|
|
|
315
506
|
}
|
|
316
507
|
|
|
317
508
|
/**
|
|
318
|
-
* Extension function to return
|
|
509
|
+
* Extension function to return an unsuccessful plugin result
|
|
319
510
|
* @param error error class representing the error to return, containing a code and message
|
|
320
511
|
*/
|
|
321
512
|
private fun PluginCall.sendError(error: GeolocationErrors.ErrorInfo) {
|
|
@@ -328,13 +519,26 @@ class GeolocationPlugin : Plugin() {
|
|
|
328
519
|
* @return IONGLOCLocationOptions object
|
|
329
520
|
*/
|
|
330
521
|
private fun createOptions(call: PluginCall): IONGLOCLocationOptions {
|
|
331
|
-
val timeout = call.
|
|
332
|
-
val maximumAge = call.
|
|
522
|
+
val timeout = call.getNumber("timeout", 10000)
|
|
523
|
+
val maximumAge = call.getNumber("maximumAge", 0)
|
|
333
524
|
val enableHighAccuracy = call.getBoolean("enableHighAccuracy", false) ?: false
|
|
334
|
-
val minimumUpdateInterval = call.
|
|
335
|
-
|
|
336
|
-
val
|
|
525
|
+
val minimumUpdateInterval = call.getNumber("minimumUpdateInterval", 5000)
|
|
526
|
+
val enableLocationFallback = call.getBoolean("enableLocationFallback", true) ?: true
|
|
527
|
+
val interval = call.getNumber("interval", -1).let {
|
|
528
|
+
// using "< 0" and not "<= 0" because 0 is a valid value for interval
|
|
529
|
+
if (it < 0) timeout else it
|
|
530
|
+
}
|
|
337
531
|
|
|
338
|
-
return
|
|
532
|
+
return IONGLOCLocationOptions(
|
|
533
|
+
timeout = timeout,
|
|
534
|
+
maximumAge = maximumAge,
|
|
535
|
+
enableHighAccuracy = enableHighAccuracy,
|
|
536
|
+
enableLocationManagerFallback = enableLocationFallback,
|
|
537
|
+
interval = interval,
|
|
538
|
+
minUpdateInterval = minimumUpdateInterval
|
|
539
|
+
)
|
|
339
540
|
}
|
|
340
|
-
|
|
541
|
+
|
|
542
|
+
private fun PluginCall.getNumber(name: String, defaultValue: Long): Long =
|
|
543
|
+
getLong(name) ?: getInt(name)?.toLong() ?: defaultValue
|
|
544
|
+
}
|
package/dist/docs.json
CHANGED
|
@@ -142,9 +142,9 @@
|
|
|
142
142
|
"name": "since"
|
|
143
143
|
}
|
|
144
144
|
],
|
|
145
|
-
"docs": "
|
|
145
|
+
"docs": "GNSS satellite timestamp for coords, in milliseconds since the Unix epoch.\n\nOn Android, this is populated only when a GNSS RMC NMEA message is available.\nOn iOS and web, this is always `null`.",
|
|
146
146
|
"complexTypes": [],
|
|
147
|
-
"type": "number"
|
|
147
|
+
"type": "number | null"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"name": "coords",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
],
|
|
157
157
|
"docs": "The GPS coordinates along with the accuracy of the data",
|
|
158
158
|
"complexTypes": [],
|
|
159
|
-
"type": "{ latitude: number; longitude: number; accuracy: number; altitudeAccuracy: number | null | undefined; altitude: number | null; speed: number | null; heading: number | null; }"
|
|
159
|
+
"type": "{ latitude: number; longitude: number; accuracy: number; altitudeAccuracy: number | null | undefined; altitude: number | null; speed: number | null; heading: number | null; magneticHeading: number | null | undefined; trueHeading: number | null | undefined; headingAccuracy: number | null | undefined; course: number | null | undefined; isMock?: boolean | undefined; provider?: string | undefined; }"
|
|
160
160
|
}
|
|
161
161
|
]
|
|
162
162
|
},
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"name": "since"
|
|
196
196
|
}
|
|
197
197
|
],
|
|
198
|
-
"docs": "The maximum wait time in milliseconds for location updates
|
|
198
|
+
"docs": "The maximum wait time in milliseconds for location updates.",
|
|
199
199
|
"complexTypes": [],
|
|
200
200
|
"type": "number | undefined"
|
|
201
201
|
},
|
|
@@ -227,9 +227,41 @@
|
|
|
227
227
|
"name": "since"
|
|
228
228
|
}
|
|
229
229
|
],
|
|
230
|
-
"docs": "The
|
|
230
|
+
"docs": "The minimum update interval for `watchPosition`. Not to be confused with `interval`.\n\nIf location updates are available faster than this interval then an update\nwill only occur if the minimum update interval has expired since the last location update.\n\nThis parameter is only available for Android. It has no effect on iOS or Web platforms.",
|
|
231
231
|
"complexTypes": [],
|
|
232
232
|
"type": "number | undefined"
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"name": "interval",
|
|
236
|
+
"tags": [
|
|
237
|
+
{
|
|
238
|
+
"text": "`timeout`",
|
|
239
|
+
"name": "default"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"text": "8.0.0",
|
|
243
|
+
"name": "since"
|
|
244
|
+
}
|
|
245
|
+
],
|
|
246
|
+
"docs": "Desired interval in milliseconds to receive location updates in `watchPosition`.\n\nFor very low values of `interval` (a couple seconds or less),\nthe platform may not guarantee timely location updates - they may take longer than specified.\nThe platform may also be able to provide location updates faster than `interval`.\nYou may use `minimumUpdateInterval` to control that behavior.\n\nFor backwards compatibility with version 7.1.x, if no value is passed,\nthe default value of this parameter is that of `timeout`.\n\nThis parameter is only available for Android. It has no effect on iOS or Web platforms.",
|
|
247
|
+
"complexTypes": [],
|
|
248
|
+
"type": "number | undefined"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"name": "enableLocationFallback",
|
|
252
|
+
"tags": [
|
|
253
|
+
{
|
|
254
|
+
"text": "true",
|
|
255
|
+
"name": "default"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"text": "8.0.0",
|
|
259
|
+
"name": "since"
|
|
260
|
+
}
|
|
261
|
+
],
|
|
262
|
+
"docs": "Whether to fall back to the Android framework's `LocationManager` in case Google Play Service's location settings checks fail.\nThis can happen for multiple reasons - e.g. device has no Play Services or device has no network connection (Airplane Mode)\nIf set to `false`, failures are propagated to the caller.\nNote that `LocationManager` may not be as effective as Google Play Services implementation.\nIf the device's in airplane mode, only the GPS provider is used, which may take longer to return a location, depending on GPS signal.\nThis means that to receive location in such circumstances, you may need to provide a higher timeout.\n\nThis parameter is only available for Android. It has no effect on iOS or Web platforms.",
|
|
263
|
+
"complexTypes": [],
|
|
264
|
+
"type": "boolean | undefined"
|
|
233
265
|
}
|
|
234
266
|
]
|
|
235
267
|
},
|
|
@@ -71,11 +71,14 @@ export interface ClearWatchOptions {
|
|
|
71
71
|
}
|
|
72
72
|
export interface Position {
|
|
73
73
|
/**
|
|
74
|
-
*
|
|
74
|
+
* GNSS satellite timestamp for coords, in milliseconds since the Unix epoch.
|
|
75
|
+
*
|
|
76
|
+
* On Android, this is populated only when a GNSS RMC NMEA message is available.
|
|
77
|
+
* On iOS and web, this is always `null`.
|
|
75
78
|
*
|
|
76
79
|
* @since 1.0.0
|
|
77
80
|
*/
|
|
78
|
-
timestamp: number;
|
|
81
|
+
timestamp: number | null;
|
|
79
82
|
/**
|
|
80
83
|
* The GPS coordinates along with the accuracy of the data
|
|
81
84
|
*
|
|
@@ -121,11 +124,60 @@ export interface Position {
|
|
|
121
124
|
*/
|
|
122
125
|
speed: number | null;
|
|
123
126
|
/**
|
|
124
|
-
* The heading the user is facing (if available)
|
|
127
|
+
* The heading the user is facing (if available).
|
|
128
|
+
*
|
|
129
|
+
* Historically, this field returned the direction of travel (course) on iOS and Android.
|
|
130
|
+
* It now prioritizes the compass heading (true or magnetic) if available, falling back
|
|
131
|
+
* to the direction of travel (course).
|
|
125
132
|
*
|
|
126
133
|
* @since 1.0.0
|
|
127
134
|
*/
|
|
128
135
|
heading: number | null;
|
|
136
|
+
/**
|
|
137
|
+
* The heading (measured in degrees) relative to magnetic north.
|
|
138
|
+
*
|
|
139
|
+
* Only available when using `watchPosition`.
|
|
140
|
+
*
|
|
141
|
+
* @since 8.2.0
|
|
142
|
+
*/
|
|
143
|
+
magneticHeading: number | null | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* The heading (measured in degrees) relative to true north.
|
|
146
|
+
*
|
|
147
|
+
* Only available when using `watchPosition`.
|
|
148
|
+
*
|
|
149
|
+
* @since 8.2.0
|
|
150
|
+
*/
|
|
151
|
+
trueHeading: number | null | undefined;
|
|
152
|
+
/**
|
|
153
|
+
* The maximum deviation (measured in degrees) between the reported heading and the true geomagnetic heading.
|
|
154
|
+
*
|
|
155
|
+
* Only available when using `watchPosition`.
|
|
156
|
+
*
|
|
157
|
+
* @since 8.2.0
|
|
158
|
+
*/
|
|
159
|
+
headingAccuracy: number | null | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* The direction in which the device is travelling, measured in degrees and relative to due north.
|
|
162
|
+
*
|
|
163
|
+
* Only available when using `watchPosition`.
|
|
164
|
+
*
|
|
165
|
+
* @since 8.2.0
|
|
166
|
+
*/
|
|
167
|
+
course: number | null | undefined;
|
|
168
|
+
/**
|
|
169
|
+
* Whether the location was generated by a mock provider (e.g. a fake GPS app).
|
|
170
|
+
*
|
|
171
|
+
* On Android, available on API 31+ (uses `isMock()`), falls back to `isFromMockProvider()` on older versions.
|
|
172
|
+
* On iOS, detected via `CLLocationSourceInformation` on iOS 15+.
|
|
173
|
+
*/
|
|
174
|
+
isMock?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* The location provider that generated this location (Android only).
|
|
177
|
+
*
|
|
178
|
+
* Common values: `"gps"`, `"network"`, `"fused"`.
|
|
179
|
+
*/
|
|
180
|
+
provider?: string;
|
|
129
181
|
};
|
|
130
182
|
}
|
|
131
183
|
export interface PositionOptions {
|
|
@@ -142,9 +194,6 @@ export interface PositionOptions {
|
|
|
142
194
|
/**
|
|
143
195
|
* The maximum wait time in milliseconds for location updates.
|
|
144
196
|
*
|
|
145
|
-
* In Android, since version 7.1.0 of the plugin, it is also used to determine the
|
|
146
|
-
* interval of location updates for `watchPosition`.
|
|
147
|
-
*
|
|
148
197
|
* @default 10000
|
|
149
198
|
* @since 1.0.0
|
|
150
199
|
*/
|
|
@@ -157,7 +206,7 @@ export interface PositionOptions {
|
|
|
157
206
|
*/
|
|
158
207
|
maximumAge?: number;
|
|
159
208
|
/**
|
|
160
|
-
* The
|
|
209
|
+
* The minimum update interval for `watchPosition`. Not to be confused with `interval`.
|
|
161
210
|
*
|
|
162
211
|
* If location updates are available faster than this interval then an update
|
|
163
212
|
* will only occur if the minimum update interval has expired since the last location update.
|
|
@@ -168,6 +217,37 @@ export interface PositionOptions {
|
|
|
168
217
|
* @since 6.1.0
|
|
169
218
|
*/
|
|
170
219
|
minimumUpdateInterval?: number;
|
|
220
|
+
/**
|
|
221
|
+
* Desired interval in milliseconds to receive location updates in `watchPosition`.
|
|
222
|
+
*
|
|
223
|
+
* For very low values of `interval` (a couple seconds or less),
|
|
224
|
+
* the platform may not guarantee timely location updates - they may take longer than specified.
|
|
225
|
+
* The platform may also be able to provide location updates faster than `interval`.
|
|
226
|
+
* You may use `minimumUpdateInterval` to control that behavior.
|
|
227
|
+
*
|
|
228
|
+
* For backwards compatibility with version 7.1.x, if no value is passed,
|
|
229
|
+
* the default value of this parameter is that of `timeout`.
|
|
230
|
+
*
|
|
231
|
+
* This parameter is only available for Android. It has no effect on iOS or Web platforms.
|
|
232
|
+
*
|
|
233
|
+
* @default `timeout`
|
|
234
|
+
* @since 8.0.0
|
|
235
|
+
*/
|
|
236
|
+
interval?: number;
|
|
237
|
+
/**
|
|
238
|
+
* Whether to fall back to the Android framework's `LocationManager` in case Google Play Service's location settings checks fail.
|
|
239
|
+
* This can happen for multiple reasons - e.g. device has no Play Services or device has no network connection (Airplane Mode)
|
|
240
|
+
* If set to `false`, failures are propagated to the caller.
|
|
241
|
+
* Note that `LocationManager` may not be as effective as Google Play Services implementation.
|
|
242
|
+
* If the device's in airplane mode, only the GPS provider is used, which may take longer to return a location, depending on GPS signal.
|
|
243
|
+
* This means that to receive location in such circumstances, you may need to provide a higher timeout.
|
|
244
|
+
*
|
|
245
|
+
* This parameter is only available for Android. It has no effect on iOS or Web platforms.
|
|
246
|
+
*
|
|
247
|
+
* @default true
|
|
248
|
+
* @since 8.0.0
|
|
249
|
+
*/
|
|
250
|
+
enableLocationFallback?: boolean;
|
|
171
251
|
}
|
|
172
252
|
export type WatchPositionCallback = (position: Position | null, err?: any) => void;
|
|
173
253
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState } from '@capacitor/core';\n\nexport type CallbackID = string;\n\nexport interface PermissionStatus {\n /**\n * Permission state for location alias.\n *\n * On Android it requests/checks both ACCESS_COARSE_LOCATION and\n * ACCESS_FINE_LOCATION permissions.\n *\n * On iOS and web it requests/checks location permission.\n *\n * @since 1.0.0\n */\n location: PermissionState;\n\n /**\n * Permission state for coarseLocation alias.\n *\n * On Android it requests/checks ACCESS_COARSE_LOCATION.\n *\n * On Android 12+, users can choose between Approximate location (ACCESS_COARSE_LOCATION) or\n * Precise location (ACCESS_FINE_LOCATION), so this alias can be used if the app doesn't\n * need high accuracy.\n *\n * On iOS and web it will have the same value as location alias.\n *\n * @since 1.2.0\n */\n coarseLocation: PermissionState;\n}\n\nexport type GeolocationPermissionType = 'location' | 'coarseLocation';\n\nexport interface GeolocationPluginPermissions {\n permissions: GeolocationPermissionType[];\n}\n\nexport interface GeolocationPlugin {\n /**\n * Get the current GPS location of the device\n *\n * @since 1.0.0\n */\n getCurrentPosition(options?: PositionOptions): Promise<Position>;\n\n /**\n * Set up a watch for location changes. Note that watching for location changes\n * can consume a large amount of energy. Be smart about listening only when you need to.\n *\n * @since 1.0.0\n */\n watchPosition(options: PositionOptions, callback: WatchPositionCallback): Promise<CallbackID>;\n\n /**\n * Clear a given watch\n *\n * @since 1.0.0\n */\n clearWatch(options: ClearWatchOptions): Promise<void>;\n\n /**\n * Check location permissions. Will throw if system location services are disabled.\n *\n * @since 1.0.0\n */\n checkPermissions(): Promise<PermissionStatus>;\n\n /**\n * Request location permissions. Will throw if system location services are disabled.\n *\n * Not available on web.\n *\n * @since 1.0.0\n */\n requestPermissions(permissions?: GeolocationPluginPermissions): Promise<PermissionStatus>;\n}\n\nexport interface ClearWatchOptions {\n id: CallbackID;\n}\n\nexport interface Position {\n /**\n *
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { PermissionState } from '@capacitor/core';\n\nexport type CallbackID = string;\n\nexport interface PermissionStatus {\n /**\n * Permission state for location alias.\n *\n * On Android it requests/checks both ACCESS_COARSE_LOCATION and\n * ACCESS_FINE_LOCATION permissions.\n *\n * On iOS and web it requests/checks location permission.\n *\n * @since 1.0.0\n */\n location: PermissionState;\n\n /**\n * Permission state for coarseLocation alias.\n *\n * On Android it requests/checks ACCESS_COARSE_LOCATION.\n *\n * On Android 12+, users can choose between Approximate location (ACCESS_COARSE_LOCATION) or\n * Precise location (ACCESS_FINE_LOCATION), so this alias can be used if the app doesn't\n * need high accuracy.\n *\n * On iOS and web it will have the same value as location alias.\n *\n * @since 1.2.0\n */\n coarseLocation: PermissionState;\n}\n\nexport type GeolocationPermissionType = 'location' | 'coarseLocation';\n\nexport interface GeolocationPluginPermissions {\n permissions: GeolocationPermissionType[];\n}\n\nexport interface GeolocationPlugin {\n /**\n * Get the current GPS location of the device\n *\n * @since 1.0.0\n */\n getCurrentPosition(options?: PositionOptions): Promise<Position>;\n\n /**\n * Set up a watch for location changes. Note that watching for location changes\n * can consume a large amount of energy. Be smart about listening only when you need to.\n *\n * @since 1.0.0\n */\n watchPosition(options: PositionOptions, callback: WatchPositionCallback): Promise<CallbackID>;\n\n /**\n * Clear a given watch\n *\n * @since 1.0.0\n */\n clearWatch(options: ClearWatchOptions): Promise<void>;\n\n /**\n * Check location permissions. Will throw if system location services are disabled.\n *\n * @since 1.0.0\n */\n checkPermissions(): Promise<PermissionStatus>;\n\n /**\n * Request location permissions. Will throw if system location services are disabled.\n *\n * Not available on web.\n *\n * @since 1.0.0\n */\n requestPermissions(permissions?: GeolocationPluginPermissions): Promise<PermissionStatus>;\n}\n\nexport interface ClearWatchOptions {\n id: CallbackID;\n}\n\nexport interface Position {\n /**\n * GNSS satellite timestamp for coords, in milliseconds since the Unix epoch.\n *\n * On Android, this is populated only when a GNSS RMC NMEA message is available.\n * On iOS and web, this is always `null`.\n *\n * @since 1.0.0\n */\n timestamp: number | null;\n\n /**\n * The GPS coordinates along with the accuracy of the data\n *\n * @since 1.0.0\n */\n coords: {\n /**\n * Latitude in decimal degrees\n *\n * @since 1.0.0\n */\n latitude: number;\n\n /**\n * longitude in decimal degrees\n *\n * @since 1.0.0\n */\n longitude: number;\n\n /**\n * Accuracy level of the latitude and longitude coordinates in meters\n *\n * @since 1.0.0\n */\n accuracy: number;\n\n /**\n * Accuracy level of the altitude coordinate in meters, if available.\n *\n * Available on all iOS versions and on Android 8.0+.\n *\n * @since 1.0.0\n */\n altitudeAccuracy: number | null | undefined;\n\n /**\n * The altitude the user is at (if available)\n *\n * @since 1.0.0\n */\n altitude: number | null;\n\n /**\n * The speed the user is traveling (if available)\n *\n * @since 1.0.0\n */\n speed: number | null;\n\n /**\n * The heading the user is facing (if available).\n *\n * Historically, this field returned the direction of travel (course) on iOS and Android.\n * It now prioritizes the compass heading (true or magnetic) if available, falling back\n * to the direction of travel (course).\n *\n * @since 1.0.0\n */\n heading: number | null;\n\n /**\n * The heading (measured in degrees) relative to magnetic north.\n *\n * Only available when using `watchPosition`.\n *\n * @since 8.2.0\n */\n magneticHeading: number | null | undefined;\n\n /**\n * The heading (measured in degrees) relative to true north.\n *\n * Only available when using `watchPosition`.\n *\n * @since 8.2.0\n */\n trueHeading: number | null | undefined;\n\n /**\n * The maximum deviation (measured in degrees) between the reported heading and the true geomagnetic heading.\n *\n * Only available when using `watchPosition`.\n *\n * @since 8.2.0\n */\n headingAccuracy: number | null | undefined;\n\n /**\n * The direction in which the device is travelling, measured in degrees and relative to due north.\n *\n * Only available when using `watchPosition`.\n *\n * @since 8.2.0\n */\n course: number | null | undefined;\n\n /**\n * Whether the location was generated by a mock provider (e.g. a fake GPS app).\n *\n * On Android, available on API 31+ (uses `isMock()`), falls back to `isFromMockProvider()` on older versions.\n * On iOS, detected via `CLLocationSourceInformation` on iOS 15+.\n */\n isMock?: boolean;\n\n /**\n * The location provider that generated this location (Android only).\n *\n * Common values: `\"gps\"`, `\"network\"`, `\"fused\"`.\n */\n provider?: string;\n };\n}\n\nexport interface PositionOptions {\n /**\n * High accuracy mode (such as GPS, if available)\n *\n * On Android 12+ devices it will be ignored if users didn't grant\n * ACCESS_FINE_LOCATION permissions (can be checked with location alias).\n *\n * @default false\n * @since 1.0.0\n */\n enableHighAccuracy?: boolean;\n\n /**\n * The maximum wait time in milliseconds for location updates.\n *\n * @default 10000\n * @since 1.0.0\n */\n timeout?: number;\n\n /**\n * The maximum age in milliseconds of a possible cached position that is acceptable to return\n *\n * @default 0\n * @since 1.0.0\n */\n maximumAge?: number;\n\n /**\n * The minimum update interval for `watchPosition`. Not to be confused with `interval`.\n *\n * If location updates are available faster than this interval then an update\n * will only occur if the minimum update interval has expired since the last location update.\n *\n * This parameter is only available for Android. It has no effect on iOS or Web platforms.\n *\n * @default 5000\n * @since 6.1.0\n */\n minimumUpdateInterval?: number;\n\n /**\n * Desired interval in milliseconds to receive location updates in `watchPosition`.\n *\n * For very low values of `interval` (a couple seconds or less),\n * the platform may not guarantee timely location updates - they may take longer than specified.\n * The platform may also be able to provide location updates faster than `interval`.\n * You may use `minimumUpdateInterval` to control that behavior.\n *\n * For backwards compatibility with version 7.1.x, if no value is passed,\n * the default value of this parameter is that of `timeout`.\n *\n * This parameter is only available for Android. It has no effect on iOS or Web platforms.\n *\n * @default `timeout`\n * @since 8.0.0\n */\n interval?: number;\n\n /**\n * Whether to fall back to the Android framework's `LocationManager` in case Google Play Service's location settings checks fail.\n * This can happen for multiple reasons - e.g. device has no Play Services or device has no network connection (Airplane Mode)\n * If set to `false`, failures are propagated to the caller.\n * Note that `LocationManager` may not be as effective as Google Play Services implementation.\n * If the device's in airplane mode, only the GPS provider is used, which may take longer to return a location, depending on GPS signal.\n * This means that to receive location in such circumstances, you may need to provide a higher timeout.\n *\n * This parameter is only available for Android. It has no effect on iOS or Web platforms.\n *\n * @default true\n * @since 8.0.0\n */\n enableLocationFallback?: boolean;\n}\n\nexport type WatchPositionCallback = (position: Position | null, err?: any) => void;\n\n/**\n * @deprecated Use `PositionOptions`.\n * @since 1.0.0\n */\nexport type GeolocationOptions = PositionOptions;\n\n/**\n * @deprecated Use `WatchPositionCallback`.\n * @since 1.0.0\n */\nexport type GeolocationWatchCallback = WatchPositionCallback;\n\n/**\n * @deprecated Use `Position`.\n * @since 1.0.0\n */\nexport type GeolocationPosition = Position;\n"]}
|
package/dist/esm/web.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { WebPlugin } from '@capacitor/core';
|
|
2
2
|
import type { CallbackID, GeolocationPlugin, PermissionStatus, Position, PositionOptions, WatchPositionCallback } from './definitions';
|
|
3
3
|
export declare class GeolocationWeb extends WebPlugin implements GeolocationPlugin {
|
|
4
|
+
private latestOrientation;
|
|
5
|
+
constructor();
|
|
6
|
+
private updateOrientation;
|
|
7
|
+
private augmentPosition;
|
|
4
8
|
getCurrentPosition(options?: PositionOptions): Promise<Position>;
|
|
5
9
|
watchPosition(options: PositionOptions, callback: WatchPositionCallback): Promise<CallbackID>;
|
|
6
10
|
clearWatch(options: {
|