@tokamakdev/plugin-location 0.1.0-beta.122 → 0.1.0-beta.125
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 +10 -1
- package/package.json +2 -2
- package/src/android.ts +20 -8
package/README.md
CHANGED
|
@@ -48,7 +48,9 @@ onRegionTransition(({ id, transition }) => console.log(id, transition));
|
|
|
48
48
|
options. It resolves with a recent position no older than `maximumAge`
|
|
49
49
|
milliseconds, 0 by default, or takes a new one, and rejects with
|
|
50
50
|
`TimeoutError` when none arrives within `timeout` milliseconds, unlimited by
|
|
51
|
-
default. The timeout starts once the user allows location.
|
|
51
|
+
default. The timeout starts once the user allows location. Android takes
|
|
52
|
+
positions from its fused provider at high accuracy, which combines GPS with
|
|
53
|
+
Wi-Fi and cell signals.
|
|
52
54
|
|
|
53
55
|
`watchPosition()` returns a byte stream of JSON positions. `decodeValues<Position>`
|
|
54
56
|
decodes them. Cancelling the stream ends the native watch; errors terminate it.
|
|
@@ -135,6 +137,13 @@ event:
|
|
|
135
137
|
| `onVisit` | Apple only: `{ latitude, longitude, accuracy, arrival, departure }`, times in milliseconds or null when unknown. |
|
|
136
138
|
| `onLocationError` | `{ name, message, id? }` when a background service fails, with the region's `id` when it concerns one. |
|
|
137
139
|
|
|
140
|
+
In Doze, the idle state of an unplugged Android device left still with its
|
|
141
|
+
screen off, Android starts the app for updates and region transitions alike,
|
|
142
|
+
but only an update's delivery lets the app use the network for a while. The
|
|
143
|
+
listeners for a region transition, `onStart` among them when it launches the
|
|
144
|
+
app, have no network until Doze ends: record the transition and send it from
|
|
145
|
+
a later event.
|
|
146
|
+
|
|
138
147
|
`positions` holds the positions that arrived while the Worker handled the
|
|
139
148
|
previous update, oldest first, so a slow Worker never builds a queue. Each is
|
|
140
149
|
newer than those delivered before it, so a position that both updates and
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokamakdev/plugin-location",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.125",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/mantty/tokamak.git",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"./android": "./android/bindings.ts"
|
|
15
15
|
},
|
|
16
16
|
"peerDependencies": {
|
|
17
|
-
"@tokamakdev/plugin": "0.1.0-beta.
|
|
17
|
+
"@tokamakdev/plugin": "0.1.0-beta.125"
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
20
|
"android",
|
package/src/android.ts
CHANGED
|
@@ -8,6 +8,8 @@ import {
|
|
|
8
8
|
Location,
|
|
9
9
|
LocationListener,
|
|
10
10
|
LocationManager,
|
|
11
|
+
LocationRequest,
|
|
12
|
+
LocationRequest$Builder,
|
|
11
13
|
SystemClock,
|
|
12
14
|
type Executor,
|
|
13
15
|
} from "../android/bindings.js";
|
|
@@ -15,15 +17,15 @@ import { permissionDenied, unavailable, within } from "./errors.js";
|
|
|
15
17
|
import type { Position, PositionOptions } from "./types.js";
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
* The device's position: the provider's last known when no older than `maximumAge`, or a new
|
|
19
|
-
* within `timeout`. Null when Android finds none.
|
|
20
|
+
* The device's position: the provider's last known when no older than `maximumAge`, or a new
|
|
21
|
+
* high-accuracy fix within `timeout`. Null when Android finds none.
|
|
20
22
|
*/
|
|
21
23
|
export async function currentPosition(ctx: TokamakContext, { maximumAge = 0, timeout }: PositionOptions): Promise<Position | null> {
|
|
22
24
|
const { context, manager } = await locationManager(ctx);
|
|
23
25
|
const name = await provider(manager);
|
|
24
26
|
const recent = await manager.getLastKnownLocation(name);
|
|
25
27
|
if (recent && await age(ctx, recent) <= maximumAge) return await position(recent);
|
|
26
|
-
const [signal, executor] = await Promise.all([CancellationSignal.new(ctx), mainExecutor(context)]);
|
|
28
|
+
const [request, signal, executor] = await Promise.all([highAccuracy(ctx, 0), CancellationSignal.new(ctx), mainExecutor(context)]);
|
|
27
29
|
let consumer: Consumer | undefined;
|
|
28
30
|
let finished = false;
|
|
29
31
|
const found = new Promise<Position | null>((resolve, reject) => {
|
|
@@ -35,7 +37,7 @@ export async function currentPosition(ctx: TokamakContext, { maximumAge = 0, tim
|
|
|
35
37
|
release(created);
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
38
|
-
await manager.getCurrentLocation(name, signal, executor, created);
|
|
40
|
+
await manager.getCurrentLocation(name, request, signal, executor, created);
|
|
39
41
|
}).catch((failure: unknown) => { reject(locationError(failure as Error)); });
|
|
40
42
|
});
|
|
41
43
|
return within(found, timeout, () => {
|
|
@@ -52,7 +54,7 @@ export async function watchPositions(
|
|
|
52
54
|
error: (error: unknown) => void,
|
|
53
55
|
): Promise<() => void> {
|
|
54
56
|
const { context, manager } = await locationManager(ctx);
|
|
55
|
-
const [name, executor] = await Promise.all([provider(manager), mainExecutor(context)]);
|
|
57
|
+
const [name, executor, request] = await Promise.all([provider(manager), mainExecutor(context), highAccuracy(ctx, 1000)]);
|
|
56
58
|
const listener = await LocationListener.implement(ctx, {
|
|
57
59
|
async onLocationChanged(location) {
|
|
58
60
|
next(await position(location));
|
|
@@ -61,7 +63,7 @@ export async function watchPositions(
|
|
|
61
63
|
onProviderDisabled() { error(unavailable("Location provider is unavailable")); },
|
|
62
64
|
});
|
|
63
65
|
try {
|
|
64
|
-
await manager.requestLocationUpdates(name,
|
|
66
|
+
await manager.requestLocationUpdates(name, request, executor, listener);
|
|
65
67
|
} catch (failure) {
|
|
66
68
|
release(listener);
|
|
67
69
|
throw locationError(failure as Error);
|
|
@@ -84,9 +86,12 @@ async function mainExecutor(context: Context): Promise<Executor> {
|
|
|
84
86
|
return executor;
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* The first enabled of the fused provider, which combines GPS with Wi-Fi and cell signals, GPS and
|
|
91
|
+
* the network, or any other enabled provider.
|
|
92
|
+
*/
|
|
88
93
|
async function provider(manager: LocationManager): Promise<string> {
|
|
89
|
-
for (const name of [LocationManager.
|
|
94
|
+
for (const name of [LocationManager.FUSED_PROVIDER, LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER]) {
|
|
90
95
|
if (await manager.isProviderEnabled(name)) return name;
|
|
91
96
|
}
|
|
92
97
|
const [other] = await manager.getProviders(true);
|
|
@@ -94,6 +99,13 @@ async function provider(manager: LocationManager): Promise<string> {
|
|
|
94
99
|
return other;
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
/** A request for the most accurate fixes the provider can give, every `interval` milliseconds. */
|
|
103
|
+
async function highAccuracy(ctx: TokamakContext, interval: number): Promise<LocationRequest> {
|
|
104
|
+
const builder = await LocationRequest$Builder.new(ctx, interval);
|
|
105
|
+
await builder.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY);
|
|
106
|
+
return builder.build();
|
|
107
|
+
}
|
|
108
|
+
|
|
97
109
|
/** The milliseconds since Android took `location`. */
|
|
98
110
|
async function age(ctx: TokamakContext, location: Location): Promise<number> {
|
|
99
111
|
const [now, taken] = await Promise.all([SystemClock.elapsedRealtimeNanos(ctx), location.getElapsedRealtimeNanos()]);
|