diff --git a/README.md b/README.md
index 3a00f8d..11021d5 100644
--- a/README.md
+++ b/README.md
@@ -164,6 +164,7 @@ Geolocation.setRNConfiguration(
config: {
skipPermissionRequests: boolean;
authorizationLevel?: 'always' | 'whenInUse' | 'auto';
+ enableBackgroundLocationUpdates?: boolean;
locationProvider?: 'playServices' | 'android' | 'auto';
}
) => void
@@ -173,6 +174,7 @@ Supported options:
* `skipPermissionRequests` (boolean) - Defaults to `false`. If `true`, you must request permissions before using Geolocation APIs.
* `authorizationLevel` (string, iOS-only) - Either `"whenInUse"`, `"always"`, or `"auto"`. Changes whether the user will be asked to give "always" or "when in use" location services permission. Any other value or `auto` will use the default behaviour, where the permission level is based on the contents of your `Info.plist`.
+* `enableBackgroundLocationUpdates` (boolean, iOS-only) - When using `skipPermissionRequests`, toggle wether to automatically enableBackgroundLocationUpdates. Defaults to true.
* `locationProvider` (string, Android-only) - Either `"playServices"`, `"android"`, or `"auto"`. Determines wether to use `Google’s Location Services API` or `Android’s Location API`. The `"auto"` mode defaults to `android`, and falls back to Android's Location API if play services aren't available.
---
diff --git a/android/build.gradle b/android/build.gradle
index a489454..06c8bd1 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -133,7 +133,7 @@ repositories {
dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'
- implementation 'com.google.android.gms:play-services-location:20.0.0'
+ implementation 'com.google.android.gms:play-services-location:21.0.1'
}
if (isNewArchitectureEnabled()) {
diff --git a/android/src/main/java/com/reactnativecommunity/geolocation/PlayServicesLocationManager.java b/android/src/main/java/com/reactnativecommunity/geolocation/PlayServicesLocationManager.java
index 4b9e004..362c7e6 100644
--- a/android/src/main/java/com/reactnativecommunity/geolocation/PlayServicesLocationManager.java
+++ b/android/src/main/java/com/reactnativecommunity/geolocation/PlayServicesLocationManager.java
@@ -49,8 +49,9 @@ public void getCurrentLocationData(ReadableMap options, Callback success, Callba
Activity currentActivity = mReactContext.getCurrentActivity();
if (currentActivity == null) {
- error.invoke(PositionError.buildError(PositionError.ACTIVITY_NULL, "mReactContext.getCurrentActivity() returned null but should be non-null in getCurrentLocationData"));
- return;
+ mSingleLocationCallback = createSingleLocationCallback(success, error);
+ checkLocationSettings(options, mSingleLocationCallback);
+ return;
}
try {
@@ -59,29 +60,7 @@ public void getCurrentLocationData(ReadableMap options, Callback success, Callba
if (location != null && (SystemClock.currentTimeMillis() - location.getTime()) < locationOptions.maximumAge) {
success.invoke(locationToMap(location));
} else {
- mSingleLocationCallback = new LocationCallback() {
- @Override
- public void onLocationResult(LocationResult locationResult) {
- if (locationResult == null) {
- emitError(PositionError.POSITION_UNAVAILABLE, "No location provided (FusedLocationProvider/lastLocation).");
- return;
- }
-
- AndroidLocationManager.LocationOptions locationOptions = AndroidLocationManager.LocationOptions.fromReactMap(options);
- Location location = locationResult.getLastLocation();
- success.invoke(locationToMap(location));
-
- mFusedLocationClient.removeLocationUpdates(mSingleLocationCallback);
- mSingleLocationCallback = null;
- }
-
- @Override
- public void onLocationAvailability(LocationAvailability locationAvailability) {
- if (!locationAvailability.isLocationAvailable()) {
- emitError(PositionError.POSITION_UNAVAILABLE, "Location not available (FusedLocationProvider/lastLocation).");
- }
- }
- };
+ mSingleLocationCallback = createSingleLocationCallback(success, error);
checkLocationSettings(options, mSingleLocationCallback);
}
});
@@ -151,4 +130,29 @@ private void requestLocationUpdates(LocationRequest locationRequest, LocationCal
throw e;
}
}
+
+ private LocationCallback createSingleLocationCallback(Callback success, Callback error){
+ return new LocationCallback() {
+ @Override
+ public void onLocationResult(LocationResult locationResult) {
+ if (locationResult == null) {
+ error.invoke(PositionError.buildError(PositionError.POSITION_UNAVAILABLE, "No location provided (FusedLocationProvider/lastLocation)."));
+ return;
+ }
+
+ Location location = locationResult.getLastLocation();
+ success.invoke(locationToMap(location));
+
+ mFusedLocationClient.removeLocationUpdates(mSingleLocationCallback);
+ mSingleLocationCallback = null;
+ }
+
+ @Override
+ public void onLocationAvailability(LocationAvailability locationAvailability) {
+ if (!locationAvailability.isLocationAvailable()) {
+ error.invoke(PositionError.buildError(PositionError.POSITION_UNAVAILABLE, "Location not available (FusedLocationProvider/lastLocation)."));
+ }
+ }
+ };
+ }
}
diff --git a/example/ios/GeolocationExample/Info.plist b/example/ios/GeolocationExample/Info.plist
index 1a0be27..48e46cd 100644
--- a/example/ios/GeolocationExample/Info.plist
+++ b/example/ios/GeolocationExample/Info.plist
@@ -35,8 +35,6 @@
- NSLocationWhenInUseUsageDescription
-
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
@@ -51,5 +49,9 @@
UIViewControllerBasedStatusBarAppearance
+ NSLocationAlwaysAndWhenInUseUsageDescription
+ Testing purposes
+ NSLocationWhenInUseUsageDescription
+ Testing purposes
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index d97f610..8745c22 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -562,6 +562,8 @@ PODS:
- React-jsinspector (0.70.10)
- React-logger (0.70.10):
- glog
+ - react-native-background-timer (2.4.1):
+ - React-Core
- react-native-geolocation (3.0.6):
- RCT-Folly (= 2021.07.22.00)
- RCTRequired
@@ -711,6 +713,7 @@ DEPENDENCIES:
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
+ - react-native-background-timer (from `../node_modules/react-native-background-timer`)
- react-native-geolocation (from `../..`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -782,6 +785,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
React-logger:
:path: "../node_modules/react-native/ReactCommon/logger"
+ react-native-background-timer:
+ :path: "../node_modules/react-native-background-timer"
react-native-geolocation:
:path: "../.."
react-native-safe-area-context:
@@ -845,6 +850,7 @@ SPEC CHECKSUMS:
React-jsiexecutor: 1457c7bb2f08f257e6b5ece8b62348da43c4ccf2
React-jsinspector: 2a1985769123b199c195a96f46478b35db139712
React-logger: 7fbdc3576b2ed3834360c8ea3c1c2ec54ac3043a
+ react-native-background-timer: 17ea5e06803401a379ebf1f20505b793ac44d0fe
react-native-geolocation: a4df6c9ef98f624737b40c9e3d709eec1bff6ea7
react-native-safe-area-context: 0dfc8e3a7d5ff115d100bafe4269d64a2c0a1456
React-perflogger: f86e85eb59151d09e9b44b8931f5c088e4f08bbc
diff --git a/example/package.json b/example/package.json
index 75f3e86..9ee0c64 100644
--- a/example/package.json
+++ b/example/package.json
@@ -16,6 +16,7 @@
"@react-navigation/native-stack": "^6.7.0",
"react": "18.1.0",
"react-native": "0.70.10",
+ "react-native-background-timer": "^2.4.1",
"react-native-safe-area-context": "4.5.1",
"react-native-screens": "3.17.0"
},
diff --git a/example/src/configs/BackgroundLocationUpdates.tsx b/example/src/configs/BackgroundLocationUpdates.tsx
new file mode 100644
index 0000000..9d2bd16
--- /dev/null
+++ b/example/src/configs/BackgroundLocationUpdates.tsx
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) React Native Community
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ *
+ * @format
+ */
+
+'use strict';
+
+import React, { useEffect, useRef, useState } from 'react';
+import { View, Button, AppState } from 'react-native';
+import BackgroundTimer from 'react-native-background-timer';
+import Geolocation from '@react-native-community/geolocation';
+
+export default function BackgroundLocationUpdates() {
+ const appState = useRef(AppState.currentState);
+ const [backgroundListener, setBackgroundListener] = useState(false);
+
+ useEffect(() => {
+ if (!backgroundListener) {
+ return;
+ }
+
+ const subscription = AppState.addEventListener('change', (nextAppState) => {
+ if (nextAppState.match(/inactive|background/)) {
+ BackgroundTimer.runBackgroundTimer(() => {
+ Geolocation.getCurrentPosition(
+ (position) => {
+ console.log(
+ 'getCurrentPosition background',
+ JSON.stringify(position)
+ );
+ },
+ (error) =>
+ console.log(
+ 'getCurrentPosition background error',
+ JSON.stringify(error)
+ ),
+ { enableHighAccuracy: true }
+ );
+ }, 1000);
+
+ console.log('App has come to the foreground!');
+ } else {
+ BackgroundTimer.stopBackgroundTimer();
+ }
+
+ appState.current = nextAppState;
+ });
+
+ return () => {
+ subscription.remove();
+ };
+ }, [backgroundListener]);
+
+ return (
+
+
+ );
+}
diff --git a/example/src/configs/SetConfiguration.tsx b/example/src/configs/SetConfiguration.tsx
index 0940689..a5e8a14 100644
--- a/example/src/configs/SetConfiguration.tsx
+++ b/example/src/configs/SetConfiguration.tsx
@@ -40,6 +40,9 @@ export default function SetConfigurationExample() {
const [locationProvider, setLocationProvider] = useState<
'playServices' | 'android' | 'auto'
>('auto');
+ const [enableBackgroundLocationUpdates, setEnableBackgroundLocationUpdates] =
+ useState(false);
+
useEffect(() => {
Geolocation.setRNConfiguration({
skipPermissionRequests,
@@ -60,34 +63,47 @@ export default function SetConfigurationExample() {
/>
{Platform.OS === 'ios' && (
-
- authorizationLevel
-
- {authorizationLevelOptions.map((item, index) => (
-
- setAuthorizationLevel(authorizationLevelOptions[index])
- }
- style={[
- styles.segmentedControlButton,
- authorizationLevelOptions.indexOf(authorizationLevel) ===
- index && styles.segmentedControlButtonActive,
- ]}
- >
-
+
+ authorizationLevel
+
+ {authorizationLevelOptions.map((item, index) => (
+
+ setAuthorizationLevel(authorizationLevelOptions[index])
+ }
style={[
- styles.segmentControlText,
+ styles.segmentedControlButton,
authorizationLevelOptions.indexOf(authorizationLevel) ===
- index && styles.segmentControlTextActive,
+ index && styles.segmentedControlButtonActive,
]}
>
- {item}
-
-
- ))}
+
+ {item}
+
+
+ ))}
+
-
+
+ enableBackgroundLocationUpdates
+
+ setEnableBackgroundLocationUpdates(
+ !enableBackgroundLocationUpdates
+ )
+ }
+ value={enableBackgroundLocationUpdates}
+ />
+
+ >
)}
{Platform.OS === 'android' && (
diff --git a/example/src/configs/index.tsx b/example/src/configs/index.tsx
index 05ef07e..ae541e3 100644
--- a/example/src/configs/index.tsx
+++ b/example/src/configs/index.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import RequestAuthorization from './RequestAuthorization';
import SetConfiguration from './SetConfiguration';
+import BackgroundLocationUpdates from './BackgroundLocationUpdates';
const configs = [
{
@@ -20,6 +21,14 @@ const configs = [
return ;
},
},
+ {
+ id: 'backgroundLocationUpdates',
+ title: 'getCurrentLoaction() in background',
+ description: 'Test background location updates',
+ render() {
+ return ;
+ },
+ },
];
export default configs;
diff --git a/example/src/examples/WatchPosition.tsx b/example/src/examples/WatchPosition.tsx
index 096a7d8..6653406 100644
--- a/example/src/examples/WatchPosition.tsx
+++ b/example/src/examples/WatchPosition.tsx
@@ -18,6 +18,7 @@ export default function WatchPositionExample() {
try {
const watchID = Geolocation.watchPosition(
(position) => {
+ console.log('watchPosition', JSON.stringify(position));
setPosition(JSON.stringify(position));
},
(error) => Alert.alert('WatchPosition Error', JSON.stringify(error))
diff --git a/example/yarn.lock b/example/yarn.lock
index acc713c..feb920d 100644
--- a/example/yarn.lock
+++ b/example/yarn.lock
@@ -4238,6 +4238,11 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-native-background-timer@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/react-native-background-timer/-/react-native-background-timer-2.4.1.tgz#a3bc1cafa8c1e3aeefd0611de120298b67978a0f"
+ integrity sha512-TE4Kiy7jUyv+hugxDxitzu38sW1NqjCk4uE5IgU2WevLv7sZacaBc6PZKOShNRPGirLl1NWkaG3LDEkdb9Um5g==
+
react-native-codegen@^0.70.6:
version "0.70.6"
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.70.6.tgz#2ce17d1faad02ad4562345f8ee7cbe6397eda5cb"
diff --git a/ios/RNCGeolocation.mm b/ios/RNCGeolocation.mm
index 9eb6f0a..8a598f0 100644
--- a/ios/RNCGeolocation.mm
+++ b/ios/RNCGeolocation.mm
@@ -38,6 +38,7 @@ typedef NS_ENUM(NSInteger, RNCGeolocationAuthorizationLevel) {
typedef struct {
BOOL skipPermissionRequests;
RNCGeolocationAuthorizationLevel authorizationLevel;
+ BOOL enableBackgroundLocationUpdates;
} RNCGeolocationConfiguration;
typedef struct {
@@ -76,7 +77,7 @@ + (RNCGeolocationOptions)RNCGeolocationOptions:(id)json
return (RNCGeolocationOptions){
.timeout = [RCTConvert NSTimeInterval:options[@"timeout"]] ?: 1000 * 60 * 10,
- .maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]] ?: INFINITY,
+ .maximumAge = [RCTConvert NSTimeInterval:options[@"maximumAge"]],
.accuracy = [RCTConvert BOOL:options[@"enableHighAccuracy"]] ? kCLLocationAccuracyBest : RNC_DEFAULT_LOCATION_ACCURACY,
.distanceFilter = distanceFilter,
.useSignificantChanges = static_cast([RCTConvert BOOL:options[@"useSignificantChanges"]] ?: NO),
@@ -174,7 +175,7 @@ - (void)beginLocationUpdatesWithDesiredAccuracy:(CLLocationAccuracy)desiredAccur
{
if (!_locationConfiguration.skipPermissionRequests) {
[self requestAuthorization:nil error:nil];
- } else {
+ } else if (_locationConfiguration.enableBackgroundLocationUpdates) {
[self enableBackgroundLocationUpdates];
}
diff --git a/js/NativeRNCGeolocation.ts b/js/NativeRNCGeolocation.ts
index 6a16178..8f6ab11 100644
--- a/js/NativeRNCGeolocation.ts
+++ b/js/NativeRNCGeolocation.ts
@@ -5,6 +5,7 @@ export type GeolocationConfiguration = {
skipPermissionRequests: boolean;
authorizationLevel?: 'always' | 'whenInUse' | 'auto';
locationProvider?: 'playServices' | 'android' | 'auto';
+ enableBackgroundLocationUpdates?: boolean;
};
export type GeolocationOptions = {
@@ -42,6 +43,7 @@ export interface Spec extends TurboModule {
setConfiguration(config: {
skipPermissionRequests: boolean;
authorizationLevel?: string;
+ enableBackgroundLocationUpdates?: string;
}): void;
requestAuthorization(
success: () => void,
diff --git a/js/implementation.native.ts b/js/implementation.native.ts
index 28e0f34..9a1d313 100644
--- a/js/implementation.native.ts
+++ b/js/implementation.native.ts
@@ -45,6 +45,8 @@ let updatesEnabled = false;
export function setRNConfiguration(config: GeolocationConfiguration) {
RNCGeolocation.setConfiguration({
...config,
+ enableBackgroundLocationUpdates:
+ config?.enableBackgroundLocationUpdates ?? true,
authorizationLevel:
config?.authorizationLevel === 'auto'
? undefined
diff --git a/package.json b/package.json
index f2b8f37..98118d4 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,7 @@
"@types/jest": "^26.0.23",
"@types/react-native": "^0.69.3",
"@types/react-test-renderer": "^18.0.0",
+ "@types/react-native-background-timer": "^2.0.0",
"babel-jest": "^26.6.3",
"babel-plugin-module-resolver": "^3.2.0",
"eslint": "^7.32.0",
diff --git a/yarn.lock b/yarn.lock
index eec4074..3666e5f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2976,6 +2976,11 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==
+"@types/react-native-background-timer@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@types/react-native-background-timer/-/react-native-background-timer-2.0.0.tgz#c44c57f8fbca9d9d5521fdd72a8f55232b79381e"
+ integrity sha512-y5VW82dL/ESOLg+5QQHyBdsFVA4ZklENxmOyxv8o06T+3HBG2JOSuz/CIPz1vKdB7dmWDGPZNuPosdtnp+xv2A==
+
"@types/react-native@^0.69.3":
version "0.69.4"
resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.69.4.tgz#3ab61e4738b3223dda53d13f365ef806f84ad4b4"