2015-03-01 04:46:42 +00:00
|
|
|
/**
|
2015-03-23 20:35:08 +00:00
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
2015-03-01 04:46:42 +00:00
|
|
|
*
|
2015-03-09 10:04:44 +00:00
|
|
|
* @providesModule Geolocation
|
2015-03-25 18:12:57 +00:00
|
|
|
* @flow
|
2015-03-01 04:46:42 +00:00
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-05-25 11:17:35 +00:00
|
|
|
const NativeEventEmitter = require('NativeEventEmitter');
|
|
|
|
const RCTLocationObserver = require('NativeModules').LocationObserver;
|
2015-03-01 04:46:42 +00:00
|
|
|
|
2016-05-25 11:17:35 +00:00
|
|
|
const invariant = require('fbjs/lib/invariant');
|
|
|
|
const logError = require('logError');
|
|
|
|
const warning = require('fbjs/lib/warning');
|
2015-03-01 04:46:42 +00:00
|
|
|
|
2016-05-25 11:17:35 +00:00
|
|
|
const LocationEventEmitter = new NativeEventEmitter(RCTLocationObserver);
|
2016-05-24 19:33:57 +00:00
|
|
|
|
2017-06-06 01:58:37 +00:00
|
|
|
const Platform = require('Platform');
|
|
|
|
const PermissionsAndroid = require('PermissionsAndroid');
|
|
|
|
|
2016-05-25 11:17:35 +00:00
|
|
|
var subscriptions = [];
|
2015-03-01 04:46:42 +00:00
|
|
|
var updatesEnabled = false;
|
|
|
|
|
2015-04-24 23:10:46 +00:00
|
|
|
type GeoOptions = {
|
2016-08-09 13:32:41 +00:00
|
|
|
timeout: number,
|
|
|
|
maximumAge: number,
|
|
|
|
enableHighAccuracy: bool,
|
|
|
|
distanceFilter: number,
|
2015-04-24 23:10:46 +00:00
|
|
|
}
|
|
|
|
|
2015-03-01 04:46:42 +00:00
|
|
|
/**
|
2016-09-20 12:46:59 +00:00
|
|
|
* The Geolocation API extends the web spec:
|
2015-11-18 17:03:51 +00:00
|
|
|
* https://developer.mozilla.org/en-US/docs/Web/API/Geolocation
|
|
|
|
*
|
2016-09-09 12:28:34 +00:00
|
|
|
* As a browser polyfill, this API is available through the `navigator.geolocation`
|
|
|
|
* global - you do not need to `import` it.
|
|
|
|
*
|
2017-04-26 14:09:36 +00:00
|
|
|
* ### Configuration and Permissions
|
|
|
|
*
|
|
|
|
* <div class="banner-crna-ejected">
|
|
|
|
* <h3>Projects with Native Code Only</h3>
|
|
|
|
* <p>
|
|
|
|
* This section only applies to projects made with <code>react-native init</code>
|
|
|
|
* or to those made with Create React Native App which have since ejected. For
|
|
|
|
* more information about ejecting, please see
|
|
|
|
* the <a href="https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md" target="_blank">guide</a> on
|
|
|
|
* the Create React Native App repository.
|
|
|
|
* </p>
|
|
|
|
* </div>
|
|
|
|
*
|
|
|
|
* #### iOS
|
2015-04-09 04:47:06 +00:00
|
|
|
* You need to include the `NSLocationWhenInUseUsageDescription` key
|
2016-11-14 19:44:49 +00:00
|
|
|
* in Info.plist to enable geolocation when using the app. Geolocation is
|
|
|
|
* enabled by default when you create a project with `react-native init`.
|
|
|
|
*
|
|
|
|
* In order to enable geolocation in the background, you need to include the
|
|
|
|
* 'NSLocationAlwaysUsageDescription' key in Info.plist and add location as
|
|
|
|
* a background mode in the 'Capabilities' tab in Xcode.
|
2015-03-01 04:46:42 +00:00
|
|
|
*
|
2017-04-26 14:09:36 +00:00
|
|
|
* #### Android
|
2015-11-18 17:03:51 +00:00
|
|
|
* To request access to location, you need to add the following line to your
|
|
|
|
* app's `AndroidManifest.xml`:
|
|
|
|
*
|
|
|
|
* `<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />`
|
2015-11-20 20:43:07 +00:00
|
|
|
*
|
2016-09-20 12:46:59 +00:00
|
|
|
* Android API >= 18 Positions will also contain a `mocked` boolean to indicate if position
|
|
|
|
* was created from a mock provider.
|
|
|
|
*
|
2015-03-01 04:46:42 +00:00
|
|
|
*/
|
2015-03-09 10:04:44 +00:00
|
|
|
var Geolocation = {
|
|
|
|
|
2017-05-25 14:01:17 +00:00
|
|
|
/*
|
|
|
|
* Request suitable Location permission based on the key configured on pList.
|
|
|
|
* If NSLocationAlwaysUsageDescription is set, it will request Always authorization,
|
|
|
|
* although if NSLocationWhenInUseUsageDescription is set, it will request InUse
|
|
|
|
* authorization.
|
|
|
|
*/
|
|
|
|
requestAuthorization: function() {
|
|
|
|
RCTLocationObserver.requestAuthorization();
|
|
|
|
},
|
|
|
|
|
2015-04-24 23:10:46 +00:00
|
|
|
/*
|
|
|
|
* Invokes the success callback once with the latest location info. Supported
|
|
|
|
* options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool)
|
2017-01-31 19:52:40 +00:00
|
|
|
* On Android, if the location is cached this can return almost immediately,
|
|
|
|
* or it will request an update which might take a while.
|
2015-04-24 23:10:46 +00:00
|
|
|
*/
|
2017-06-06 01:58:37 +00:00
|
|
|
getCurrentPosition: async function(
|
2015-03-25 18:12:57 +00:00
|
|
|
geo_success: Function,
|
|
|
|
geo_error?: Function,
|
2015-04-24 23:10:46 +00:00
|
|
|
geo_options?: GeoOptions
|
2015-03-26 17:06:50 +00:00
|
|
|
) {
|
2015-03-01 04:46:42 +00:00
|
|
|
invariant(
|
|
|
|
typeof geo_success === 'function',
|
|
|
|
'Must provide a valid geo_success callback.'
|
|
|
|
);
|
2017-06-06 01:58:37 +00:00
|
|
|
let hasPermission = true;
|
|
|
|
// Supports Android's new permission model. For Android older devices,
|
|
|
|
// it's always on.
|
|
|
|
if (Platform.OS === 'android' && Platform.Version >= 23) {
|
|
|
|
hasPermission = await PermissionsAndroid.check(
|
|
|
|
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
|
|
|
|
);
|
|
|
|
if (!hasPermission) {
|
|
|
|
const status = await PermissionsAndroid.request(
|
|
|
|
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
|
|
|
|
);
|
|
|
|
hasPermission = status === PermissionsAndroid.RESULTS.GRANTED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (hasPermission) {
|
|
|
|
RCTLocationObserver.getCurrentPosition(
|
|
|
|
geo_options || {},
|
|
|
|
geo_success,
|
|
|
|
geo_error || logError,
|
|
|
|
);
|
|
|
|
}
|
2015-03-09 10:04:44 +00:00
|
|
|
},
|
|
|
|
|
2015-04-24 23:10:46 +00:00
|
|
|
/*
|
|
|
|
* Invokes the success callback whenever the location changes. Supported
|
2016-02-06 00:54:14 +00:00
|
|
|
* options: timeout (ms), maximumAge (ms), enableHighAccuracy (bool), distanceFilter(m)
|
2015-04-24 23:10:46 +00:00
|
|
|
*/
|
|
|
|
watchPosition: function(success: Function, error?: Function, options?: GeoOptions): number {
|
2015-03-09 10:04:44 +00:00
|
|
|
if (!updatesEnabled) {
|
|
|
|
RCTLocationObserver.startObserving(options || {});
|
|
|
|
updatesEnabled = true;
|
|
|
|
}
|
2015-03-01 04:46:42 +00:00
|
|
|
var watchID = subscriptions.length;
|
2015-03-09 10:04:44 +00:00
|
|
|
subscriptions.push([
|
2016-05-25 11:17:35 +00:00
|
|
|
LocationEventEmitter.addListener(
|
2015-03-09 10:04:44 +00:00
|
|
|
'geolocationDidChange',
|
|
|
|
success
|
|
|
|
),
|
2016-05-25 11:17:35 +00:00
|
|
|
error ? LocationEventEmitter.addListener(
|
2015-03-09 10:04:44 +00:00
|
|
|
'geolocationError',
|
|
|
|
error
|
|
|
|
) : null,
|
|
|
|
]);
|
2015-03-01 04:46:42 +00:00
|
|
|
return watchID;
|
2015-03-09 10:04:44 +00:00
|
|
|
},
|
|
|
|
|
2015-03-25 18:12:57 +00:00
|
|
|
clearWatch: function(watchID: number) {
|
2015-03-01 04:46:42 +00:00
|
|
|
var sub = subscriptions[watchID];
|
|
|
|
if (!sub) {
|
|
|
|
// Silently exit when the watchID is invalid or already cleared
|
|
|
|
// This is consistent with timers
|
|
|
|
return;
|
|
|
|
}
|
2015-03-25 18:12:57 +00:00
|
|
|
|
2015-03-09 10:04:44 +00:00
|
|
|
sub[0].remove();
|
2015-03-25 18:12:57 +00:00
|
|
|
// array element refinements not yet enabled in Flow
|
|
|
|
var sub1 = sub[1]; sub1 && sub1.remove();
|
2015-03-01 04:46:42 +00:00
|
|
|
subscriptions[watchID] = undefined;
|
|
|
|
var noWatchers = true;
|
|
|
|
for (var ii = 0; ii < subscriptions.length; ii++) {
|
|
|
|
if (subscriptions[ii]) {
|
|
|
|
noWatchers = false; // still valid subscriptions
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (noWatchers) {
|
2015-03-09 10:04:44 +00:00
|
|
|
Geolocation.stopObserving();
|
2015-03-01 04:46:42 +00:00
|
|
|
}
|
2015-03-09 10:04:44 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
stopObserving: function() {
|
2015-03-01 04:46:42 +00:00
|
|
|
if (updatesEnabled) {
|
|
|
|
RCTLocationObserver.stopObserving();
|
|
|
|
updatesEnabled = false;
|
|
|
|
for (var ii = 0; ii < subscriptions.length; ii++) {
|
2015-03-25 18:12:57 +00:00
|
|
|
var sub = subscriptions[ii];
|
|
|
|
if (sub) {
|
2017-06-22 15:34:16 +00:00
|
|
|
warning(false, 'Called stopObserving with existing subscriptions.');
|
2015-03-25 18:12:57 +00:00
|
|
|
sub[0].remove();
|
|
|
|
// array element refinements not yet enabled in Flow
|
|
|
|
var sub1 = sub[1]; sub1 && sub1.remove();
|
2015-03-01 04:46:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
subscriptions = [];
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 20:43:46 +00:00
|
|
|
};
|
2015-03-01 04:46:42 +00:00
|
|
|
|
2015-03-09 10:04:44 +00:00
|
|
|
module.exports = Geolocation;
|