@tokamakdev/plugin-location 0.1.0-beta.101 → 0.1.0-beta.103
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 +198 -18
- package/android/LocationEvents.kt +82 -0
- package/android/LocationPlugin.kt +216 -34
- package/android/LocationReceiver.kt +27 -0
- package/android/LocationRegions.kt +73 -0
- package/android/LocationService.kt +64 -0
- package/android/LocationUpdates.kt +120 -0
- package/apple/LocationEvents.swift +149 -0
- package/apple/LocationPlugin.swift +347 -56
- package/apple/LocationRegions.swift +154 -0
- package/apple/LocationUpdates.swift +137 -0
- package/build/declarations.d.mts +19 -0
- package/build/declarations.mjs +105 -0
- package/build/index.mjs +17 -0
- package/events/index.ts +18 -0
- package/package.json +6 -3
- package/src/checks.ts +65 -0
- package/src/index.ts +122 -25
- package/src/options.ts +54 -0
- package/src/types.ts +163 -0
- package/tokamak-plugin.json +52 -6
- package/web/index.ts +15 -1
- package/android/AndroidManifest.xml +0 -4
- package/apple/ios/Info.plist +0 -8
- package/apple/macos/Info.plist +0 -8
package/src/types.ts
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
export interface Coordinates {
|
|
2
|
+
readonly latitude: number;
|
|
3
|
+
readonly longitude: number;
|
|
4
|
+
readonly accuracy: number;
|
|
5
|
+
readonly altitude: number | null;
|
|
6
|
+
readonly altitudeAccuracy: number | null;
|
|
7
|
+
readonly heading: number | null;
|
|
8
|
+
readonly speed: number | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Position {
|
|
12
|
+
readonly coords: Coordinates;
|
|
13
|
+
readonly timestamp: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Options for `getCurrentPosition`, as the Geolocation API reads them. */
|
|
17
|
+
export interface PositionOptions {
|
|
18
|
+
/** The age in milliseconds of a recent position to accept instead of a new one; 0 by default. */
|
|
19
|
+
readonly maximumAge?: number;
|
|
20
|
+
/** Milliseconds to wait for a position before rejecting with `TimeoutError`; unlimited by default. */
|
|
21
|
+
readonly timeout?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type PositionCallback = (position: Position) => void;
|
|
25
|
+
export type PositionErrorCallback = (error: DOMException) => void;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* What runs at every launch: `setup` starts what it names and stops what it leaves out. Apple only;
|
|
29
|
+
* other platforms ignore these keys. Each relaunches the app for its events with Always permission.
|
|
30
|
+
*/
|
|
31
|
+
export interface SetupConfig {
|
|
32
|
+
/** Delivers significant changes of location to `onLocationUpdate`. */
|
|
33
|
+
readonly significantChanges?: boolean;
|
|
34
|
+
/** Delivers arrivals at and departures from places to `onVisit`. */
|
|
35
|
+
readonly visits?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Whether the user granted a permission, denied it, or has not been asked. */
|
|
39
|
+
export type Permission = "granted" | "denied" | "prompt";
|
|
40
|
+
|
|
41
|
+
export type Precision = "precise" | "approximate";
|
|
42
|
+
|
|
43
|
+
/** The app's location permission. */
|
|
44
|
+
export interface LocationPermission {
|
|
45
|
+
/** Location while the app is in use. */
|
|
46
|
+
readonly inUse: Permission;
|
|
47
|
+
/** Location while it is not: Apple's Always, Android's "Allow all the time". The same as `inUse` on macOS. */
|
|
48
|
+
readonly background: Permission;
|
|
49
|
+
/** The precision the app gets, or would get once allowed. */
|
|
50
|
+
readonly precision: Precision;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** What `requestPermission` asks for besides in-use permission. */
|
|
54
|
+
export interface PermissionRequest {
|
|
55
|
+
/** Background permission, which needs the `background` build setting. */
|
|
56
|
+
readonly background?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* `"precise"` asks for precise location where the user allowed approximate: on Apple platforms
|
|
59
|
+
* temporarily, for `purpose`. `"approximate"` asks Android for approximate location only.
|
|
60
|
+
*/
|
|
61
|
+
readonly precision?: Precision;
|
|
62
|
+
/** Apple only: the key in the `accuracy-purposes` build setting of the reason to show. */
|
|
63
|
+
readonly purpose?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Apple only: the live update configuration, by Apple's name, which tunes when updates pause and
|
|
68
|
+
* how coordinates follow roads. `"maritime"` needs iOS 27 or macOS 27.
|
|
69
|
+
*/
|
|
70
|
+
export type Activity =
|
|
71
|
+
| "default"
|
|
72
|
+
| "automotiveNavigation"
|
|
73
|
+
| "otherNavigation"
|
|
74
|
+
| "fitness"
|
|
75
|
+
| "airborne"
|
|
76
|
+
| "maritime";
|
|
77
|
+
|
|
78
|
+
/** `startUpdates` options. Each is platform-specific: Apple's relaunching updates take only an `activity`. */
|
|
79
|
+
export interface UpdateOptions {
|
|
80
|
+
/** Apple only: `"default"` by default. */
|
|
81
|
+
readonly activity?: Activity;
|
|
82
|
+
/** Android only: the request's quality, precision traded against battery use; `"high"` by default. */
|
|
83
|
+
readonly accuracy?: "high" | "balanced" | "low";
|
|
84
|
+
/** Android only: metres the device moves before the next update; 0 by default. */
|
|
85
|
+
readonly distance?: number;
|
|
86
|
+
/** Android only: milliseconds between updates; 10 seconds by default. */
|
|
87
|
+
readonly interval?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Android only: runs updates in a foreground service showing this notification, so they arrive at
|
|
90
|
+
* the requested rate off screen. Needs the `background-updates` build setting.
|
|
91
|
+
*/
|
|
92
|
+
readonly foregroundService?: ServiceNotification;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Android only: the foreground service's notification, set from JavaScript so apps can translate it. */
|
|
96
|
+
export interface ServiceNotification {
|
|
97
|
+
readonly title: string;
|
|
98
|
+
readonly body: string;
|
|
99
|
+
/** The name of the notification channel, which Android's settings show. */
|
|
100
|
+
readonly channel: string;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Positions from continuous updates or significant changes. */
|
|
104
|
+
export interface LocationUpdate {
|
|
105
|
+
/** Oldest first: those that arrived while the Worker handled the previous update come together. */
|
|
106
|
+
readonly positions: readonly Position[];
|
|
107
|
+
/**
|
|
108
|
+
* Apple only, from iOS 18 and macOS 15: true on the last update before updates pause while the
|
|
109
|
+
* device is still. `positions` is empty when the update has no position.
|
|
110
|
+
*/
|
|
111
|
+
readonly stationary?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** A circular region to monitor, with the same fields as the notifications plugin's `Region`. */
|
|
115
|
+
export interface Region {
|
|
116
|
+
readonly id: string;
|
|
117
|
+
readonly latitude: number;
|
|
118
|
+
readonly longitude: number;
|
|
119
|
+
/** In metres. */
|
|
120
|
+
readonly radius: number;
|
|
121
|
+
/** Whether entering the region reaches `onRegionTransition`; true by default. */
|
|
122
|
+
readonly onEntry?: boolean;
|
|
123
|
+
/** Whether leaving it does; true by default. */
|
|
124
|
+
readonly onExit?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** A monitored region. */
|
|
128
|
+
export interface MonitoredRegion extends Required<Region> {
|
|
129
|
+
/** Apple only: whether the device is inside the region, as the system last determined. */
|
|
130
|
+
readonly state?: "inside" | "outside" | "unknown";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** An entry into or exit from a monitored region. */
|
|
134
|
+
export interface RegionTransition {
|
|
135
|
+
readonly id: string;
|
|
136
|
+
readonly transition: "enter" | "exit";
|
|
137
|
+
/** Milliseconds since the epoch. */
|
|
138
|
+
readonly timestamp: number;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Apple only: an arrival at or departure from a place where the user stays. */
|
|
142
|
+
export interface Visit {
|
|
143
|
+
readonly latitude: number;
|
|
144
|
+
readonly longitude: number;
|
|
145
|
+
/** In metres. */
|
|
146
|
+
readonly accuracy: number;
|
|
147
|
+
/** Milliseconds since the epoch, or null when unknown. */
|
|
148
|
+
readonly arrival: number | null;
|
|
149
|
+
/** Milliseconds since the epoch, or null before the user leaves. */
|
|
150
|
+
readonly departure: number | null;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** A failure of a background location service. */
|
|
154
|
+
export interface LocationErrorEvent {
|
|
155
|
+
/** A `DOMException` name, such as `"NotAllowedError"` or `"NotReadableError"`. */
|
|
156
|
+
readonly name: string;
|
|
157
|
+
readonly message: string;
|
|
158
|
+
/** The region's id, when the failure concerns one. */
|
|
159
|
+
readonly id?: string;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export type Listener<T> = (event: T) => void;
|
|
163
|
+
export type ErrorListener = (error: DOMException) => void;
|
package/tokamak-plugin.json
CHANGED
|
@@ -1,21 +1,67 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"id": "location",
|
|
4
|
+
"settings": {
|
|
5
|
+
"background": {
|
|
6
|
+
"kind": "boolean",
|
|
7
|
+
"default": false,
|
|
8
|
+
"description": "Declares background location permission: Apple's Always, Android's ACCESS_BACKGROUND_LOCATION"
|
|
9
|
+
},
|
|
10
|
+
"background-updates": {
|
|
11
|
+
"kind": "boolean",
|
|
12
|
+
"default": false,
|
|
13
|
+
"description": "Declares continuous updates off screen: iOS's location background mode, and Android's location foreground service"
|
|
14
|
+
},
|
|
15
|
+
"precision": {
|
|
16
|
+
"kind": "enum",
|
|
17
|
+
"values": ["precise", "approximate"],
|
|
18
|
+
"default": "precise",
|
|
19
|
+
"description": "The precision the app declares; approximate leaves out precise location"
|
|
20
|
+
},
|
|
21
|
+
"when-in-use-description": {
|
|
22
|
+
"kind": "string",
|
|
23
|
+
"description": "Apple only: the reason the in-use prompt shows"
|
|
24
|
+
},
|
|
25
|
+
"always-description": {
|
|
26
|
+
"kind": "string",
|
|
27
|
+
"description": "iOS only: the reason the Always prompt shows"
|
|
28
|
+
},
|
|
29
|
+
"accuracy-purposes": {
|
|
30
|
+
"kind": "map",
|
|
31
|
+
"of": "string",
|
|
32
|
+
"description": "Apple only: the reasons for temporary precise location, by purpose key"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"build": "build/index.mjs",
|
|
4
36
|
"platforms": {
|
|
5
37
|
"macos": {
|
|
6
38
|
"class": "TokamakLocationPlugin",
|
|
7
|
-
"sources": [
|
|
8
|
-
|
|
39
|
+
"sources": [
|
|
40
|
+
"apple/LocationEvents.swift",
|
|
41
|
+
"apple/LocationPlugin.swift",
|
|
42
|
+
"apple/LocationRegions.swift",
|
|
43
|
+
"apple/LocationUpdates.swift"
|
|
44
|
+
]
|
|
9
45
|
},
|
|
10
46
|
"ios": {
|
|
11
47
|
"class": "TokamakLocationPlugin",
|
|
12
|
-
"sources": [
|
|
13
|
-
|
|
48
|
+
"sources": [
|
|
49
|
+
"apple/LocationEvents.swift",
|
|
50
|
+
"apple/LocationPlugin.swift",
|
|
51
|
+
"apple/LocationRegions.swift",
|
|
52
|
+
"apple/LocationUpdates.swift"
|
|
53
|
+
]
|
|
14
54
|
},
|
|
15
55
|
"android": {
|
|
16
56
|
"class": "com.tokamak.plugins.location.TokamakLocationPlugin",
|
|
17
|
-
"sources": [
|
|
18
|
-
|
|
57
|
+
"sources": [
|
|
58
|
+
"android/LocationEvents.kt",
|
|
59
|
+
"android/LocationPlugin.kt",
|
|
60
|
+
"android/LocationReceiver.kt",
|
|
61
|
+
"android/LocationRegions.kt",
|
|
62
|
+
"android/LocationService.kt",
|
|
63
|
+
"android/LocationUpdates.kt"
|
|
64
|
+
]
|
|
19
65
|
}
|
|
20
66
|
}
|
|
21
67
|
}
|
package/web/index.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
LocationPermission,
|
|
2
3
|
Position,
|
|
3
4
|
PositionCallback,
|
|
4
5
|
PositionErrorCallback,
|
|
5
6
|
PositionOptions,
|
|
6
|
-
} from "../src/
|
|
7
|
+
} from "../src/types.js";
|
|
7
8
|
|
|
8
9
|
export function getCurrentPosition(options: PositionOptions): Promise<Position> {
|
|
9
10
|
return new Promise((resolve, reject) => {
|
|
@@ -38,6 +39,19 @@ export function watchPosition(
|
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/** The page's location permission, which the web has only for pages in use. */
|
|
43
|
+
export async function permission(): Promise<LocationPermission> {
|
|
44
|
+
const permissions = (globalThis as { navigator?: { permissions?: Permissions } }).navigator?.permissions;
|
|
45
|
+
if (!permissions) throw new DOMException("The Permissions API is unavailable", "NotSupportedError");
|
|
46
|
+
const { state } = await permissions.query({ name: "geolocation" });
|
|
47
|
+
return { inUse: state, background: "denied", precision: "precise" };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Rejects as the web does for what only native apps have. */
|
|
51
|
+
export function unsupported(): Promise<never> {
|
|
52
|
+
return Promise.reject(new DOMException("Native location is unavailable", "NotSupportedError"));
|
|
53
|
+
}
|
|
54
|
+
|
|
41
55
|
function browserLocationApi(): Geolocation {
|
|
42
56
|
const root: { navigator?: { geolocation?: Geolocation } } = globalThis;
|
|
43
57
|
const browserLocation = root.navigator?.geolocation;
|
package/apple/ios/Info.plist
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>NSLocationWhenInUseUsageDescription</key>
|
|
6
|
-
<string>This app uses your location when you request location features.</string>
|
|
7
|
-
</dict>
|
|
8
|
-
</plist>
|
package/apple/macos/Info.plist
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
-
<plist version="1.0">
|
|
4
|
-
<dict>
|
|
5
|
-
<key>NSLocationUsageDescription</key>
|
|
6
|
-
<string>This app uses your location when you request location features.</string>
|
|
7
|
-
</dict>
|
|
8
|
-
</plist>
|