@tokamakdev/plugin-location 0.1.0-beta.107 → 0.1.0-beta.112
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/README.md +30 -13
- package/android/Bindings.kt +997 -0
- package/android/LocationPlugin.kt +3 -124
- package/android/bindings.ts +4150 -0
- package/apple/Bindings.swift +1057 -0
- package/apple/LocationPlugin.swift +28 -167
- package/apple/LocationRegions.swift +3 -1
- package/apple/bindings.ts +1314 -0
- package/events/index.ts +5 -5
- package/package.json +5 -3
- package/src/android.ts +138 -0
- package/src/apple.ts +117 -0
- package/src/errors.ts +34 -0
- package/src/index.ts +89 -64
- package/src/types.ts +12 -0
- package/tokamak-plugin.json +69 -2
- package/web/client.ts +11 -0
- package/src/checks.ts +0 -65
- package/src/options.ts +0 -54
|
@@ -7,14 +7,8 @@ import android.content.Context
|
|
|
7
7
|
import android.content.Intent
|
|
8
8
|
import android.content.pm.PackageManager
|
|
9
9
|
import android.location.Location
|
|
10
|
-
import android.location.LocationListener
|
|
11
10
|
import android.location.LocationManager
|
|
12
11
|
import android.net.Uri
|
|
13
|
-
import android.os.Bundle
|
|
14
|
-
import android.os.CancellationSignal
|
|
15
|
-
import android.os.Handler
|
|
16
|
-
import android.os.Looper
|
|
17
|
-
import android.os.SystemClock
|
|
18
12
|
import android.provider.Settings
|
|
19
13
|
import com.tokamak.runtime.TokamakHost
|
|
20
14
|
import com.tokamak.runtime.TokamakPlugin
|
|
@@ -34,7 +28,6 @@ class TokamakLocationPlugin(
|
|
|
34
28
|
private val context = host.context
|
|
35
29
|
private val manager =
|
|
36
30
|
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
|
37
|
-
private val handler = Handler(Looper.getMainLooper())
|
|
38
31
|
|
|
39
32
|
/** The permissions the app's manifest requests. */
|
|
40
33
|
private val declared: Set<String> by lazy {
|
|
@@ -71,13 +64,9 @@ class TokamakLocationPlugin(
|
|
|
71
64
|
if (host.isInForeground) startService()
|
|
72
65
|
}
|
|
73
66
|
|
|
74
|
-
override fun call(method: String, arguments: Any?, reply: TokamakPluginReply) {
|
|
67
|
+
override fun call(method: String, arguments: Any?, reply: TokamakPluginReply): (() -> Unit)? {
|
|
75
68
|
when (method) {
|
|
76
69
|
"setup" -> setup(requireObject(arguments), reply)
|
|
77
|
-
"getCurrentPosition" ->
|
|
78
|
-
withPermission { granted ->
|
|
79
|
-
if (granted) currentPosition(arguments as? JSONObject, reply) else reply(Result.failure(permissionDenied()))
|
|
80
|
-
}
|
|
81
70
|
"permission" -> reply(Result.success(permission()))
|
|
82
71
|
"requestPermission" -> requestPermission(requireObject(arguments), reply)
|
|
83
72
|
"servicesEnabled" -> reply(Result.success(manager.isLocationEnabled))
|
|
@@ -96,38 +85,19 @@ class TokamakLocationPlugin(
|
|
|
96
85
|
}
|
|
97
86
|
else -> super.call(method, arguments, reply)
|
|
98
87
|
}
|
|
88
|
+
return null
|
|
99
89
|
}
|
|
100
90
|
|
|
101
91
|
override fun subscribe(
|
|
102
92
|
method: String,
|
|
103
93
|
arguments: Any?,
|
|
104
94
|
reply: TokamakPluginReply,
|
|
105
|
-
): () -> Unit
|
|
106
|
-
events.listen(method, reply)?.let { return it }
|
|
107
|
-
if (method != "watchPosition") return super.subscribe(method, arguments, reply)
|
|
108
|
-
var cancelled = false
|
|
109
|
-
var stop = {}
|
|
110
|
-
withPermission { granted ->
|
|
111
|
-
if (!granted) {
|
|
112
|
-
reply(Result.failure(permissionDenied()))
|
|
113
|
-
} else if (!cancelled) {
|
|
114
|
-
stop = watchPosition(reply)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return {
|
|
118
|
-
cancelled = true
|
|
119
|
-
stop()
|
|
120
|
-
}
|
|
121
|
-
}
|
|
95
|
+
): () -> Unit = events.listen(method, reply) ?: super.subscribe(method, arguments, reply)
|
|
122
96
|
|
|
123
97
|
/**
|
|
124
98
|
* Passes [action] whether the app may read fine or coarse location, asking when it may not.
|
|
125
99
|
* Throws `NeedsUIError` when asking would show UI while the app is in the background.
|
|
126
100
|
*/
|
|
127
|
-
private fun withPermission(action: (Boolean) -> Unit) {
|
|
128
|
-
if (hasForegroundPermission()) action(true) else askForeground(foreground, action)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
101
|
/**
|
|
132
102
|
* Asks for the foreground [permissions], then passes [action] whether the app has one, first
|
|
133
103
|
* starting what waited for permission.
|
|
@@ -291,82 +261,6 @@ class TokamakLocationPlugin(
|
|
|
291
261
|
reply(Result.success(null))
|
|
292
262
|
}
|
|
293
263
|
|
|
294
|
-
/**
|
|
295
|
-
* Replies with a fix no older than the `maximumAge` option, taking a new one when there is
|
|
296
|
-
* none, or fails after the `timeout` option.
|
|
297
|
-
*/
|
|
298
|
-
private fun currentPosition(options: JSONObject?, reply: TokamakPluginReply) {
|
|
299
|
-
val provider = availableProvider(reply) ?: return
|
|
300
|
-
val maximumAge = options?.optLong("maximumAge", 0) ?: 0
|
|
301
|
-
val recent = runCatching { manager.getLastKnownLocation(provider) }.getOrNull()
|
|
302
|
-
if (recent != null && ageMillis(recent) <= maximumAge) {
|
|
303
|
-
reply(Result.success(position(recent)))
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
val cancellation = CancellationSignal()
|
|
307
|
-
val timeout = Runnable {
|
|
308
|
-
cancellation.cancel()
|
|
309
|
-
reply(Result.failure(TokamakPluginError("TimeoutError", "Location was not found in time")))
|
|
310
|
-
}
|
|
311
|
-
handler.postDelayed(timeout, options?.optLong("timeout", UNLIMITED) ?: UNLIMITED)
|
|
312
|
-
runCatching {
|
|
313
|
-
manager.getCurrentLocation(
|
|
314
|
-
provider,
|
|
315
|
-
cancellation,
|
|
316
|
-
context.mainExecutor,
|
|
317
|
-
) { location ->
|
|
318
|
-
handler.removeCallbacks(timeout)
|
|
319
|
-
reply(if (location == null) missingFix() else Result.success(position(location)))
|
|
320
|
-
}
|
|
321
|
-
}.onFailure {
|
|
322
|
-
handler.removeCallbacks(timeout)
|
|
323
|
-
reply(locationFailure(it))
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
private fun ageMillis(location: Location): Long =
|
|
328
|
-
(SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos) / 1_000_000
|
|
329
|
-
|
|
330
|
-
private fun watchPosition(reply: TokamakPluginReply): () -> Unit {
|
|
331
|
-
val provider = availableProvider(reply) ?: return {}
|
|
332
|
-
val listener = object : LocationListener {
|
|
333
|
-
override fun onLocationChanged(location: Location) {
|
|
334
|
-
reply(Result.success(position(location)))
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
override fun onProviderDisabled(provider: String) {
|
|
338
|
-
reply(unavailable("Location provider is unavailable"))
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) = Unit
|
|
342
|
-
}
|
|
343
|
-
runCatching {
|
|
344
|
-
manager.requestLocationUpdates(provider, 1000, 0F, listener, Looper.getMainLooper())
|
|
345
|
-
}.onFailure {
|
|
346
|
-
reply(locationFailure(it))
|
|
347
|
-
return {}
|
|
348
|
-
}
|
|
349
|
-
return { manager.removeUpdates(listener) }
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/** The provider to use, or null once [reply] has the reason there is none. */
|
|
353
|
-
private fun availableProvider(reply: TokamakPluginReply): String? {
|
|
354
|
-
val provider = runCatching(::provider).getOrElse {
|
|
355
|
-
reply(locationFailure(it))
|
|
356
|
-
return null
|
|
357
|
-
}
|
|
358
|
-
if (provider == null) reply(unavailable("No location provider is available"))
|
|
359
|
-
return provider
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
private fun provider(): String? =
|
|
363
|
-
listOf(
|
|
364
|
-
LocationManager.GPS_PROVIDER,
|
|
365
|
-
LocationManager.NETWORK_PROVIDER,
|
|
366
|
-
LocationManager.FUSED_PROVIDER,
|
|
367
|
-
).firstOrNull(manager::isProviderEnabled)
|
|
368
|
-
?: manager.getProviders(true).firstOrNull()
|
|
369
|
-
|
|
370
264
|
private fun position(location: Location): JSONObject =
|
|
371
265
|
JSONObject()
|
|
372
266
|
.put(
|
|
@@ -389,19 +283,6 @@ class TokamakLocationPlugin(
|
|
|
389
283
|
|
|
390
284
|
private fun permissionDenied() = TokamakPluginError("NotAllowedError", "Location permission was denied")
|
|
391
285
|
|
|
392
|
-
/** Why no fix came: Android gives none in the background without background location permission. */
|
|
393
|
-
private fun missingFix(): Result<Any?> =
|
|
394
|
-
if (host.isInForeground || granted(BACKGROUND)) {
|
|
395
|
-
unavailable("Location is unavailable")
|
|
396
|
-
} else {
|
|
397
|
-
Result.failure(TokamakPluginError("NotAllowedError", "Location permission does not cover the background"))
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
private fun unavailable(message: String): Result<Any?> =
|
|
401
|
-
Result.failure(TokamakPluginError("NotReadableError", message))
|
|
402
|
-
|
|
403
|
-
private fun locationFailure(error: Throwable): Result<Any?> = Result.failure(locationError(error))
|
|
404
|
-
|
|
405
286
|
private fun locationError(error: Throwable): TokamakPluginError =
|
|
406
287
|
when (error) {
|
|
407
288
|
is SecurityException -> permissionDenied()
|
|
@@ -411,8 +292,6 @@ class TokamakLocationPlugin(
|
|
|
411
292
|
}
|
|
412
293
|
|
|
413
294
|
private companion object {
|
|
414
|
-
/** The `timeout` that never expires, as the page sends it. */
|
|
415
|
-
const val UNLIMITED = 0xffffffffL
|
|
416
295
|
const val FINE = Manifest.permission.ACCESS_FINE_LOCATION
|
|
417
296
|
const val COARSE = Manifest.permission.ACCESS_COARSE_LOCATION
|
|
418
297
|
const val BACKGROUND = Manifest.permission.ACCESS_BACKGROUND_LOCATION
|