190 lines
7.7 KiB
JavaScript
190 lines
7.7 KiB
JavaScript
import { Platform } from '@unimodules/core';
|
|
import { PermissionStatus } from 'unimodules-permissions-interface';
|
|
import ExpoLocation from './ExpoLocation';
|
|
import { LocationAccuracy, LocationActivityType, LocationGeofencingEventType, LocationGeofencingRegionState, } from './Location.types';
|
|
import { LocationEventEmitter } from './LocationEventEmitter';
|
|
import { setGoogleApiKey, googleGeocodeAsync, googleReverseGeocodeAsync, } from './LocationGoogleGeocoding';
|
|
import { LocationSubscriber, HeadingSubscriber, _getCurrentWatchId } from './LocationSubscribers';
|
|
export async function getProviderStatusAsync() {
|
|
return ExpoLocation.getProviderStatusAsync();
|
|
}
|
|
export async function enableNetworkProviderAsync() {
|
|
// If network provider is disabled (user's location mode is set to "Device only"),
|
|
// Android's location provider may not give you any results. Use this method in order to ask the user
|
|
// to change the location mode to "High accuracy" which uses Google Play services and enables network provider.
|
|
// `getCurrentPositionAsync` and `watchPositionAsync` are doing it automatically anyway.
|
|
if (Platform.OS === 'android') {
|
|
return ExpoLocation.enableNetworkProviderAsync();
|
|
}
|
|
}
|
|
/**
|
|
* Requests for one-time delivery of the user's current location.
|
|
* Depending on given `accuracy` option it may take some time to resolve,
|
|
* especially when you're inside a building.
|
|
*/
|
|
export async function getCurrentPositionAsync(options = {}) {
|
|
return ExpoLocation.getCurrentPositionAsync(options);
|
|
}
|
|
/**
|
|
* Gets the last known position of the device or `null` if it's not available
|
|
* or doesn't match given requirements such as maximum age or required accuracy.
|
|
* It's considered to be faster than `getCurrentPositionAsync` as it doesn't request for the current location.
|
|
*/
|
|
export async function getLastKnownPositionAsync(options = {}) {
|
|
return ExpoLocation.getLastKnownPositionAsync(options);
|
|
}
|
|
/**
|
|
* Starts watching for location changes.
|
|
* Given callback will be called once the new location is available.
|
|
*/
|
|
export async function watchPositionAsync(options, callback) {
|
|
const watchId = LocationSubscriber.registerCallback(callback);
|
|
await ExpoLocation.watchPositionImplAsync(watchId, options);
|
|
return {
|
|
remove() {
|
|
LocationSubscriber.unregisterCallback(watchId);
|
|
},
|
|
};
|
|
}
|
|
/**
|
|
* Resolves to an object with current heading details.
|
|
* To simplify, it calls `watchHeadingAsync` and waits for a couple of updates
|
|
* and returns the one that is accurate enough.
|
|
*/
|
|
export async function getHeadingAsync() {
|
|
return new Promise(async (resolve) => {
|
|
let tries = 0;
|
|
const subscription = await watchHeadingAsync(heading => {
|
|
if (heading.accuracy > 1 || tries > 5) {
|
|
subscription.remove();
|
|
resolve(heading);
|
|
}
|
|
else {
|
|
tries += 1;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
* Starts watching for heading changes.
|
|
* Given callback will be called once the new heading is available.
|
|
*/
|
|
export async function watchHeadingAsync(callback) {
|
|
const watchId = HeadingSubscriber.registerCallback(callback);
|
|
await ExpoLocation.watchDeviceHeading(watchId);
|
|
return {
|
|
remove() {
|
|
HeadingSubscriber.unregisterCallback(watchId);
|
|
},
|
|
};
|
|
}
|
|
/**
|
|
* Geocodes given address to an array of latitude-longitude coordinates.
|
|
*/
|
|
export async function geocodeAsync(address, options) {
|
|
if (typeof address !== 'string') {
|
|
throw new TypeError(`Address to geocode must be a string. Got ${address} instead.`);
|
|
}
|
|
if (options?.useGoogleMaps || Platform.OS === 'web') {
|
|
return await googleGeocodeAsync(address);
|
|
}
|
|
return await ExpoLocation.geocodeAsync(address);
|
|
}
|
|
/**
|
|
* The opposite behavior of `geocodeAsync` — translates location coordinates to an array of addresses.
|
|
*/
|
|
export async function reverseGeocodeAsync(location, options) {
|
|
if (typeof location.latitude !== 'number' || typeof location.longitude !== 'number') {
|
|
throw new TypeError('Location to reverse-geocode must be an object with number properties `latitude` and `longitude`.');
|
|
}
|
|
if (options?.useGoogleMaps || Platform.OS === 'web') {
|
|
return await googleReverseGeocodeAsync(location);
|
|
}
|
|
return await ExpoLocation.reverseGeocodeAsync(location);
|
|
}
|
|
/**
|
|
* Gets the current state of location permissions.
|
|
*/
|
|
export async function getPermissionsAsync() {
|
|
return await ExpoLocation.getPermissionsAsync();
|
|
}
|
|
/**
|
|
* Requests the user to grant location permissions.
|
|
*/
|
|
export async function requestPermissionsAsync() {
|
|
return await ExpoLocation.requestPermissionsAsync();
|
|
}
|
|
// --- Location service
|
|
/**
|
|
* Returns `true` if the device has location services enabled or `false` otherwise.
|
|
*/
|
|
export async function hasServicesEnabledAsync() {
|
|
return await ExpoLocation.hasServicesEnabledAsync();
|
|
}
|
|
// --- Background location updates
|
|
function _validateTaskName(taskName) {
|
|
if (!taskName || typeof taskName !== 'string') {
|
|
throw new Error(`\`taskName\` must be a non-empty string. Got ${taskName} instead.`);
|
|
}
|
|
}
|
|
export async function isBackgroundLocationAvailableAsync() {
|
|
const providerStatus = await getProviderStatusAsync();
|
|
return providerStatus.backgroundModeEnabled;
|
|
}
|
|
export async function startLocationUpdatesAsync(taskName, options = { accuracy: LocationAccuracy.Balanced }) {
|
|
_validateTaskName(taskName);
|
|
await ExpoLocation.startLocationUpdatesAsync(taskName, options);
|
|
}
|
|
export async function stopLocationUpdatesAsync(taskName) {
|
|
_validateTaskName(taskName);
|
|
await ExpoLocation.stopLocationUpdatesAsync(taskName);
|
|
}
|
|
export async function hasStartedLocationUpdatesAsync(taskName) {
|
|
_validateTaskName(taskName);
|
|
return ExpoLocation.hasStartedLocationUpdatesAsync(taskName);
|
|
}
|
|
// --- Geofencing
|
|
function _validateRegions(regions) {
|
|
if (!regions || regions.length === 0) {
|
|
throw new Error('Regions array cannot be empty. Use `stopGeofencingAsync` if you want to stop geofencing all regions');
|
|
}
|
|
for (const region of regions) {
|
|
if (typeof region.latitude !== 'number') {
|
|
throw new TypeError(`Region's latitude must be a number. Got '${region.latitude}' instead.`);
|
|
}
|
|
if (typeof region.longitude !== 'number') {
|
|
throw new TypeError(`Region's longitude must be a number. Got '${region.longitude}' instead.`);
|
|
}
|
|
if (typeof region.radius !== 'number') {
|
|
throw new TypeError(`Region's radius must be a number. Got '${region.radius}' instead.`);
|
|
}
|
|
}
|
|
}
|
|
export async function startGeofencingAsync(taskName, regions = []) {
|
|
_validateTaskName(taskName);
|
|
_validateRegions(regions);
|
|
await ExpoLocation.startGeofencingAsync(taskName, { regions });
|
|
}
|
|
export async function stopGeofencingAsync(taskName) {
|
|
_validateTaskName(taskName);
|
|
await ExpoLocation.stopGeofencingAsync(taskName);
|
|
}
|
|
export async function hasStartedGeofencingAsync(taskName) {
|
|
_validateTaskName(taskName);
|
|
return ExpoLocation.hasStartedGeofencingAsync(taskName);
|
|
}
|
|
/**
|
|
* @deprecated
|
|
* Deprecated as of SDK39 in favour of `setGoogleApiKey`.
|
|
*/
|
|
export function setApiKey(apiKey) {
|
|
console.warn("Location's method `setApiKey` is deprecated in favor of `setGoogleApiKey`.");
|
|
setGoogleApiKey(apiKey);
|
|
}
|
|
// For internal purposes
|
|
export { LocationEventEmitter as EventEmitter, _getCurrentWatchId };
|
|
// Export as namespaced types.
|
|
export { LocationAccuracy as Accuracy, LocationActivityType as ActivityType, LocationGeofencingEventType as GeofencingEventType, LocationGeofencingRegionState as GeofencingRegionState, PermissionStatus, setGoogleApiKey, };
|
|
export { installWebGeolocationPolyfill } from './GeolocationPolyfill';
|
|
export * from './Location.types';
|
|
//# sourceMappingURL=Location.js.map
|