@symbiote-native/location 0.1.0

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.
Files changed (40) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/build/angular/index.d.ts +1 -0
  4. package/build/angular/index.js +4 -0
  5. package/build/core/index.d.ts +5 -0
  6. package/build/core/index.js +5 -0
  7. package/build/core/location.d.ts +39 -0
  8. package/build/core/location.js +204 -0
  9. package/build/core/native-module.d.ts +31 -0
  10. package/build/core/native-module.js +3 -0
  11. package/build/core/subscribers.d.ts +23 -0
  12. package/build/core/subscribers.js +65 -0
  13. package/build/core/types.d.ts +160 -0
  14. package/build/core/types.js +44 -0
  15. package/build-ngc/angular/index.d.ts +1 -0
  16. package/build-ngc/angular/index.js +5 -0
  17. package/build-ngc/angular/index.js.map +1 -0
  18. package/build-ngc/core/index.d.ts +5 -0
  19. package/build-ngc/core/index.js +6 -0
  20. package/build-ngc/core/index.js.map +1 -0
  21. package/build-ngc/core/location.d.ts +39 -0
  22. package/build-ngc/core/location.js +205 -0
  23. package/build-ngc/core/location.js.map +1 -0
  24. package/build-ngc/core/native-module.d.ts +31 -0
  25. package/build-ngc/core/native-module.js +4 -0
  26. package/build-ngc/core/native-module.js.map +1 -0
  27. package/build-ngc/core/subscribers.d.ts +23 -0
  28. package/build-ngc/core/subscribers.js +66 -0
  29. package/build-ngc/core/subscribers.js.map +1 -0
  30. package/build-ngc/core/types.d.ts +160 -0
  31. package/build-ngc/core/types.js +45 -0
  32. package/build-ngc/core/types.js.map +1 -0
  33. package/native-link.json +40 -0
  34. package/package.json +151 -0
  35. package/src/angular/index.ts +4 -0
  36. package/src/core/index.ts +17 -0
  37. package/src/core/location.ts +313 -0
  38. package/src/core/native-module.ts +62 -0
  39. package/src/core/subscribers.ts +101 -0
  40. package/src/core/types.ts +191 -0
@@ -0,0 +1,313 @@
1
+ import { createPermissionHook, Platform } from 'expo-modules-core';
2
+ import { expoLocation } from './native-module';
3
+ import {
4
+ headingSubscriber,
5
+ locationErrorSubscriber,
6
+ locationSubscriber,
7
+ motionActivitySubscriber,
8
+ } from './subscribers';
9
+ import { LocationAccuracy } from './types';
10
+ import type {
11
+ ILocationCallback,
12
+ ILocationErrorCallback,
13
+ ILocationGeocodedAddress,
14
+ ILocationGeocodedLocation,
15
+ ILocationHeadingCallback,
16
+ ILocationHeadingObject,
17
+ ILocationLastKnownOptions,
18
+ ILocationObject,
19
+ ILocationOptions,
20
+ ILocationPermissionResponse,
21
+ ILocationProviderStatus,
22
+ ILocationRegion,
23
+ ILocationSubscription,
24
+ ILocationTaskOptions,
25
+ IMotionActivityCallback,
26
+ IMotionActivityObject,
27
+ PermissionResponse,
28
+ } from './types';
29
+
30
+ export async function getProviderStatusAsync(): Promise<ILocationProviderStatus> {
31
+ return expoLocation.getProviderStatusAsync();
32
+ }
33
+
34
+ /** @platform android */
35
+ export async function enableNetworkProviderAsync(): Promise<void> {
36
+ if (Platform.OS === 'android') {
37
+ return expoLocation.enableNetworkProviderAsync();
38
+ }
39
+ }
40
+
41
+ export async function getCurrentPositionAsync(
42
+ options: ILocationOptions = {},
43
+ ): Promise<ILocationObject> {
44
+ return expoLocation.getCurrentPositionAsync(options);
45
+ }
46
+
47
+ export async function getLastKnownPositionAsync(
48
+ options: ILocationLastKnownOptions = {},
49
+ ): Promise<ILocationObject | null> {
50
+ return expoLocation.getLastKnownPositionAsync(options);
51
+ }
52
+
53
+ export async function watchPositionAsync(
54
+ options: ILocationOptions,
55
+ callback: ILocationCallback,
56
+ errorHandler?: ILocationErrorCallback,
57
+ ): Promise<ILocationSubscription> {
58
+ const watchId = locationSubscriber.registerCallback(callback);
59
+ if (errorHandler)
60
+ locationErrorSubscriber.registerCallbackForId(watchId, errorHandler);
61
+
62
+ await expoLocation.watchPositionImplAsync(watchId, options);
63
+
64
+ return {
65
+ remove() {
66
+ locationSubscriber.unregisterCallback(watchId);
67
+ if (errorHandler) locationErrorSubscriber.unregisterCallback(watchId);
68
+ },
69
+ };
70
+ }
71
+
72
+ export async function watchHeadingAsync(
73
+ callback: ILocationHeadingCallback,
74
+ errorHandler?: ILocationErrorCallback,
75
+ ): Promise<ILocationSubscription> {
76
+ const watchId = headingSubscriber.registerCallback(callback);
77
+ if (errorHandler)
78
+ locationErrorSubscriber.registerCallbackForId(watchId, errorHandler);
79
+
80
+ await expoLocation.watchDeviceHeading(watchId);
81
+
82
+ return {
83
+ remove() {
84
+ headingSubscriber.unregisterCallback(watchId);
85
+ if (errorHandler) locationErrorSubscriber.unregisterCallback(watchId);
86
+ },
87
+ };
88
+ }
89
+
90
+ /** Waits for a couple of heading updates and returns the first accurate-enough one. */
91
+ export async function getHeadingAsync(): Promise<ILocationHeadingObject> {
92
+ return new Promise((resolve, reject) => {
93
+ let tries = 0;
94
+ let subscriber: ILocationSubscription | undefined;
95
+
96
+ watchHeadingAsync(
97
+ heading => {
98
+ if (heading.accuracy > 1 || tries > 5) {
99
+ subscriber?.remove();
100
+ resolve(heading);
101
+ } else {
102
+ tries += 1;
103
+ }
104
+ },
105
+ reason => {
106
+ subscriber?.remove();
107
+ reject(new Error(reason));
108
+ },
109
+ )
110
+ .then(sub => {
111
+ subscriber = sub;
112
+ })
113
+ .catch(reject);
114
+ });
115
+ }
116
+
117
+ export async function geocodeAsync(
118
+ address: string,
119
+ ): Promise<ILocationGeocodedLocation[]> {
120
+ if (typeof address !== 'string') {
121
+ throw new TypeError(
122
+ `Address to geocode must be a string. Got ${address} instead.`,
123
+ );
124
+ }
125
+ return expoLocation.geocodeAsync(address);
126
+ }
127
+
128
+ export async function reverseGeocodeAsync(
129
+ location: Pick<ILocationGeocodedLocation, 'latitude' | 'longitude'>,
130
+ ): Promise<ILocationGeocodedAddress[]> {
131
+ if (
132
+ typeof location.latitude !== 'number' ||
133
+ typeof location.longitude !== 'number'
134
+ ) {
135
+ throw new TypeError(
136
+ 'Location to reverse-geocode must be an object with number properties `latitude` and `longitude`.',
137
+ );
138
+ }
139
+ return expoLocation.reverseGeocodeAsync(location);
140
+ }
141
+
142
+ export async function getForegroundPermissionsAsync(): Promise<ILocationPermissionResponse> {
143
+ return expoLocation.getForegroundPermissionsAsync();
144
+ }
145
+
146
+ export async function requestForegroundPermissionsAsync(): Promise<ILocationPermissionResponse> {
147
+ return expoLocation.requestForegroundPermissionsAsync();
148
+ }
149
+
150
+ export const useForegroundPermissions = createPermissionHook({
151
+ getMethod: getForegroundPermissionsAsync,
152
+ requestMethod: requestForegroundPermissionsAsync,
153
+ });
154
+
155
+ export async function getBackgroundPermissionsAsync(): Promise<PermissionResponse> {
156
+ return expoLocation.getBackgroundPermissionsAsync();
157
+ }
158
+
159
+ export async function requestBackgroundPermissionsAsync(): Promise<PermissionResponse> {
160
+ return expoLocation.requestBackgroundPermissionsAsync();
161
+ }
162
+
163
+ export const useBackgroundPermissions = createPermissionHook({
164
+ getMethod: getBackgroundPermissionsAsync,
165
+ requestMethod: requestBackgroundPermissionsAsync,
166
+ });
167
+
168
+ export async function hasServicesEnabledAsync(): Promise<boolean> {
169
+ return expoLocation.hasServicesEnabledAsync();
170
+ }
171
+
172
+ export async function getMotionActivityPermissionsAsync(): Promise<PermissionResponse> {
173
+ return expoLocation.getMotionActivityPermissionsAsync();
174
+ }
175
+
176
+ export async function requestMotionActivityPermissionsAsync(): Promise<PermissionResponse> {
177
+ return expoLocation.requestMotionActivityPermissionsAsync();
178
+ }
179
+
180
+ export const useMotionActivityPermissions = createPermissionHook({
181
+ getMethod: getMotionActivityPermissionsAsync,
182
+ requestMethod: requestMotionActivityPermissionsAsync,
183
+ });
184
+
185
+ /** No location permission required — uses Play Services activity recognition / the iOS motion
186
+ * coprocessor directly. */
187
+ export async function getMotionActivityAsync(): Promise<IMotionActivityObject> {
188
+ return new Promise((resolve, reject) => {
189
+ let subscriber: ILocationSubscription | undefined;
190
+ watchMotionActivityAsync(
191
+ activity => {
192
+ subscriber?.remove();
193
+ resolve(activity);
194
+ },
195
+ reason => {
196
+ subscriber?.remove();
197
+ reject(new Error(reason));
198
+ },
199
+ )
200
+ .then(sub => {
201
+ subscriber = sub;
202
+ })
203
+ .catch(reject);
204
+ });
205
+ }
206
+
207
+ /** Foreground-only — updates pause while the app is backgrounded. */
208
+ export async function watchMotionActivityAsync(
209
+ callback: IMotionActivityCallback,
210
+ errorHandler?: ILocationErrorCallback,
211
+ ): Promise<ILocationSubscription> {
212
+ const watchId = motionActivitySubscriber.registerCallback(callback);
213
+ if (errorHandler)
214
+ locationErrorSubscriber.registerCallbackForId(watchId, errorHandler);
215
+
216
+ await expoLocation.watchMotionActivityImplAsync(watchId);
217
+
218
+ return {
219
+ remove() {
220
+ motionActivitySubscriber.unregisterCallback(watchId);
221
+ // forgetCallback, not unregisterCallback: the native watch was already torn down above by
222
+ // motionActivitySubscriber; re-issuing removeWatchAsync for the same id would hit this
223
+ // native module's location-permission guard and throw for a motion-activity-only caller.
224
+ if (errorHandler) locationErrorSubscriber.forgetCallback(watchId);
225
+ },
226
+ };
227
+ }
228
+
229
+ function assertValidTaskName(taskName: unknown): asserts taskName is string {
230
+ if (!taskName || typeof taskName !== 'string') {
231
+ throw new Error(
232
+ `\`taskName\` must be a non-empty string. Got ${taskName} instead.`,
233
+ );
234
+ }
235
+ }
236
+
237
+ function assertValidRegions(regions: ILocationRegion[]): void {
238
+ if (!regions || regions.length === 0) {
239
+ throw new Error(
240
+ 'Regions array cannot be empty. Use `stopGeofencingAsync` if you want to stop geofencing all regions',
241
+ );
242
+ }
243
+ for (const region of regions) {
244
+ if (typeof region.latitude !== 'number') {
245
+ throw new TypeError(
246
+ `Region's latitude must be a number. Got '${region.latitude}' instead.`,
247
+ );
248
+ }
249
+ if (typeof region.longitude !== 'number') {
250
+ throw new TypeError(
251
+ `Region's longitude must be a number. Got '${region.longitude}' instead.`,
252
+ );
253
+ }
254
+ if (typeof region.radius !== 'number') {
255
+ throw new TypeError(
256
+ `Region's radius must be a number. Got '${region.radius}' instead.`,
257
+ );
258
+ }
259
+ }
260
+ }
261
+
262
+ export async function isBackgroundLocationAvailableAsync(): Promise<boolean> {
263
+ const providerStatus = await getProviderStatusAsync();
264
+ return providerStatus.backgroundModeEnabled;
265
+ }
266
+
267
+ /** Register a task (defined with `@symbiote-native/task-manager`'s `defineTask`) to keep
268
+ * receiving location updates while the app is backgrounded. */
269
+ export async function startLocationUpdatesAsync(
270
+ taskName: string,
271
+ options: ILocationTaskOptions = { accuracy: LocationAccuracy.Balanced },
272
+ ): Promise<void> {
273
+ assertValidTaskName(taskName);
274
+ await expoLocation.startLocationUpdatesAsync(taskName, options);
275
+ }
276
+
277
+ export async function stopLocationUpdatesAsync(
278
+ taskName: string,
279
+ ): Promise<void> {
280
+ assertValidTaskName(taskName);
281
+ await expoLocation.stopLocationUpdatesAsync(taskName);
282
+ }
283
+
284
+ export async function hasStartedLocationUpdatesAsync(
285
+ taskName: string,
286
+ ): Promise<boolean> {
287
+ assertValidTaskName(taskName);
288
+ return expoLocation.hasStartedLocationUpdatesAsync(taskName);
289
+ }
290
+
291
+ /** Register a task (defined with `@symbiote-native/task-manager`'s `defineTask`) to receive
292
+ * enter/exit events for the given regions, including while the app is backgrounded. Calling
293
+ * this again with a new region array replaces the regions already being watched. */
294
+ export async function startGeofencingAsync(
295
+ taskName: string,
296
+ regions: ILocationRegion[] = [],
297
+ ): Promise<void> {
298
+ assertValidTaskName(taskName);
299
+ assertValidRegions(regions);
300
+ await expoLocation.startGeofencingAsync(taskName, { regions });
301
+ }
302
+
303
+ export async function stopGeofencingAsync(taskName: string): Promise<void> {
304
+ assertValidTaskName(taskName);
305
+ await expoLocation.stopGeofencingAsync(taskName);
306
+ }
307
+
308
+ export async function hasStartedGeofencingAsync(
309
+ taskName: string,
310
+ ): Promise<boolean> {
311
+ assertValidTaskName(taskName);
312
+ return expoLocation.hasStartedGeofencingAsync(taskName);
313
+ }
@@ -0,0 +1,62 @@
1
+ import { requireNativeModule, type EventSubscription } from 'expo-modules-core';
2
+ import type {
3
+ ILocationGeocodedAddress,
4
+ ILocationGeocodedLocation,
5
+ ILocationLastKnownOptions,
6
+ ILocationObject,
7
+ ILocationOptions,
8
+ ILocationPermissionResponse,
9
+ ILocationProviderStatus,
10
+ ILocationRegion,
11
+ ILocationTaskOptions,
12
+ PermissionResponse,
13
+ } from './types';
14
+
15
+ const EXPO_LOCATION_MODULE_NAME = 'ExpoLocation';
16
+
17
+ export type INativeLocationModule = {
18
+ addListener(
19
+ eventName: string,
20
+ listener: (event: unknown) => void,
21
+ ): EventSubscription;
22
+ getProviderStatusAsync(): Promise<ILocationProviderStatus>;
23
+ enableNetworkProviderAsync(): Promise<void>;
24
+ getCurrentPositionAsync(options: ILocationOptions): Promise<ILocationObject>;
25
+ getLastKnownPositionAsync(
26
+ options: ILocationLastKnownOptions,
27
+ ): Promise<ILocationObject | null>;
28
+ watchPositionImplAsync(
29
+ watchId: number,
30
+ options: ILocationOptions,
31
+ ): Promise<void>;
32
+ watchDeviceHeading(watchId: number): Promise<void>;
33
+ removeWatchAsync(watchId: number): Promise<void>;
34
+ geocodeAsync(address: string): Promise<ILocationGeocodedLocation[]>;
35
+ reverseGeocodeAsync(
36
+ location: Pick<ILocationGeocodedLocation, 'latitude' | 'longitude'>,
37
+ ): Promise<ILocationGeocodedAddress[]>;
38
+ getForegroundPermissionsAsync(): Promise<ILocationPermissionResponse>;
39
+ requestForegroundPermissionsAsync(): Promise<ILocationPermissionResponse>;
40
+ getBackgroundPermissionsAsync(): Promise<PermissionResponse>;
41
+ requestBackgroundPermissionsAsync(): Promise<PermissionResponse>;
42
+ hasServicesEnabledAsync(): Promise<boolean>;
43
+ getMotionActivityPermissionsAsync(): Promise<PermissionResponse>;
44
+ requestMotionActivityPermissionsAsync(): Promise<PermissionResponse>;
45
+ watchMotionActivityImplAsync(watchId: number): Promise<void>;
46
+ startLocationUpdatesAsync(
47
+ taskName: string,
48
+ options: ILocationTaskOptions,
49
+ ): Promise<void>;
50
+ stopLocationUpdatesAsync(taskName: string): Promise<void>;
51
+ hasStartedLocationUpdatesAsync(taskName: string): Promise<boolean>;
52
+ startGeofencingAsync(
53
+ taskName: string,
54
+ options: { regions: ILocationRegion[] },
55
+ ): Promise<void>;
56
+ stopGeofencingAsync(taskName: string): Promise<void>;
57
+ hasStartedGeofencingAsync(taskName: string): Promise<boolean>;
58
+ };
59
+
60
+ export const expoLocation = requireNativeModule<INativeLocationModule>(
61
+ EXPO_LOCATION_MODULE_NAME,
62
+ );
@@ -0,0 +1,101 @@
1
+ import type { EventSubscription } from 'expo-modules-core';
2
+ import { expoLocation } from './native-module';
3
+ import type {
4
+ ILocationCallback,
5
+ ILocationErrorCallback,
6
+ ILocationHeadingCallback,
7
+ IMotionActivityCallback,
8
+ } from './types';
9
+
10
+ type IEventObject = { watchId: number; [key: string]: unknown };
11
+
12
+ let nextWatchId = 0;
13
+
14
+ class Subscriber<
15
+ TCallback extends
16
+ | ILocationCallback
17
+ | ILocationHeadingCallback
18
+ | ILocationErrorCallback
19
+ | IMotionActivityCallback,
20
+ > {
21
+ private callbacks: Record<number, TCallback> = {};
22
+ private eventSubscription: EventSubscription | null = null;
23
+
24
+ constructor(
25
+ private eventName: string,
26
+ private eventDataField: string,
27
+ ) {}
28
+
29
+ private maybeInitializeSubscription(): void {
30
+ if (this.eventSubscription) return;
31
+ this.eventSubscription = expoLocation.addListener(
32
+ this.eventName,
33
+ (event: unknown) => this.trigger(event as IEventObject),
34
+ );
35
+ }
36
+
37
+ registerCallback(callback: TCallback): number {
38
+ this.maybeInitializeSubscription();
39
+ const id = ++nextWatchId;
40
+ this.callbacks[id] = callback;
41
+ return id;
42
+ }
43
+
44
+ registerCallbackForId(watchId: number, callback: TCallback): number {
45
+ this.maybeInitializeSubscription();
46
+ this.callbacks[watchId] = callback;
47
+ return watchId;
48
+ }
49
+
50
+ unregisterCallback(id: number): void {
51
+ if (!this.callbacks[id]) return;
52
+ delete this.callbacks[id];
53
+ void expoLocation.removeWatchAsync(id);
54
+ this.releaseSubscriptionIfIdle();
55
+ }
56
+
57
+ /** Drops the local callback without calling native removeWatchAsync — used when another
58
+ * subscriber already tore down the same watchId natively. */
59
+ forgetCallback(id: number): void {
60
+ if (!this.callbacks[id]) return;
61
+ delete this.callbacks[id];
62
+ this.releaseSubscriptionIfIdle();
63
+ }
64
+
65
+ private releaseSubscriptionIfIdle(): void {
66
+ if (Object.keys(this.callbacks).length === 0 && this.eventSubscription) {
67
+ this.eventSubscription.remove();
68
+ this.eventSubscription = null;
69
+ }
70
+ }
71
+
72
+ private trigger(event: IEventObject): void {
73
+ const callback = this.callbacks[event.watchId];
74
+ if (callback) {
75
+ (callback as (arg: unknown) => unknown)(event[this.eventDataField]);
76
+ } else {
77
+ void expoLocation.removeWatchAsync(event.watchId);
78
+ }
79
+ }
80
+ }
81
+
82
+ export const locationSubscriber = new Subscriber<ILocationCallback>(
83
+ 'Expo.locationChanged',
84
+ 'location',
85
+ );
86
+ export const headingSubscriber = new Subscriber<ILocationHeadingCallback>(
87
+ 'Expo.headingChanged',
88
+ 'heading',
89
+ );
90
+ export const locationErrorSubscriber = new Subscriber<ILocationErrorCallback>(
91
+ 'Expo.locationError',
92
+ 'reason',
93
+ );
94
+ export const motionActivitySubscriber = new Subscriber<IMotionActivityCallback>(
95
+ 'Expo.motionActivityChanged',
96
+ 'activity',
97
+ );
98
+
99
+ export function getCurrentWatchId(): number {
100
+ return nextWatchId;
101
+ }
@@ -0,0 +1,191 @@
1
+ import type { PermissionResponse } from 'expo-modules-core';
2
+
3
+ export enum LocationAccuracy {
4
+ Lowest = 1,
5
+ Low = 2,
6
+ Balanced = 3,
7
+ High = 4,
8
+ Highest = 5,
9
+ BestForNavigation = 6,
10
+ }
11
+
12
+ export enum LocationActivityType {
13
+ Other = 1,
14
+ AutomotiveNavigation = 2,
15
+ Fitness = 3,
16
+ OtherNavigation = 4,
17
+ /** @platform ios */
18
+ Airborne = 5,
19
+ }
20
+
21
+ export enum LocationGeofencingEventType {
22
+ Enter = 1,
23
+ Exit = 2,
24
+ }
25
+
26
+ export enum LocationGeofencingRegionState {
27
+ Unknown = 0,
28
+ Inside = 1,
29
+ Outside = 2,
30
+ }
31
+
32
+ export type ILocationOptions = {
33
+ accuracy?: LocationAccuracy;
34
+ /** @platform android */
35
+ mayShowUserSettingsDialog?: boolean;
36
+ /** @platform android */
37
+ timeInterval?: number;
38
+ distanceInterval?: number;
39
+ };
40
+
41
+ export type ILocationLastKnownOptions = {
42
+ maxAge?: number;
43
+ requiredAccuracy?: number;
44
+ };
45
+
46
+ export type ILocationTaskServiceOptions = {
47
+ notificationTitle: string;
48
+ notificationBody: string;
49
+ notificationColor?: string;
50
+ killServiceOnDestroy?: boolean;
51
+ };
52
+
53
+ export type ILocationTaskOptions = ILocationOptions & {
54
+ /** @platform ios */
55
+ showsBackgroundLocationIndicator?: boolean;
56
+ deferredUpdatesDistance?: number;
57
+ deferredUpdatesTimeout?: number;
58
+ deferredUpdatesInterval?: number;
59
+ /** @platform ios */
60
+ activityType?: LocationActivityType;
61
+ /** @platform ios */
62
+ pausesUpdatesAutomatically?: boolean;
63
+ foregroundService?: ILocationTaskServiceOptions;
64
+ };
65
+
66
+ export type ILocationRegion = {
67
+ identifier?: string;
68
+ latitude: number;
69
+ longitude: number;
70
+ radius: number;
71
+ /** @default true */
72
+ notifyOnEnter?: boolean;
73
+ /** @default true */
74
+ notifyOnExit?: boolean;
75
+ state?: LocationGeofencingRegionState;
76
+ };
77
+
78
+ export type ILocationObjectCoords = {
79
+ latitude: number;
80
+ longitude: number;
81
+ altitude: number | null;
82
+ accuracy: number | null;
83
+ altitudeAccuracy: number | null;
84
+ heading: number | null;
85
+ speed: number | null;
86
+ };
87
+
88
+ export type ILocationObject = {
89
+ coords: ILocationObjectCoords;
90
+ timestamp: number;
91
+ /** @platform android */
92
+ mocked?: boolean;
93
+ };
94
+
95
+ export type ILocationCallback = (location: ILocationObject) => unknown;
96
+ export type ILocationErrorCallback = (reason: string) => void;
97
+
98
+ export type ILocationProviderStatus = {
99
+ locationServicesEnabled: boolean;
100
+ backgroundModeEnabled: boolean;
101
+ /** @platform android */
102
+ gpsAvailable?: boolean;
103
+ /** @platform android */
104
+ networkAvailable?: boolean;
105
+ /** @platform android */
106
+ passiveAvailable?: boolean;
107
+ };
108
+
109
+ export type ILocationHeadingObject = {
110
+ trueHeading: number;
111
+ magHeading: number;
112
+ /** 0 none, 1 low, 2 medium, 3 high */
113
+ accuracy: number;
114
+ };
115
+
116
+ export type ILocationHeadingCallback = (
117
+ heading: ILocationHeadingObject,
118
+ ) => unknown;
119
+
120
+ export type ILocationGeocodedLocation = {
121
+ latitude: number;
122
+ longitude: number;
123
+ altitude?: number;
124
+ accuracy?: number;
125
+ };
126
+
127
+ export type ILocationGeocodedAddress = {
128
+ city: string | null;
129
+ district: string | null;
130
+ streetNumber: string | null;
131
+ street: string | null;
132
+ region: string | null;
133
+ subregion: string | null;
134
+ country: string | null;
135
+ postalCode: string | null;
136
+ name: string | null;
137
+ isoCountryCode: string | null;
138
+ /** @platform ios */
139
+ timezone: string | null;
140
+ /** @platform android */
141
+ formattedAddress: string | null;
142
+ };
143
+
144
+ export type ILocationSubscription = {
145
+ remove: () => void;
146
+ };
147
+
148
+ export type IPermissionDetailsLocationIOS = {
149
+ scope: 'whenInUse' | 'always' | 'none';
150
+ accuracy: 'full' | 'reduced';
151
+ };
152
+
153
+ export type IPermissionDetailsLocationAndroid = {
154
+ accuracy: 'fine' | 'coarse' | 'none';
155
+ };
156
+
157
+ export type ILocationPermissionResponse = PermissionResponse & {
158
+ ios?: IPermissionDetailsLocationIOS;
159
+ android?: IPermissionDetailsLocationAndroid;
160
+ };
161
+
162
+ export type { PermissionResponse };
163
+
164
+ export enum MotionActivityConfidence {
165
+ Low = 0,
166
+ Medium = 1,
167
+ High = 2,
168
+ }
169
+
170
+ export enum MotionActivityType {
171
+ Automotive = 'automotive',
172
+ Cycling = 'cycling',
173
+ Running = 'running',
174
+ Walking = 'walking',
175
+ Stationary = 'stationary',
176
+ Unknown = 'unknown',
177
+ }
178
+
179
+ export type IMotionActivityState = {
180
+ detected: boolean;
181
+ confidence: MotionActivityConfidence;
182
+ };
183
+
184
+ export type IMotionActivityObject = {
185
+ activities: Record<MotionActivityType, IMotionActivityState>;
186
+ timestamp: number;
187
+ };
188
+
189
+ export type IMotionActivityCallback = (
190
+ activity: IMotionActivityObject,
191
+ ) => unknown;