@tokamakdev/plugin-location 0.1.0-beta.128 → 0.1.0-beta.130
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 +2 -2
- package/android/Exports.generated.kt +264 -0
- package/android/Exports.kt +30 -0
- package/android/LocationEvents.kt +19 -33
- package/android/LocationPlugin.kt +102 -160
- package/android/LocationPositions.kt +30 -15
- package/android/LocationRegions.kt +8 -16
- package/android/LocationService.kt +10 -11
- package/android/LocationTypes.kt +132 -0
- package/android/LocationUpdates.kt +16 -17
- package/apple/Exports.generated.swift +134 -0
- package/apple/Exports.swift +48 -0
- package/apple/LocationEvents.swift +36 -68
- package/apple/LocationPlugin.swift +172 -186
- package/apple/LocationPositions.swift +34 -24
- package/apple/LocationRegions.swift +29 -34
- package/apple/LocationTypes.swift +158 -0
- package/apple/LocationUpdates.swift +20 -28
- package/package.json +5 -4
- package/src/android.generated.ts +170 -0
- package/src/apple.generated.ts +183 -0
- package/src/events.ts +31 -0
- package/src/index.ts +114 -139
- package/src/types.ts +9 -20
- package/tokamak-plugin.json +3 -26
- package/web/index.ts +11 -10
- package/events/index.ts +0 -18
package/README.md
CHANGED
|
@@ -52,7 +52,7 @@ default. The timeout starts once the user allows location. Android takes
|
|
|
52
52
|
positions from its fused provider at high accuracy, which combines GPS with
|
|
53
53
|
Wi-Fi and cell signals.
|
|
54
54
|
|
|
55
|
-
`watchPosition()`
|
|
55
|
+
`watchPosition()` resolves to a byte stream of JSON positions. `decodeValues<Position>`
|
|
56
56
|
decodes them. Cancelling the stream ends the native watch; errors terminate it.
|
|
57
57
|
|
|
58
58
|
Both ask for in-use permission when the user has not answered. Off screen
|
|
@@ -134,7 +134,7 @@ event:
|
|
|
134
134
|
|---|---|
|
|
135
135
|
| `onLocationUpdate` | `{ positions, stationary? }` from updates and significant changes. |
|
|
136
136
|
| `onRegionTransition` | `{ id, transition, timestamp }`, where `transition` is `"enter"` or `"exit"`. |
|
|
137
|
-
| `onVisit` | Apple only: `{ latitude, longitude, accuracy, arrival, departure }`, times in milliseconds
|
|
137
|
+
| `onVisit` | Apple only: `{ latitude, longitude, accuracy, arrival, departure }`, times in milliseconds, absent when unknown. |
|
|
138
138
|
| `onLocationError` | `{ name, message, id? }` when a background service fails, with the region's `id` when it concerns one. |
|
|
139
139
|
|
|
140
140
|
In Doze, the idle state of an unplugged Android device left still with its
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// Generated by `tok plugin generate android`. Do not edit.
|
|
2
|
+
package com.tokamak.plugins.location
|
|
3
|
+
|
|
4
|
+
import com.tokamak.runtime.TokamakDispatch
|
|
5
|
+
import com.tokamak.runtime.TokamakHost
|
|
6
|
+
import com.tokamak.runtime.TokamakPluginBytesReply
|
|
7
|
+
import com.tokamak.runtime.TokamakPluginError
|
|
8
|
+
import com.tokamak.runtime.TokamakValue
|
|
9
|
+
import kotlin.reflect.KClass
|
|
10
|
+
import org.json.JSONArray
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
|
|
13
|
+
/** Routes the Worker's requests to the methods [TokamakLocationPlugin] exports, and encodes the events it emits. */
|
|
14
|
+
class TokamakLocationPluginDispatch(host: TokamakHost) : TokamakDispatch<TokamakLocationPlugin>(TokamakLocationPlugin(host), host) {
|
|
15
|
+
override fun callBytes(method: String, payload: ByteArray, reply: TokamakPluginBytesReply): (() -> Unit)? {
|
|
16
|
+
val arguments by lazy(LazyThreadSafetyMode.NONE) { TokamakValue.arguments(payload) }
|
|
17
|
+
return when (method) {
|
|
18
|
+
"setup" -> respond(reply) { plugin.setup(config = arguments["config"].asSetupConfig()) }
|
|
19
|
+
"recentPosition" -> respond(reply) {
|
|
20
|
+
val result = plugin.recentPosition(
|
|
21
|
+
maximumAge = arguments["maximumAge"].double(),
|
|
22
|
+
)
|
|
23
|
+
result?.let { json(it) }
|
|
24
|
+
}
|
|
25
|
+
"currentPosition" -> launch(method, reply) { plugin.currentPosition()?.let { json(it) } }
|
|
26
|
+
"permission" -> respond(reply) { json(plugin.permission()) }
|
|
27
|
+
"requestPermission" -> launch(method, reply) {
|
|
28
|
+
val result = plugin.requestPermission(
|
|
29
|
+
request = arguments["request"].nullable { it.asPermissionRequest() },
|
|
30
|
+
)
|
|
31
|
+
json(result)
|
|
32
|
+
}
|
|
33
|
+
"servicesEnabled" -> respond(reply) { plugin.servicesEnabled() }
|
|
34
|
+
"openSettings" -> respond(reply) { plugin.openSettings() }
|
|
35
|
+
"startUpdates" -> respond(reply) {
|
|
36
|
+
plugin.startUpdates(
|
|
37
|
+
options = arguments["options"].nullable { it.asUpdateOptions() },
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
"stopUpdates" -> respond(reply) { plugin.stopUpdates() }
|
|
41
|
+
"getUpdates" -> respond(reply) { plugin.getUpdates()?.let { json(it) } }
|
|
42
|
+
"addRegion" -> respond(reply) { plugin.addRegion(region = arguments["region"].asRegion()) }
|
|
43
|
+
"getRegions" -> respond(reply) { JSONArray(plugin.getRegions().map { json(it) }) }
|
|
44
|
+
"removeRegion" -> respond(reply) { plugin.removeRegion(id = arguments["id"].string()) }
|
|
45
|
+
else -> throw TokamakPluginError.notSupported("$id.$method is not supported")
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override fun subscribeBytes(method: String, payload: ByteArray, reply: TokamakPluginBytesReply): () -> Unit {
|
|
50
|
+
return when (method) {
|
|
51
|
+
"watchPosition" -> collect(method, reply, plugin.watchPosition()) { json(it) }
|
|
52
|
+
"onLocationUpdate" -> collect(method, reply, plugin.onLocationUpdate()) { json(it) }
|
|
53
|
+
"onRegionTransition" -> collect(method, reply, plugin.onRegionTransition()) { json(it) }
|
|
54
|
+
"onLocationError" -> collect(method, reply, plugin.onLocationError()) { json(it) }
|
|
55
|
+
else -> throw TokamakPluginError.notSupported("$id.$method is not supported")
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
override fun eventPayload(name: String, event: Any): ByteArray =
|
|
60
|
+
when (name) {
|
|
61
|
+
"update" -> encodeEvent(name, event, LocationUpdate::class) { json(it) }
|
|
62
|
+
"region" -> encodeEvent(name, event, RegionTransition::class) { json(it) }
|
|
63
|
+
"error" -> encodeEvent(name, event, LocationErrorEvent::class) { json(it) }
|
|
64
|
+
else -> super.eventPayload(name, event)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
override fun encodeValue(value: Any): Any =
|
|
68
|
+
when (value) {
|
|
69
|
+
is Coordinates -> json(value)
|
|
70
|
+
is LocationErrorEvent -> json(value)
|
|
71
|
+
is LocationPermission -> json(value)
|
|
72
|
+
is LocationUpdate -> json(value)
|
|
73
|
+
is MonitoredRegion -> json(value)
|
|
74
|
+
is PermissionRequest -> json(value)
|
|
75
|
+
is Position -> json(value)
|
|
76
|
+
is Region -> json(value)
|
|
77
|
+
is RegionTransition -> json(value)
|
|
78
|
+
is ServiceNotification -> json(value)
|
|
79
|
+
is SetupConfig -> json(value)
|
|
80
|
+
is UpdateOptions -> json(value)
|
|
81
|
+
else -> super.encodeValue(value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
override fun decodeValue(json: TokamakValue, type: KClass<*>): Any =
|
|
85
|
+
when (type) {
|
|
86
|
+
Coordinates::class -> json.asCoordinates()
|
|
87
|
+
LocationErrorEvent::class -> json.asLocationErrorEvent()
|
|
88
|
+
LocationPermission::class -> json.asLocationPermission()
|
|
89
|
+
LocationUpdate::class -> json.asLocationUpdate()
|
|
90
|
+
MonitoredRegion::class -> json.asMonitoredRegion()
|
|
91
|
+
PermissionRequest::class -> json.asPermissionRequest()
|
|
92
|
+
Position::class -> json.asPosition()
|
|
93
|
+
Region::class -> json.asRegion()
|
|
94
|
+
RegionTransition::class -> json.asRegionTransition()
|
|
95
|
+
ServiceNotification::class -> json.asServiceNotification()
|
|
96
|
+
SetupConfig::class -> json.asSetupConfig()
|
|
97
|
+
UpdateOptions::class -> json.asUpdateOptions()
|
|
98
|
+
else -> super.decodeValue(json, type)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private fun TokamakValue.asCoordinates(): Coordinates =
|
|
102
|
+
Coordinates(
|
|
103
|
+
latitude = this["latitude"].double(),
|
|
104
|
+
longitude = this["longitude"].double(),
|
|
105
|
+
accuracy = this["accuracy"].double(),
|
|
106
|
+
altitude = this["altitude"].nullable { it.double() },
|
|
107
|
+
altitudeAccuracy = this["altitudeAccuracy"].nullable { it.double() },
|
|
108
|
+
heading = this["heading"].nullable { it.double() },
|
|
109
|
+
speed = this["speed"].nullable { it.double() },
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
private fun json(value: Coordinates): JSONObject =
|
|
113
|
+
JSONObject()
|
|
114
|
+
.put("latitude", value.latitude)
|
|
115
|
+
.put("longitude", value.longitude)
|
|
116
|
+
.put("accuracy", value.accuracy)
|
|
117
|
+
.putOpt("altitude", value.altitude)
|
|
118
|
+
.putOpt("altitudeAccuracy", value.altitudeAccuracy)
|
|
119
|
+
.putOpt("heading", value.heading)
|
|
120
|
+
.putOpt("speed", value.speed)
|
|
121
|
+
|
|
122
|
+
private fun TokamakValue.asLocationErrorEvent(): LocationErrorEvent =
|
|
123
|
+
LocationErrorEvent(
|
|
124
|
+
name = this["name"].string(),
|
|
125
|
+
message = this["message"].string(),
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
private fun json(value: LocationErrorEvent): JSONObject =
|
|
129
|
+
JSONObject()
|
|
130
|
+
.put("name", value.name)
|
|
131
|
+
.put("message", value.message)
|
|
132
|
+
|
|
133
|
+
private fun TokamakValue.asLocationPermission(): LocationPermission =
|
|
134
|
+
LocationPermission(
|
|
135
|
+
inUse = this["inUse"].enum(Permission.entries),
|
|
136
|
+
background = this["background"].enum(Permission.entries),
|
|
137
|
+
precision = this["precision"].enum(Precision.entries),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
private fun json(value: LocationPermission): JSONObject =
|
|
141
|
+
JSONObject()
|
|
142
|
+
.put("inUse", value.inUse.name)
|
|
143
|
+
.put("background", value.background.name)
|
|
144
|
+
.put("precision", value.precision.name)
|
|
145
|
+
|
|
146
|
+
private fun TokamakValue.asLocationUpdate(): LocationUpdate =
|
|
147
|
+
LocationUpdate(
|
|
148
|
+
positions = this["positions"].list { it.asPosition() },
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
private fun json(value: LocationUpdate): JSONObject =
|
|
152
|
+
JSONObject()
|
|
153
|
+
.put("positions", JSONArray(value.positions.map { json(it) }))
|
|
154
|
+
|
|
155
|
+
private fun TokamakValue.asMonitoredRegion(): MonitoredRegion =
|
|
156
|
+
MonitoredRegion(
|
|
157
|
+
id = this["id"].string(),
|
|
158
|
+
latitude = this["latitude"].double(),
|
|
159
|
+
longitude = this["longitude"].double(),
|
|
160
|
+
radius = this["radius"].double(),
|
|
161
|
+
onEntry = this["onEntry"].boolean(),
|
|
162
|
+
onExit = this["onExit"].boolean(),
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
private fun json(value: MonitoredRegion): JSONObject =
|
|
166
|
+
JSONObject()
|
|
167
|
+
.put("id", value.id)
|
|
168
|
+
.put("latitude", value.latitude)
|
|
169
|
+
.put("longitude", value.longitude)
|
|
170
|
+
.put("radius", value.radius)
|
|
171
|
+
.put("onEntry", value.onEntry)
|
|
172
|
+
.put("onExit", value.onExit)
|
|
173
|
+
|
|
174
|
+
private fun TokamakValue.asPermissionRequest(): PermissionRequest =
|
|
175
|
+
PermissionRequest(
|
|
176
|
+
background = this["background"].nullable { it.boolean() },
|
|
177
|
+
precision = this["precision"].nullable { it.enum(Precision.entries) },
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
private fun json(value: PermissionRequest): JSONObject =
|
|
181
|
+
JSONObject()
|
|
182
|
+
.putOpt("background", value.background)
|
|
183
|
+
.putOpt("precision", value.precision?.name)
|
|
184
|
+
|
|
185
|
+
private fun TokamakValue.asPosition(): Position =
|
|
186
|
+
Position(
|
|
187
|
+
coords = this["coords"].asCoordinates(),
|
|
188
|
+
timestamp = this["timestamp"].long(),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
private fun json(value: Position): JSONObject =
|
|
192
|
+
JSONObject()
|
|
193
|
+
.put("coords", json(value.coords))
|
|
194
|
+
.put("timestamp", value.timestamp)
|
|
195
|
+
|
|
196
|
+
private fun TokamakValue.asRegion(): Region =
|
|
197
|
+
Region(
|
|
198
|
+
id = this["id"].string(),
|
|
199
|
+
latitude = this["latitude"].double(),
|
|
200
|
+
longitude = this["longitude"].double(),
|
|
201
|
+
radius = this["radius"].double(),
|
|
202
|
+
onEntry = this["onEntry"].nullable { it.boolean() },
|
|
203
|
+
onExit = this["onExit"].nullable { it.boolean() },
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
private fun json(value: Region): JSONObject =
|
|
207
|
+
JSONObject()
|
|
208
|
+
.put("id", value.id)
|
|
209
|
+
.put("latitude", value.latitude)
|
|
210
|
+
.put("longitude", value.longitude)
|
|
211
|
+
.put("radius", value.radius)
|
|
212
|
+
.putOpt("onEntry", value.onEntry)
|
|
213
|
+
.putOpt("onExit", value.onExit)
|
|
214
|
+
|
|
215
|
+
private fun TokamakValue.asRegionTransition(): RegionTransition =
|
|
216
|
+
RegionTransition(
|
|
217
|
+
id = this["id"].string(),
|
|
218
|
+
transition = this["transition"].enum(Transition.entries),
|
|
219
|
+
timestamp = this["timestamp"].long(),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
private fun json(value: RegionTransition): JSONObject =
|
|
223
|
+
JSONObject()
|
|
224
|
+
.put("id", value.id)
|
|
225
|
+
.put("transition", value.transition.name)
|
|
226
|
+
.put("timestamp", value.timestamp)
|
|
227
|
+
|
|
228
|
+
private fun TokamakValue.asServiceNotification(): ServiceNotification =
|
|
229
|
+
ServiceNotification(
|
|
230
|
+
title = this["title"].string(),
|
|
231
|
+
body = this["body"].string(),
|
|
232
|
+
channel = this["channel"].string(),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
private fun json(value: ServiceNotification): JSONObject =
|
|
236
|
+
JSONObject()
|
|
237
|
+
.put("title", value.title)
|
|
238
|
+
.put("body", value.body)
|
|
239
|
+
.put("channel", value.channel)
|
|
240
|
+
|
|
241
|
+
private fun TokamakValue.asSetupConfig(): SetupConfig =
|
|
242
|
+
SetupConfig(
|
|
243
|
+
significantChanges = this["significantChanges"].nullable { it.boolean() },
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
private fun json(value: SetupConfig): JSONObject =
|
|
247
|
+
JSONObject()
|
|
248
|
+
.putOpt("significantChanges", value.significantChanges)
|
|
249
|
+
|
|
250
|
+
private fun TokamakValue.asUpdateOptions(): UpdateOptions =
|
|
251
|
+
UpdateOptions(
|
|
252
|
+
accuracy = this["accuracy"].nullable { it.enum(Accuracy.entries) },
|
|
253
|
+
distance = this["distance"].nullable { it.double() },
|
|
254
|
+
interval = this["interval"].nullable { it.long() },
|
|
255
|
+
foregroundService = this["foregroundService"].nullable { it.asServiceNotification() },
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
private fun json(value: UpdateOptions): JSONObject =
|
|
259
|
+
JSONObject()
|
|
260
|
+
.putOpt("accuracy", value.accuracy?.name)
|
|
261
|
+
.putOpt("distance", value.distance)
|
|
262
|
+
.putOpt("interval", value.interval)
|
|
263
|
+
.putOpt("foregroundService", value.foregroundService?.let { json(it) })
|
|
264
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.tokamak.plugins.location
|
|
2
|
+
|
|
3
|
+
import com.tokamak.runtime.TokamakExporter
|
|
4
|
+
import com.tokamak.runtime.TokamakExports
|
|
5
|
+
import com.tokamak.runtime.TokamakOverflow
|
|
6
|
+
|
|
7
|
+
object LocationExports : TokamakExports<TokamakLocationPlugin> {
|
|
8
|
+
override fun exports(e: TokamakExporter<TokamakLocationPlugin>) {
|
|
9
|
+
e.call(TokamakLocationPlugin::setup, doc = "Applies long-term location awareness, replacing the previous configuration.")
|
|
10
|
+
e.call(TokamakLocationPlugin::recentPosition, doc = "The provider's last known position, when it is no older than `maximumAge`.")
|
|
11
|
+
e.call(TokamakLocationPlugin::currentPosition, doc = "A new fix, or null when Android finds none. Cancelling the call stops locating.")
|
|
12
|
+
e.stream(TokamakLocationPlugin::watchPosition, overflow = TokamakOverflow.DROP_OLDEST, doc = "Positions as the device moves.")
|
|
13
|
+
e.call(TokamakLocationPlugin::permission, doc = "The app's in-use and background permission, and the precision it gets.")
|
|
14
|
+
e.call(TokamakLocationPlugin::requestPermission, doc = "Asks for the permission `request` names where the user has not answered.")
|
|
15
|
+
e.call(TokamakLocationPlugin::servicesEnabled, doc = "Whether location services are on for the device.")
|
|
16
|
+
e.call(TokamakLocationPlugin::openSettings, doc = "Opens the app's page in the system settings.")
|
|
17
|
+
e.call(TokamakLocationPlugin::startUpdates, doc = "Starts continuous updates, replacing any running, until `stopUpdates`.")
|
|
18
|
+
e.call(TokamakLocationPlugin::stopUpdates)
|
|
19
|
+
e.call(TokamakLocationPlugin::getUpdates, doc = "The options of the running updates, or null when none are running.")
|
|
20
|
+
e.call(TokamakLocationPlugin::addRegion, doc = "Monitors a circular region, replacing one with the same `id`.")
|
|
21
|
+
e.call(TokamakLocationPlugin::getRegions, doc = "The monitored regions.")
|
|
22
|
+
e.call(TokamakLocationPlugin::removeRegion)
|
|
23
|
+
e.stream(TokamakLocationPlugin::onLocationUpdate, overflow = TokamakOverflow.DROP_OLDEST, doc = "The updates the Worker's listeners receive.")
|
|
24
|
+
e.stream(TokamakLocationPlugin::onRegionTransition, doc = "The transitions the Worker's listeners receive.")
|
|
25
|
+
e.stream(TokamakLocationPlugin::onLocationError, doc = "The failures the Worker's listeners receive.")
|
|
26
|
+
e.event("update", LocationUpdate::class, doc = "Receives continuous updates and significant changes, including those that start the app.")
|
|
27
|
+
e.event("region", RegionTransition::class, doc = "Receives each region entry or exit, including one that starts the app.")
|
|
28
|
+
e.event("error", LocationErrorEvent::class, doc = "Receives failures of background services, such as a region Android cannot monitor.")
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -2,83 +2,69 @@ package com.tokamak.plugins.location
|
|
|
2
2
|
|
|
3
3
|
import android.util.Log
|
|
4
4
|
import com.tokamak.runtime.TokamakHost
|
|
5
|
+
import com.tokamak.runtime.TokamakListeners
|
|
5
6
|
import com.tokamak.runtime.TokamakPluginError
|
|
6
|
-
import com.tokamak.runtime.TokamakPluginReply
|
|
7
|
-
import org.json.JSONArray
|
|
8
|
-
import org.json.JSONObject
|
|
9
7
|
|
|
10
8
|
/** How long the Worker's listeners have for an event, within the time Android gives a broadcast. */
|
|
11
9
|
private const val EVENT_DEADLINE_MILLIS = 8_000L
|
|
12
10
|
|
|
13
|
-
/** The listener methods, which receive the Worker's events. */
|
|
14
|
-
private val LISTENERS = setOf("onLocationUpdate", "onRegionTransition", "onVisit", "onLocationError")
|
|
15
|
-
|
|
16
11
|
/** Delivers the plugin's events to the Worker's listeners and the page's. Use it on the main thread. */
|
|
17
12
|
internal class LocationEvents(private val host: TokamakHost) {
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
val updates = TokamakListeners<LocationUpdate>()
|
|
14
|
+
val transitions = TokamakListeners<RegionTransition>()
|
|
15
|
+
val errors = TokamakListeners<LocationErrorEvent>()
|
|
20
16
|
|
|
21
17
|
/** Positions that arrived while the Worker handled the previous update, and the broadcasts to finish once it has them. */
|
|
22
|
-
private val positions = mutableListOf<
|
|
18
|
+
private val positions = mutableListOf<Position>()
|
|
23
19
|
private val finishes = mutableListOf<() -> Unit>()
|
|
24
20
|
private var updating = false
|
|
25
21
|
|
|
26
22
|
/** The time of the newest position delivered, as requests that run together receive the same positions. */
|
|
27
23
|
private var latest = Long.MIN_VALUE
|
|
28
24
|
|
|
29
|
-
/** Adds a page's or Worker's listener of [method], returning its removal, or null for another method. */
|
|
30
|
-
fun listen(method: String, reply: TokamakPluginReply): (() -> Unit)? {
|
|
31
|
-
if (method !in LISTENERS) return null
|
|
32
|
-
val key = nextListener++
|
|
33
|
-
listeners.getOrPut(method, ::mutableMapOf)[key] = reply
|
|
34
|
-
return { listeners[method]?.remove(key) }
|
|
35
|
-
}
|
|
36
|
-
|
|
37
25
|
/**
|
|
38
26
|
* Delivers the [received] positions newer than those delivered, then calls [finish]. Positions
|
|
39
27
|
* that arrive while the Worker handles an update reach it together as the next.
|
|
40
28
|
*/
|
|
41
|
-
fun update(received: List<
|
|
42
|
-
val newer = received.filter { it.
|
|
29
|
+
fun update(received: List<Position>, finish: () -> Unit) {
|
|
30
|
+
val newer = received.filter { it.timestamp > latest }
|
|
43
31
|
if (newer.isEmpty()) return finish()
|
|
44
|
-
latest = newer.maxOf { it.
|
|
32
|
+
latest = newer.maxOf { it.timestamp }
|
|
45
33
|
positions += newer
|
|
46
34
|
finishes += finish
|
|
47
35
|
if (!updating) sendUpdate()
|
|
48
36
|
}
|
|
49
37
|
|
|
50
38
|
fun transition(id: String, entered: Boolean, finish: () -> Unit) {
|
|
51
|
-
val
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
.put("transition", if (entered) "enter" else "exit")
|
|
55
|
-
.put("timestamp", System.currentTimeMillis())
|
|
56
|
-
send("onRegionTransition", "region", event, finish)
|
|
39
|
+
val transition = RegionTransition(id, if (entered) Transition.enter else Transition.exit, System.currentTimeMillis())
|
|
40
|
+
transitions.send(transition)
|
|
41
|
+
send("region", transition, finish)
|
|
57
42
|
}
|
|
58
43
|
|
|
59
44
|
/** Reports a background service's failure, then calls [finish]. */
|
|
60
45
|
fun fail(error: TokamakPluginError, finish: () -> Unit = {}) {
|
|
61
|
-
val event =
|
|
62
|
-
send(
|
|
46
|
+
val event = LocationErrorEvent(error.errorName, error.message.orEmpty())
|
|
47
|
+
errors.send(event)
|
|
48
|
+
send("error", event, finish)
|
|
63
49
|
}
|
|
64
50
|
|
|
65
51
|
/** Sends the waiting positions as one update, then the positions that arrive meanwhile as the next. */
|
|
66
52
|
private fun sendUpdate() {
|
|
67
53
|
updating = positions.isNotEmpty()
|
|
68
54
|
if (!updating) return
|
|
69
|
-
val
|
|
55
|
+
val update = LocationUpdate(positions.toList())
|
|
70
56
|
val delivered = finishes.toList()
|
|
71
57
|
positions.clear()
|
|
72
58
|
finishes.clear()
|
|
73
|
-
send(
|
|
59
|
+
updates.send(update)
|
|
60
|
+
send("update", update) {
|
|
74
61
|
delivered.forEach { it() }
|
|
75
62
|
sendUpdate()
|
|
76
63
|
}
|
|
77
64
|
}
|
|
78
65
|
|
|
79
|
-
/** Delivers [event] to the
|
|
80
|
-
private fun send(
|
|
81
|
-
listeners[method].orEmpty().values.toList().forEach { it(Result.success(event)) }
|
|
66
|
+
/** Delivers [event] to the Worker's [name] listeners, then calls [finish]. */
|
|
67
|
+
private fun send(name: String, event: Any, finish: () -> Unit) {
|
|
82
68
|
Thread {
|
|
83
69
|
runCatching { host.emit(name, event, EVENT_DEADLINE_MILLIS) }
|
|
84
70
|
.onFailure { Log.w("tokamak", "location $name failed: ${it.message}") }
|