@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
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import CoreLocation
|
|
2
|
+
import Foundation
|
|
3
|
+
|
|
4
|
+
extension TokamakLocationPlugin {
|
|
5
|
+
struct Coordinates: Codable {
|
|
6
|
+
let latitude: Double
|
|
7
|
+
let longitude: Double
|
|
8
|
+
let accuracy: Double
|
|
9
|
+
let altitude: Double?
|
|
10
|
+
let altitudeAccuracy: Double?
|
|
11
|
+
let heading: Double?
|
|
12
|
+
let speed: Double?
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
struct Position: Codable {
|
|
16
|
+
let coords: Coordinates
|
|
17
|
+
/// Milliseconds since the epoch.
|
|
18
|
+
let timestamp: Double
|
|
19
|
+
|
|
20
|
+
init(_ location: CLLocation) {
|
|
21
|
+
let coordinate = location.coordinate
|
|
22
|
+
let altitude = location.verticalAccuracy >= 0
|
|
23
|
+
coords = Coordinates(
|
|
24
|
+
latitude: coordinate.latitude,
|
|
25
|
+
longitude: coordinate.longitude,
|
|
26
|
+
accuracy: location.horizontalAccuracy,
|
|
27
|
+
altitude: altitude ? location.altitude : nil,
|
|
28
|
+
altitudeAccuracy: altitude ? location.verticalAccuracy : nil,
|
|
29
|
+
heading: location.course >= 0 ? location.course : nil,
|
|
30
|
+
speed: location.speed >= 0 ? location.speed : nil
|
|
31
|
+
)
|
|
32
|
+
timestamp = location.timestamp.milliseconds
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct LocationUpdate: Codable {
|
|
37
|
+
let positions: [Position]
|
|
38
|
+
/// True on the last update before updates pause while the device is still.
|
|
39
|
+
let stationary: Bool?
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
enum Transition: String, Codable {
|
|
43
|
+
case enter
|
|
44
|
+
case exit
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
struct RegionTransition: Codable {
|
|
48
|
+
let id: String
|
|
49
|
+
let transition: Transition
|
|
50
|
+
/// Milliseconds since the epoch.
|
|
51
|
+
let timestamp: Double
|
|
52
|
+
|
|
53
|
+
init(id: String, entered: Bool, at date: Date) {
|
|
54
|
+
self.id = id
|
|
55
|
+
transition = entered ? .enter : .exit
|
|
56
|
+
timestamp = date.milliseconds
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
struct Visit: Codable {
|
|
61
|
+
let latitude: Double
|
|
62
|
+
let longitude: Double
|
|
63
|
+
let accuracy: Double
|
|
64
|
+
/// Milliseconds since the epoch, or nil when unknown.
|
|
65
|
+
let arrival: Double?
|
|
66
|
+
/// Milliseconds since the epoch, or nil before the user leaves.
|
|
67
|
+
let departure: Double?
|
|
68
|
+
|
|
69
|
+
init(_ visit: CLVisit) {
|
|
70
|
+
latitude = visit.coordinate.latitude
|
|
71
|
+
longitude = visit.coordinate.longitude
|
|
72
|
+
accuracy = visit.horizontalAccuracy
|
|
73
|
+
arrival = visit.arrivalDate == .distantPast ? nil : visit.arrivalDate.milliseconds
|
|
74
|
+
departure = visit.departureDate == .distantFuture ? nil : visit.departureDate.milliseconds
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
struct LocationErrorEvent: Codable {
|
|
79
|
+
let name: String
|
|
80
|
+
let message: String
|
|
81
|
+
/// The region's id, when the failure concerns one.
|
|
82
|
+
let id: String?
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
enum Permission: String, Codable {
|
|
86
|
+
case granted
|
|
87
|
+
case denied
|
|
88
|
+
case prompt
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
enum Precision: String, Codable {
|
|
92
|
+
case precise
|
|
93
|
+
case approximate
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
struct LocationPermission: Codable {
|
|
97
|
+
let inUse: Permission
|
|
98
|
+
let background: Permission
|
|
99
|
+
let precision: Precision
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
struct PermissionRequest: Codable {
|
|
103
|
+
let background: Bool?
|
|
104
|
+
let precision: Precision?
|
|
105
|
+
/// The key in the app's `NSLocationTemporaryUsageDescriptionDictionary` of the reason to show.
|
|
106
|
+
let purpose: String?
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
struct SetupConfig: Codable {
|
|
110
|
+
let significantChanges: Bool?
|
|
111
|
+
let visits: Bool?
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/// The live update configuration, by Apple's name.
|
|
115
|
+
enum Activity: String, Codable {
|
|
116
|
+
case `default`
|
|
117
|
+
case automotiveNavigation
|
|
118
|
+
case otherNavigation
|
|
119
|
+
case fitness
|
|
120
|
+
case airborne
|
|
121
|
+
case maritime
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
struct UpdateOptions: Codable {
|
|
125
|
+
let activity: Activity?
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
struct Region: Codable {
|
|
129
|
+
let id: String
|
|
130
|
+
let latitude: Double
|
|
131
|
+
let longitude: Double
|
|
132
|
+
/// In metres.
|
|
133
|
+
let radius: Double
|
|
134
|
+
let onEntry: Bool?
|
|
135
|
+
let onExit: Bool?
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
enum RegionState: String, Codable {
|
|
139
|
+
case inside
|
|
140
|
+
case outside
|
|
141
|
+
case unknown
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
struct MonitoredRegion: Codable {
|
|
145
|
+
let id: String
|
|
146
|
+
let latitude: Double
|
|
147
|
+
let longitude: Double
|
|
148
|
+
let radius: Double
|
|
149
|
+
let onEntry: Bool
|
|
150
|
+
let onExit: Bool
|
|
151
|
+
/// Whether the device is inside the region, as the system last determined.
|
|
152
|
+
let state: RegionState
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
extension Date {
|
|
157
|
+
fileprivate var milliseconds: Double { timeIntervalSince1970 * 1000 }
|
|
158
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import CoreLocation
|
|
2
2
|
import Foundation
|
|
3
3
|
|
|
4
|
-
/// The JSON of the running updates' options
|
|
4
|
+
/// The JSON of the running updates' options.
|
|
5
5
|
private let updatesKey = "tokamak.location.updates"
|
|
6
6
|
|
|
7
7
|
/// Continuous updates from `CLLocationUpdate.liveUpdates`. iOS launches an app
|
|
@@ -22,16 +22,16 @@ final class TokamakLocationUpdates {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/// The options of the running updates, or nil.
|
|
25
|
-
var options:
|
|
25
|
+
var options: TokamakLocationPlugin.UpdateOptions? {
|
|
26
26
|
defaults.data(forKey: updatesKey)
|
|
27
|
-
.flatMap { try?
|
|
27
|
+
.flatMap { try? JSONDecoder().decode(TokamakLocationPlugin.UpdateOptions.self, from: $0) }
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
/// Starts updates with `options`, replacing any running.
|
|
31
|
-
func start(_ options:
|
|
32
|
-
let configuration = try Self.configuration(options)
|
|
31
|
+
func start(_ options: TokamakLocationPlugin.UpdateOptions) throws(TokamakPluginError) {
|
|
32
|
+
let configuration = try Self.configuration(options.activity ?? .default)
|
|
33
33
|
stop()
|
|
34
|
-
defaults.set(try?
|
|
34
|
+
defaults.set(try? JSONEncoder().encode(options), forKey: updatesKey)
|
|
35
35
|
run(configuration)
|
|
36
36
|
}
|
|
37
37
|
|
|
@@ -39,7 +39,8 @@ final class TokamakLocationUpdates {
|
|
|
39
39
|
/// their session for only a few seconds after it launches the app, so call
|
|
40
40
|
/// it before launch finishes.
|
|
41
41
|
func resume() {
|
|
42
|
-
guard let options, let configuration = try? Self.configuration(options
|
|
42
|
+
guard let options, let configuration = try? Self.configuration(options.activity ?? .default)
|
|
43
|
+
else { return }
|
|
43
44
|
run(configuration)
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -60,16 +61,9 @@ final class TokamakLocationUpdates {
|
|
|
60
61
|
session = CLBackgroundActivitySession()
|
|
61
62
|
}
|
|
62
63
|
#endif
|
|
63
|
-
task =
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
self?.receive(update)
|
|
67
|
-
}
|
|
68
|
-
} catch {
|
|
69
|
-
if !Task.isCancelled {
|
|
70
|
-
self?.events.fail(
|
|
71
|
-
.locationUnavailable(error.localizedDescription))
|
|
72
|
-
}
|
|
64
|
+
task = events.task { [weak self] in
|
|
65
|
+
for try await update in CLLocationUpdate.liveUpdates(configuration) {
|
|
66
|
+
self?.receive(update)
|
|
73
67
|
}
|
|
74
68
|
}
|
|
75
69
|
}
|
|
@@ -104,17 +98,17 @@ final class TokamakLocationUpdates {
|
|
|
104
98
|
return flags.filter(\.0).map { TokamakPluginError(name: $0.1, message: $0.2) }
|
|
105
99
|
}
|
|
106
100
|
|
|
107
|
-
/// The live update configuration
|
|
101
|
+
/// The live update configuration of `activity`.
|
|
108
102
|
private static func configuration(
|
|
109
|
-
_
|
|
103
|
+
_ activity: TokamakLocationPlugin.Activity
|
|
110
104
|
) throws(TokamakPluginError) -> CLLocationUpdate.LiveConfiguration {
|
|
111
|
-
switch
|
|
112
|
-
case
|
|
113
|
-
case
|
|
114
|
-
case
|
|
115
|
-
case
|
|
116
|
-
case
|
|
117
|
-
case
|
|
105
|
+
switch activity {
|
|
106
|
+
case .default: return .default
|
|
107
|
+
case .automotiveNavigation: return .automotiveNavigation
|
|
108
|
+
case .otherNavigation: return .otherNavigation
|
|
109
|
+
case .fitness: return .fitness
|
|
110
|
+
case .airborne: return .airborne
|
|
111
|
+
case .maritime:
|
|
118
112
|
// The SDKs that have `maritime` ship with Swift 6.4.
|
|
119
113
|
#if compiler(>=6.4)
|
|
120
114
|
if #available(iOS 27, macOS 27, *) {
|
|
@@ -122,8 +116,6 @@ final class TokamakLocationUpdates {
|
|
|
122
116
|
}
|
|
123
117
|
#endif
|
|
124
118
|
throw .notSupported("The maritime activity needs iOS 27 or macOS 27")
|
|
125
|
-
default:
|
|
126
|
-
throw TokamakPluginError(name: "TypeError", message: "activity is not a live configuration")
|
|
127
119
|
}
|
|
128
120
|
}
|
|
129
121
|
|
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.130",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/mantty/tokamak.git",
|
|
@@ -9,16 +9,17 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"exports": {
|
|
11
11
|
".": "./src/index.ts",
|
|
12
|
-
"./
|
|
12
|
+
"./apple": "./src/apple.generated.ts",
|
|
13
|
+
"./android": "./src/android.generated.ts",
|
|
14
|
+
"./events": "./src/events.ts"
|
|
13
15
|
},
|
|
14
16
|
"peerDependencies": {
|
|
15
|
-
"@tokamakdev/plugin": "0.1.0-beta.
|
|
17
|
+
"@tokamakdev/plugin": "0.1.0-beta.130"
|
|
16
18
|
},
|
|
17
19
|
"files": [
|
|
18
20
|
"android",
|
|
19
21
|
"apple",
|
|
20
22
|
"build",
|
|
21
|
-
"events",
|
|
22
23
|
"src",
|
|
23
24
|
"web",
|
|
24
25
|
"tokamak-plugin.json",
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// Generated by `tok plugin generate android`. Do not edit.
|
|
2
|
+
import { NativeEndpoint, type JsonStream, type TokamakContext } from "@tokamakdev/plugin";
|
|
3
|
+
import { defineEvent } from "@tokamakdev/plugin/events";
|
|
4
|
+
|
|
5
|
+
export type Accuracy = "high" | "balanced" | "low";
|
|
6
|
+
|
|
7
|
+
export interface Coordinates {
|
|
8
|
+
readonly latitude: number;
|
|
9
|
+
readonly longitude: number;
|
|
10
|
+
readonly accuracy: number;
|
|
11
|
+
readonly altitude?: number;
|
|
12
|
+
readonly altitudeAccuracy?: number;
|
|
13
|
+
readonly heading?: number;
|
|
14
|
+
readonly speed?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LocationErrorEvent {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LocationPermission {
|
|
23
|
+
readonly inUse: Permission;
|
|
24
|
+
readonly background: Permission;
|
|
25
|
+
readonly precision: Precision;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface LocationUpdate {
|
|
29
|
+
readonly positions: readonly Position[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface MonitoredRegion {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly latitude: number;
|
|
35
|
+
readonly longitude: number;
|
|
36
|
+
readonly radius: number;
|
|
37
|
+
readonly onEntry: boolean;
|
|
38
|
+
readonly onExit: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type Permission = "granted" | "denied" | "prompt";
|
|
42
|
+
|
|
43
|
+
export interface PermissionRequest {
|
|
44
|
+
readonly background?: boolean;
|
|
45
|
+
readonly precision?: Precision;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface Position {
|
|
49
|
+
readonly coords: Coordinates;
|
|
50
|
+
readonly timestamp: number;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type Precision = "precise" | "approximate";
|
|
54
|
+
|
|
55
|
+
export interface Region {
|
|
56
|
+
readonly id: string;
|
|
57
|
+
readonly latitude: number;
|
|
58
|
+
readonly longitude: number;
|
|
59
|
+
readonly radius: number;
|
|
60
|
+
readonly onEntry?: boolean;
|
|
61
|
+
readonly onExit?: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface RegionTransition {
|
|
65
|
+
readonly id: string;
|
|
66
|
+
readonly transition: Transition;
|
|
67
|
+
readonly timestamp: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ServiceNotification {
|
|
71
|
+
readonly title: string;
|
|
72
|
+
readonly body: string;
|
|
73
|
+
readonly channel: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface SetupConfig {
|
|
77
|
+
readonly significantChanges?: boolean;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type Transition = "enter" | "exit";
|
|
81
|
+
|
|
82
|
+
export interface UpdateOptions {
|
|
83
|
+
readonly accuracy?: Accuracy;
|
|
84
|
+
readonly distance?: number;
|
|
85
|
+
readonly interval?: number;
|
|
86
|
+
readonly foregroundService?: ServiceNotification;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The location plugin's native methods on Android. */
|
|
90
|
+
export interface Android {
|
|
91
|
+
/** Applies long-term location awareness, replacing the previous configuration. */
|
|
92
|
+
setup(config: SetupConfig): Promise<void>;
|
|
93
|
+
/** The provider's last known position, when it is no older than `maximumAge`. */
|
|
94
|
+
recentPosition(maximumAge: number): Promise<Position | null>;
|
|
95
|
+
/** A new fix, or null when Android finds none. Cancelling the call stops locating. */
|
|
96
|
+
currentPosition(): Promise<Position | null>;
|
|
97
|
+
/** Positions as the device moves. */
|
|
98
|
+
watchPosition(): JsonStream<Position>;
|
|
99
|
+
/** The app's in-use and background permission, and the precision it gets. */
|
|
100
|
+
permission(): Promise<LocationPermission>;
|
|
101
|
+
/** Asks for the permission `request` names where the user has not answered. */
|
|
102
|
+
requestPermission(request?: PermissionRequest | null): Promise<LocationPermission>;
|
|
103
|
+
/** Whether location services are on for the device. */
|
|
104
|
+
servicesEnabled(): Promise<boolean>;
|
|
105
|
+
/** Opens the app's page in the system settings. */
|
|
106
|
+
openSettings(): Promise<void>;
|
|
107
|
+
/** Starts continuous updates, replacing any running, until `stopUpdates`. */
|
|
108
|
+
startUpdates(options?: UpdateOptions | null): Promise<void>;
|
|
109
|
+
stopUpdates(): Promise<void>;
|
|
110
|
+
/** The options of the running updates, or null when none are running. */
|
|
111
|
+
getUpdates(): Promise<UpdateOptions | null>;
|
|
112
|
+
/** Monitors a circular region, replacing one with the same `id`. */
|
|
113
|
+
addRegion(region: Region): Promise<void>;
|
|
114
|
+
/** The monitored regions. */
|
|
115
|
+
getRegions(): Promise<MonitoredRegion[]>;
|
|
116
|
+
removeRegion(id: string): Promise<void>;
|
|
117
|
+
/** The updates the Worker's listeners receive. */
|
|
118
|
+
onLocationUpdate(): JsonStream<LocationUpdate>;
|
|
119
|
+
/** The transitions the Worker's listeners receive. */
|
|
120
|
+
onRegionTransition(): JsonStream<RegionTransition>;
|
|
121
|
+
/** The failures the Worker's listeners receive. */
|
|
122
|
+
onLocationError(): JsonStream<LocationErrorEvent>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The location plugin's native methods. Off Android they reject with `NotSupportedError`.
|
|
127
|
+
* Aborting `signal` cancels their calls.
|
|
128
|
+
*/
|
|
129
|
+
export function android(ctx: TokamakContext, signal?: AbortSignal): Android {
|
|
130
|
+
const endpoint = new NativeEndpoint(ctx, "location", ["android"], signal);
|
|
131
|
+
return {
|
|
132
|
+
setup: (config) => endpoint.call("setup", { config }),
|
|
133
|
+
recentPosition: (maximumAge) => endpoint.call("recentPosition", { maximumAge }),
|
|
134
|
+
currentPosition: () => endpoint.call("currentPosition"),
|
|
135
|
+
watchPosition: () => endpoint.stream("watchPosition", {}, "dropOldest"),
|
|
136
|
+
permission: () => endpoint.call("permission"),
|
|
137
|
+
requestPermission: (request) => endpoint.call("requestPermission", { request }),
|
|
138
|
+
servicesEnabled: () => endpoint.call("servicesEnabled"),
|
|
139
|
+
openSettings: () => endpoint.call("openSettings"),
|
|
140
|
+
startUpdates: (options) => endpoint.call("startUpdates", { options }),
|
|
141
|
+
stopUpdates: () => endpoint.call("stopUpdates"),
|
|
142
|
+
getUpdates: () => endpoint.call("getUpdates"),
|
|
143
|
+
addRegion: (region) => endpoint.call("addRegion", { region }),
|
|
144
|
+
getRegions: () => endpoint.call("getRegions"),
|
|
145
|
+
removeRegion: (id) => endpoint.call("removeRegion", { id }),
|
|
146
|
+
onLocationUpdate: () => endpoint.stream("onLocationUpdate", {}, "dropOldest"),
|
|
147
|
+
onRegionTransition: () => endpoint.stream("onRegionTransition"),
|
|
148
|
+
onLocationError: () => endpoint.stream("onLocationError"),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
declare global {
|
|
153
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace -- events are declared by merging into Tokamak.Events.
|
|
154
|
+
namespace Tokamak {
|
|
155
|
+
interface Events {
|
|
156
|
+
"location.android.update": LocationUpdate;
|
|
157
|
+
"location.android.region": RegionTransition;
|
|
158
|
+
"location.android.error": LocationErrorEvent;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** Receives continuous updates and significant changes, including those that start the app. */
|
|
164
|
+
export const onUpdate = defineEvent("location.android.update", { platforms: ["android"] });
|
|
165
|
+
|
|
166
|
+
/** Receives each region entry or exit, including one that starts the app. */
|
|
167
|
+
export const onRegion = defineEvent("location.android.region", { platforms: ["android"] });
|
|
168
|
+
|
|
169
|
+
/** Receives failures of background services, such as a region Android cannot monitor. */
|
|
170
|
+
export const onError = defineEvent("location.android.error", { platforms: ["android"] });
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// Generated by `tok plugin generate apple`. Do not edit.
|
|
2
|
+
import { NativeEndpoint, type JsonStream, type TokamakContext } from "@tokamakdev/plugin";
|
|
3
|
+
import { defineEvent } from "@tokamakdev/plugin/events";
|
|
4
|
+
|
|
5
|
+
export type Activity = "default" | "automotiveNavigation" | "otherNavigation" | "fitness" | "airborne" | "maritime";
|
|
6
|
+
|
|
7
|
+
export interface Coordinates {
|
|
8
|
+
readonly latitude: number;
|
|
9
|
+
readonly longitude: number;
|
|
10
|
+
readonly accuracy: number;
|
|
11
|
+
readonly altitude?: number;
|
|
12
|
+
readonly altitudeAccuracy?: number;
|
|
13
|
+
readonly heading?: number;
|
|
14
|
+
readonly speed?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LocationErrorEvent {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly message: string;
|
|
20
|
+
readonly id?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface LocationPermission {
|
|
24
|
+
readonly inUse: Permission;
|
|
25
|
+
readonly background: Permission;
|
|
26
|
+
readonly precision: Precision;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface LocationUpdate {
|
|
30
|
+
readonly positions: readonly Position[];
|
|
31
|
+
readonly stationary?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MonitoredRegion {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly latitude: number;
|
|
37
|
+
readonly longitude: number;
|
|
38
|
+
readonly radius: number;
|
|
39
|
+
readonly onEntry: boolean;
|
|
40
|
+
readonly onExit: boolean;
|
|
41
|
+
readonly state: RegionState;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Permission = "granted" | "denied" | "prompt";
|
|
45
|
+
|
|
46
|
+
export interface PermissionRequest {
|
|
47
|
+
readonly background?: boolean;
|
|
48
|
+
readonly precision?: Precision;
|
|
49
|
+
readonly purpose?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface Position {
|
|
53
|
+
readonly coords: Coordinates;
|
|
54
|
+
readonly timestamp: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type Precision = "precise" | "approximate";
|
|
58
|
+
|
|
59
|
+
export interface Region {
|
|
60
|
+
readonly id: string;
|
|
61
|
+
readonly latitude: number;
|
|
62
|
+
readonly longitude: number;
|
|
63
|
+
readonly radius: number;
|
|
64
|
+
readonly onEntry?: boolean;
|
|
65
|
+
readonly onExit?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export type RegionState = "inside" | "outside" | "unknown";
|
|
69
|
+
|
|
70
|
+
export interface RegionTransition {
|
|
71
|
+
readonly id: string;
|
|
72
|
+
readonly transition: Transition;
|
|
73
|
+
readonly timestamp: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface SetupConfig {
|
|
77
|
+
readonly significantChanges?: boolean;
|
|
78
|
+
readonly visits?: boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export type Transition = "enter" | "exit";
|
|
82
|
+
|
|
83
|
+
export interface UpdateOptions {
|
|
84
|
+
readonly activity?: Activity;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface Visit {
|
|
88
|
+
readonly latitude: number;
|
|
89
|
+
readonly longitude: number;
|
|
90
|
+
readonly accuracy: number;
|
|
91
|
+
readonly arrival?: number;
|
|
92
|
+
readonly departure?: number;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** The location plugin's native methods on iOS and macOS. */
|
|
96
|
+
export interface Apple {
|
|
97
|
+
/** Applies long-term location awareness, replacing the previous configuration. */
|
|
98
|
+
setup(config: SetupConfig): Promise<void>;
|
|
99
|
+
/** Core Location's latest position, when it is no older than `maximumAge`. */
|
|
100
|
+
recentPosition(maximumAge: number): Promise<Position | null>;
|
|
101
|
+
/** A new fix. Cancelling the call stops locating. */
|
|
102
|
+
currentPosition(): Promise<Position>;
|
|
103
|
+
/** Positions as the device moves. */
|
|
104
|
+
watchPosition(): JsonStream<Position>;
|
|
105
|
+
/** The app's in-use and background permission, and the precision it gets. */
|
|
106
|
+
permission(): Promise<LocationPermission>;
|
|
107
|
+
/** Asks for the permission `request` names where the user has not answered. */
|
|
108
|
+
requestPermission(request?: PermissionRequest | null): Promise<LocationPermission>;
|
|
109
|
+
/** Whether location services are on for the device. */
|
|
110
|
+
servicesEnabled(): Promise<boolean>;
|
|
111
|
+
/** Opens the app's page in the system settings. */
|
|
112
|
+
openSettings(): Promise<void>;
|
|
113
|
+
/** Starts continuous updates, replacing any running, until `stopUpdates`. */
|
|
114
|
+
startUpdates(options?: UpdateOptions | null): Promise<void>;
|
|
115
|
+
stopUpdates(): Promise<void>;
|
|
116
|
+
/** The options of the running updates, or null when none are running. */
|
|
117
|
+
getUpdates(): Promise<UpdateOptions | null>;
|
|
118
|
+
/** Monitors a circular region, replacing one with the same `id`. */
|
|
119
|
+
addRegion(region: Region): Promise<void>;
|
|
120
|
+
/** The monitored regions. */
|
|
121
|
+
getRegions(): Promise<MonitoredRegion[]>;
|
|
122
|
+
removeRegion(id: string): Promise<void>;
|
|
123
|
+
/** The updates the Worker's listeners receive. */
|
|
124
|
+
onLocationUpdate(): JsonStream<LocationUpdate>;
|
|
125
|
+
/** The transitions the Worker's listeners receive. */
|
|
126
|
+
onRegionTransition(): JsonStream<RegionTransition>;
|
|
127
|
+
/** The visits the Worker's listeners receive. */
|
|
128
|
+
onVisit(): JsonStream<Visit>;
|
|
129
|
+
/** The failures the Worker's listeners receive. */
|
|
130
|
+
onLocationError(): JsonStream<LocationErrorEvent>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The location plugin's native methods. Off iOS and macOS they reject with `NotSupportedError`.
|
|
135
|
+
* Aborting `signal` cancels their calls.
|
|
136
|
+
*/
|
|
137
|
+
export function apple(ctx: TokamakContext, signal?: AbortSignal): Apple {
|
|
138
|
+
const endpoint = new NativeEndpoint(ctx, "location", ["ios", "macos"], signal);
|
|
139
|
+
return {
|
|
140
|
+
setup: (config) => endpoint.call("setup", { config }),
|
|
141
|
+
recentPosition: (maximumAge) => endpoint.call("recentPosition", { maximumAge }),
|
|
142
|
+
currentPosition: () => endpoint.call("currentPosition"),
|
|
143
|
+
watchPosition: () => endpoint.stream("watchPosition", {}, "dropOldest"),
|
|
144
|
+
permission: () => endpoint.call("permission"),
|
|
145
|
+
requestPermission: (request) => endpoint.call("requestPermission", { request }),
|
|
146
|
+
servicesEnabled: () => endpoint.call("servicesEnabled"),
|
|
147
|
+
openSettings: () => endpoint.call("openSettings"),
|
|
148
|
+
startUpdates: (options) => endpoint.call("startUpdates", { options }),
|
|
149
|
+
stopUpdates: () => endpoint.call("stopUpdates"),
|
|
150
|
+
getUpdates: () => endpoint.call("getUpdates"),
|
|
151
|
+
addRegion: (region) => endpoint.call("addRegion", { region }),
|
|
152
|
+
getRegions: () => endpoint.call("getRegions"),
|
|
153
|
+
removeRegion: (id) => endpoint.call("removeRegion", { id }),
|
|
154
|
+
onLocationUpdate: () => endpoint.stream("onLocationUpdate", {}, "dropOldest"),
|
|
155
|
+
onRegionTransition: () => endpoint.stream("onRegionTransition"),
|
|
156
|
+
onVisit: () => endpoint.stream("onVisit"),
|
|
157
|
+
onLocationError: () => endpoint.stream("onLocationError"),
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
declare global {
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace -- events are declared by merging into Tokamak.Events.
|
|
163
|
+
namespace Tokamak {
|
|
164
|
+
interface Events {
|
|
165
|
+
"location.apple.update": LocationUpdate;
|
|
166
|
+
"location.apple.region": RegionTransition;
|
|
167
|
+
"location.apple.visit": Visit;
|
|
168
|
+
"location.apple.error": LocationErrorEvent;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Receives continuous updates and significant changes, including those that launch the app. */
|
|
174
|
+
export const onUpdate = defineEvent("location.apple.update", { platforms: ["ios", "macos"] });
|
|
175
|
+
|
|
176
|
+
/** Receives each region entry or exit, including one that launches the app. */
|
|
177
|
+
export const onRegion = defineEvent("location.apple.region", { platforms: ["ios", "macos"] });
|
|
178
|
+
|
|
179
|
+
/** Receives each arrival at or departure from a place, including one that launches the app. */
|
|
180
|
+
export const onVisit = defineEvent("location.apple.visit", { platforms: ["ios", "macos"] });
|
|
181
|
+
|
|
182
|
+
/** Receives failures of background services, such as a region Core Location cannot monitor. */
|
|
183
|
+
export const onError = defineEvent("location.apple.error", { platforms: ["ios", "macos"] });
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Listener } from "@tokamakdev/plugin/events";
|
|
2
|
+
|
|
3
|
+
import * as android from "./android.generated.js";
|
|
4
|
+
import * as apple from "./apple.generated.js";
|
|
5
|
+
import type { LocationErrorEvent, LocationUpdate, RegionTransition, Visit } from "./types.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Receives the positions of continuous updates and significant changes in native builds: on
|
|
9
|
+
* screen, off screen, or after the system launches the app for them.
|
|
10
|
+
*/
|
|
11
|
+
export function onLocationUpdate(listener: Listener<LocationUpdate>): void {
|
|
12
|
+
apple.onUpdate(listener);
|
|
13
|
+
android.onUpdate(listener);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Receives each region entry or exit, including one that launches the app. */
|
|
17
|
+
export function onRegionTransition(listener: Listener<RegionTransition>): void {
|
|
18
|
+
apple.onRegion(listener);
|
|
19
|
+
android.onRegion(listener);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Apple only: receives each arrival at or departure from a place, including one that launches the app. */
|
|
23
|
+
export function onVisit(listener: Listener<Visit>): void {
|
|
24
|
+
apple.onVisit(listener);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Receives failures of background location services, such as a region the system cannot monitor. */
|
|
28
|
+
export function onLocationError(listener: Listener<LocationErrorEvent>): void {
|
|
29
|
+
apple.onError(listener);
|
|
30
|
+
android.onError(listener);
|
|
31
|
+
}
|