├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── agontuk
│ └── RNFusedLocation
│ ├── FusedLocationProvider.java
│ ├── LocationAccuracy.java
│ ├── LocationChangeListener.java
│ ├── LocationError.java
│ ├── LocationManagerProvider.java
│ ├── LocationOptions.java
│ ├── LocationProvider.java
│ ├── LocationUtils.java
│ ├── RNFusedLocationModule.java
│ └── RNFusedLocationPackage.java
├── docs
├── accuracy.md
└── setup.md
├── example
├── .buckconfig
├── .bundle
│ └── config
├── .eslintrc.js
├── .gitignore
├── .prettierrc.js
├── .ruby-version
├── .watchmanconfig
├── @types
│ └── index.d.ts
├── Gemfile
├── _node-version
├── android
│ ├── app
│ │ ├── _BUCK
│ │ ├── build.gradle
│ │ ├── build_defs.bzl
│ │ ├── debug.keystore
│ │ ├── proguard-rules.pro
│ │ └── src
│ │ │ ├── debug
│ │ │ ├── AndroidManifest.xml
│ │ │ └── java
│ │ │ │ └── com
│ │ │ │ └── geoloc
│ │ │ │ └── ReactNativeFlipper.java
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── com
│ │ │ │ └── geoloc
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── MainApplication.java
│ │ │ │ └── newarchitecture
│ │ │ │ ├── MainApplicationReactNativeHost.java
│ │ │ │ ├── components
│ │ │ │ └── MainComponentsRegistry.java
│ │ │ │ └── modules
│ │ │ │ └── MainApplicationTurboModuleManagerDelegate.java
│ │ │ ├── jni
│ │ │ ├── CMakeLists.txt
│ │ │ ├── MainApplicationModuleProvider.cpp
│ │ │ ├── MainApplicationModuleProvider.h
│ │ │ ├── MainApplicationTurboModuleManagerDelegate.cpp
│ │ │ ├── MainApplicationTurboModuleManagerDelegate.h
│ │ │ ├── MainComponentsRegistry.cpp
│ │ │ ├── MainComponentsRegistry.h
│ │ │ └── OnLoad.cpp
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── rn_edit_text_material.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── app.json
├── babel.config.js
├── index.js
├── ios
│ ├── GeoLoc-Bridging-Header.h
│ ├── GeoLoc.xcodeproj
│ │ ├── project.pbxproj
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── GeoLoc.xcscheme
│ ├── GeoLoc.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── GeoLoc
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.mm
│ │ ├── Images.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── LaunchScreen.storyboard
│ │ └── main.m
│ ├── GeoLocTests
│ │ ├── GeoLocTests.m
│ │ └── Info.plist
│ ├── Podfile
│ ├── Podfile.lock
│ ├── _xcode.env
│ └── dummy.swift
├── metro.config.js
├── package.json
├── src
│ ├── App.tsx
│ └── MapView.tsx
├── tsconfig.json
└── yarn.lock
├── index.d.ts
├── ios
├── LocationOptions.swift
├── LocationProvider.swift
├── LocationUtils.swift
├── RNFusedLocation-Bridging-Header.h
├── RNFusedLocation.swift
├── RNFusedLocation.xcodeproj
│ └── project.pbxproj
├── RNFusedLocation.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── RNFusedLocationBridge.m
├── js
├── Geolocation.js
├── Geolocation.native.js
└── index.js
├── package.json
├── react-native-geolocation-service.podspec
├── screenshots
├── 01-ios-add-to-library.png
└── 02-ios-add-to-build-phases.png
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | example/
2 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["airbnb"],
3 | "parser": "babel-eslint",
4 | "plugins": ["react"],
5 | "rules": {
6 | "comma-dangle": ["error", "never"],
7 | "import/no-extraneous-dependencies": "off",
8 | "indent": ["error", 2, {
9 | "VariableDeclarator": {
10 | "var": 2,
11 | "let": 2,
12 | "const": 2
13 | },
14 | "SwitchCase": 1
15 | }],
16 | "jsx-a11y/href-no-hash": "off",
17 | "jsx-quotes": ["error", "prefer-single"],
18 | "no-use-before-define": "off",
19 | "react/jsx-indent": ["error", 4],
20 | "react/jsx-indent-props": ["error", 4],
21 | "react/jsx-filename-extension": ["error", {
22 | "extensions": [".js", ".jsx"]
23 | }],
24 | "react/sort-comp": "off"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: "\U0001F41B Bug report"
3 | about: Report a reproducible bug or regression in this library.
4 | labels: bug
5 | ---
6 |
7 | ## Environment
8 | Run `npx react-native info` in your terminal and copy the results here.
9 |
10 | ## Platforms
11 | Is this issue related to Android, iOS, or both ?
12 |
13 | ## Versions
14 | Please add the used versions/branches
15 | - Android:
16 | - iOS:
17 | - react-native-geolocation-service:
18 | - react-native:
19 | - react:
20 |
21 | ## Description
22 | Please provide a clear and concise description of what the bug is. Include screenshots if needed.
23 |
24 | ## Reproducible Demo
25 | Provide a detailed list of steps that reproduce the issue.
26 |
27 | 1.
28 | 2.
29 |
30 | ## Expected Results
31 | Describe what you expected to happen.
32 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Summary
4 |
5 |
6 |
7 | ## Test Plan
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # node.js
6 | #
7 | node_modules/
8 | npm-debug.log
9 | yarn-error.log
10 |
11 | # Xcode
12 | #
13 | build/
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata
23 | *.xccheckout
24 | *.moved-aside
25 | DerivedData
26 | *.hmap
27 | *.ipa
28 | *.xcuserstate
29 | project.xcworkspace
30 |
31 | # Android/IntelliJ
32 | #
33 | build/
34 | .idea
35 | .gradle
36 | local.properties
37 | *.iml
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # node.js
6 | #
7 | node_modules/
8 | npm-debug.log
9 | yarn-error.log
10 |
11 | # Xcode
12 | #
13 | build/
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata
23 | *.xccheckout
24 | *.moved-aside
25 | DerivedData
26 | *.hmap
27 | *.ipa
28 | *.xcuserstate
29 | project.xcworkspace
30 |
31 | # Android/IntelliJ
32 | #
33 | build/
34 | .idea
35 | .gradle
36 | local.properties
37 | *.iml
38 |
39 | # BUCK
40 | buck-out/
41 | \.buckd/
42 | *.keystore
43 |
44 |
45 | example/
46 | demo/
47 | screenshots/
48 | .editorconfig
49 | .eslintignore
50 | .eslintrc
51 | yarn.lock
52 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ### 5.3.1 (September 23, 2022)
4 | - iOS: fix crash due to location becoming nil ([#370](https://github.com/Agontuk/react-native-geolocation-service/pull/370))
5 |
6 | ### 5.3.0 (June 25, 2022)
7 | - Android: set `distanceFilter` to `0` in `getCurrentPosition` API
8 | - Android: update default version of play services location to 18.0.0
9 | - Android: rewrite location change listener logic to fix `Illegal callback invocation` error
10 |
11 | ### 5.3.0-beta.4 (December 08, 2021)
12 | - iOS: rewrite implementation
13 | - iOS: revert back to use `requestLocation` API to avoid cached location issue
14 | - iOS: fix `getCurrentPosition` not working issue without timeout
15 | - iOS: remove `distanceFilter` support in `getCurrentPosition` API
16 |
17 | ### 5.3.0-beta.3 (September 25, 2021)
18 | - Android: fix NativeEventEmitter warnings in RN 0.65
19 |
20 | ### 5.3.0-beta.2 (August 28, 2021)
21 | - Android: expose option to use LocationManager API
22 |
23 | ### 5.3.0-beta.1 (April 23, 2021)
24 | - Android: update default build tools & sdk version
25 | - Android: add vertical accuracy & provider name in location data if available
26 | - Android: rewrite implementation & add support for LocationManager API
27 | - Android: fallback to use LocationManager if Google Play Service is not available
28 |
29 | ### 5.2.0 (February 2, 2021)
30 | - Android: fix location request on airplane mode
31 | - iOS: add ability to configure showsBackgroundLocationIndicator
32 | - iOS: fix location delay in getCurrentPosition method
33 | - iOS: add `reduced` as an accuracy option
34 |
35 | ### 5.1.1 (November 28, 2020)
36 | - Fix iOS `pod install` issue.
37 |
38 | ### 5.1.0 (November 26, 2020)
39 | - Android/iOS: add support for specifying accuracy level
40 | - Android: only trigger error in watchPosition if location service is off
41 |
42 | ### 5.0.0 (May 30, 2020)
43 | #### Breaking
44 | - iOS: new iOS module implementation. Follow this [issue](https://github.com/Agontuk/react-native-geolocation-service/issues/173) for details.
45 | - iOS: Removed `setRNConfiguration` usage & updated `requestAuthorization` to return promise.
46 | - Android: removed permissions from manifest. You have to declare location permission in your main `AndroidManifest.xml`.
47 | - Android: update play-services-location version to `17.0.0`.
48 |
49 | #### Fixes
50 | - Android: emit error if location is unavailable during `watchPosition` call.
51 | - Only start timer if timeout is valid.
52 |
53 | ### 4.0.2 (May 15, 2020)
54 | - Fix ios module warning
55 |
56 | ### 4.0.1 (April 29, 2020)
57 | - Removed premature check of isLocationEnabled in startObserving
58 |
59 | ### 4.0.0 (February 3, 2020)
60 | - Android: update android support library to androidx
61 | - Android: fix ApiException handling in getLastLocation method
62 |
63 | ### 3.1.0 (August 30, 2019)
64 | - Add support for web
65 | - Update location request flow ([described here](https://github.com/Agontuk/react-native-geolocation-service/issues/108#issuecomment-524217651))
66 |
67 | ### 3.0.0 (July 23, 2019)
68 | - Fix typescript definition
69 | - __BREAKING__: Switch to `@react-native-community/geolocation` library for iOS implementation (follow iOS setup instruction)
70 | - Export `PositionError` constants
71 | - Added `forceRequestLocation` flag to request location with only GPS enabled
72 |
73 | ### 2.0.1 (May 5, 2019)
74 | - Add typescript definition
75 |
76 | ### 2.0.0 (December 2, 2018)
77 | - Support RN 0.57+
78 |
79 | ### 1.1.0 (July 2, 2018)
80 | - Added support for project wide gradle properties.
81 | - Added support for tracking location update.
82 |
83 | ### 1.0.4 (April 20, 2018)
84 | - Fix crash due to illegal callback invocation.
85 | - Added `showLocationDialog` option to control whether to show location dialog if it is disabled.
86 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 | Copyright (c) 2018 Iftekhar Rifat
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is
9 | furnished to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in
12 | all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
20 | OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-geolocation-service
2 | React native geolocation service for iOS and android.
3 |
4 | # Why ?
5 | This library is created in an attempt to fix the location timeout issue on android with the react-native's current implementation of Geolocation API. This library tries to solve the issue by using Google Play Service's new `FusedLocationProviderClient` API, which Google strongly recommends over android's default framework location API. It automatically decides which provider to use based on your request configuration and also prompts you to change the location mode if it doesn't satisfy your current request configuration.
6 |
7 | > NOTE: Location request can still timeout since many android devices have GPS issue in the hardware/system level. Check the [FAQ](#faq) for more details.
8 |
9 | # Installation
10 | yarn
11 | ```bash
12 | yarn add react-native-geolocation-service
13 | ```
14 |
15 | npm
16 | ```bash
17 | npm install react-native-geolocation-service
18 | ```
19 |
20 | # Compatibility
21 | | RN Version | Package Version |
22 | | ---------- | --------------- |
23 | | >=0.60 | >=3.0.0 |
24 | | <0.60 | 2.0.0 |
25 | | <0.57 | 1.1.0 |
26 |
27 | # Setup
28 | - See [docs/setup.md](docs/setup.md)
29 | - Check out example project
30 |
31 | # Usage
32 | Since this library was meant to be a drop-in replacement for the RN's Geolocation API, the usage is pretty straight forward, with some extra error cases to handle.
33 |
34 | > One thing to note, for android this library assumes that location permission is already granted by the user, so you have to use `PermissionsAndroid` to request for permission before making the location request.
35 |
36 | ```js
37 | ...
38 | import Geolocation from 'react-native-geolocation-service';
39 | ...
40 |
41 | componentDidMount() {
42 | if (hasLocationPermission) {
43 | Geolocation.getCurrentPosition(
44 | (position) => {
45 | console.log(position);
46 | },
47 | (error) => {
48 | // See error code charts below.
49 | console.log(error.code, error.message);
50 | },
51 | { enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 }
52 | );
53 | }
54 | }
55 | ```
56 |
57 | # API
58 | #### `async requestAuthorization(authorizationLevel) (iOS only)`
59 | Request location permission based on the authorizationLevel parameter. Can be either `"whenInUse"` or `"always"`. You have to configure the plist keys during setup.
60 |
61 | When promise resolves, returns the status of the authorization.
62 | - `disabled` - Location service is disabled
63 | - `granted` - Permission granted
64 | - `denied` - Permission denied
65 | - `restricted` - Permission restricted
66 |
67 | #### `getCurrentPosition(successCallback, ?errorCallback, ?options)`
68 | - **successCallback**: Invoked with latest location info.
69 | - **errorCallback**: Invoked whenever an error is encountered.
70 | - **options**:
71 |
72 | | Name | Type | Default | Description |
73 | | -- | -- | -- | -- |
74 | | timeout | `ms` | `INFINITY` | Request timeout |
75 | | maximumAge | `ms` | `INFINITY` | How long previous location will be cached |
76 | | accuracy | `object` | `--` | { android: [Link](docs/accuracy.md#android), ios: [Link](docs/accuracy.md#ios) }
If not provided or provided with invalid value, falls back to use `enableHighAccuracy` |
77 | | enableHighAccuracy | `bool` | `false` | Use high accuracy mode
78 | | distanceFilter | `m` | `100` | Minimum displacement in meters
79 | | showLocationDialog | `bool` | `true` | Whether to ask to enable location in Android (android only)
80 | | forceRequestLocation | `bool` | `false` | Force request location even after denying improve accuracy dialog (android only)
81 | | forceLocationManager | `bool` | `false` | If set to `true`, will use android's default LocationManager API (android only)
82 |
83 | #### `watchPosition(successCallback, ?errorCallback, ?options)`
84 | - **successCallback**: Invoked with latest location info.
85 | - **errorCallback**: Invoked whenever an error is encountered.
86 | - **options**:
87 |
88 | | Name | Type | Default | Description |
89 | | -- | -- | -- | -- |
90 | | accuracy | `object` | `--` | { android: [Link](docs/accuracy.md#android), ios: [Link](docs/accuracy.md#ios) }
If not provided or provided with invalid value, falls back to use `enableHighAccuracy` |
91 | | enableHighAccuracy | `bool` | `false` | Use high accuracy mode
92 | | distanceFilter | `m` | `100` | Minimum displacement between location updates in meters
93 | | interval | `ms` | `10000` | Interval for active location updates (android only)
94 | | fastestInterval | `ms` | `5000` | Fastest rate at which your application will receive location updates, which might be faster than `interval` in some situations (for example, if other applications are triggering location updates) (android only)
95 | | showLocationDialog | `bool` | `true` | whether to ask to enable location in Android (android only)
96 | | forceRequestLocation | `bool` | `false` | Force request location even after denying improve accuracy dialog (android only)
97 | | forceLocationManager | `bool` | `false` | If set to `true`, will use android's default LocationManager API (android only)
98 | | useSignificantChanges | `bool` | false | Uses the battery-efficient native significant changes APIs to return locations. Locations will only be returned when the device detects a significant distance has been breached (iOS only)
99 | | showsBackgroundLocationIndicator | `bool` | false | This setting enables a blue bar or a blue pill in the status bar on iOS. When the app moves to the background, the system uses this property to determine whether to change the status bar appearance to indicate that location services are in use. Users can tap the indicator to return to your app. (iOS only)
100 |
101 | #### `clearWatch(watchId)`
102 | - watchId (id returned by `watchPosition`)
103 |
104 | #### `stopObserving()`
105 | Stops observing for device location changes. In addition, it removes all listeners previously registered.
106 |
107 | # Error Codes
108 | | Name | Code | Description |
109 | | --- | --- | --- |
110 | | PERMISSION_DENIED | 1 | Location permission is not granted |
111 | | POSITION_UNAVAILABLE | 2 | Location provider not available |
112 | | TIMEOUT | 3 | Location request timed out |
113 | | PLAY_SERVICE_NOT_AVAILABLE | 4 | Google play service is not installed or has an older version (android only) |
114 | | SETTINGS_NOT_SATISFIED | 5 | Location service is not enabled or location mode is not appropriate for the current request (android only) |
115 | | INTERNAL_ERROR | -1 | Library crashed for some reason or the `getCurrentActivity()` returned null (android only) |
116 |
117 | # FAQ
118 | 1. **Location timeout still happening ?**
119 |
120 | Try the following steps: (Taken from [here](https://support.strava.com/hc/en-us/articles/216918967-Troubleshooting-GPS-Issues))
121 | - Turn phone off/on
122 | - Turn GPS off/on
123 | - Disable any battery saver settings, including Power Saving Mode, Battery Management or any third party apps
124 | - Perform an "AGPS reset": Install the App GPS Status & Toolbox, then in that app, go to Menu > Tools > Manage A-GPS State > Reset
125 |
126 | Adjusting battery saver settings on different devices:
127 |
128 | - HTC: Access your phone settings > battery > power saving mode > battery optimization > select your app > don't optimize > save
129 | - Huawei: Turn Energy Settings to Normal and add your app to "Protected Apps"
130 | - LG If you're running Android 6 or higher: Settings > battery & power saving > battery usage > ignore optimizations > turn ON for your app
131 | - Motorola If you're running Android 6 or higher: Battery > select the menu in the upper right-hand corner > battery optimization > not optimized > all apps > select your app > don't optimize
132 | - OnePlus (using OxygenOS Settings): Battery > battery optimization > switch to 'all apps' > select your app > don't optimize
133 | - Samsung: Access battery settings > app power saving > details > your app > disabled
134 | - Sony If you're running Android 6 or higher: Battery > from the menu in the upper right-hand corner > battery optimization > apps > your app
135 | - Xiaomi (MIUI OS) If you're running Android 6 or higher: Access your phone settings > additional settings > battery and performance > manage battery usage > apps > your app
136 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def DEFAULT_COMPILE_SDK_VERSION = 29
4 | def DEFAULT_BUILD_TOOLS_VERSION = "29.0.2"
5 | def DEFAULT_MIN_SDK_VERSION = 16
6 | def DEFAULT_TARGET_SDK_VERSION = 29
7 | def DEFAULT_GOOGLE_PLAY_SERVICES_VERSION = "18.0.0"
8 |
9 | def safeExtGet(prop, fallback) {
10 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
11 | }
12 |
13 | android {
14 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
15 | buildToolsVersion safeExtGet('buildToolsVersion', DEFAULT_BUILD_TOOLS_VERSION)
16 |
17 | defaultConfig {
18 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
19 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
20 | }
21 |
22 | lintOptions {
23 | abortOnError false
24 | }
25 | }
26 |
27 | dependencies {
28 | def googlePlayServicesVersion = safeExtGet('googlePlayServicesVersion', DEFAULT_GOOGLE_PLAY_SERVICES_VERSION)
29 | googlePlayServicesVersion = safeExtGet('playServicesVersion', googlePlayServicesVersion)
30 | googlePlayServicesVersion = safeExtGet('playServicesLocationVersion', googlePlayServicesVersion)
31 |
32 | implementation "com.facebook.react:react-native:+"
33 | implementation 'androidx.appcompat:appcompat:1.0.2'
34 | implementation "com.google.android.gms:play-services-location:$googlePlayServicesVersion"
35 | }
36 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/FusedLocationProvider.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.content.IntentSender;
6 | import android.location.Location;
7 | import android.location.LocationManager;
8 | import android.os.Handler;
9 | import android.os.Looper;
10 | import android.util.Log;
11 |
12 | import androidx.annotation.NonNull;
13 |
14 | import com.facebook.react.bridge.ReactApplicationContext;
15 | import com.google.android.gms.common.api.ApiException;
16 | import com.google.android.gms.common.api.ResolvableApiException;
17 | import com.google.android.gms.location.FusedLocationProviderClient;
18 | import com.google.android.gms.location.LocationAvailability;
19 | import com.google.android.gms.location.LocationCallback;
20 | import com.google.android.gms.location.LocationRequest;
21 | import com.google.android.gms.location.LocationResult;
22 | import com.google.android.gms.location.LocationServices;
23 | import com.google.android.gms.location.LocationSettingsRequest;
24 | import com.google.android.gms.location.LocationSettingsResponse;
25 | import com.google.android.gms.location.LocationSettingsStatusCodes;
26 | import com.google.android.gms.location.SettingsClient;
27 | import com.google.android.gms.tasks.OnFailureListener;
28 | import com.google.android.gms.tasks.OnSuccessListener;
29 |
30 | import java.util.Random;
31 |
32 | public class FusedLocationProvider implements LocationProvider {
33 | private final ReactApplicationContext context;
34 | private final FusedLocationProviderClient fusedLocationProviderClient;
35 | private final LocationChangeListener locationChangeListener;
36 | private final SettingsClient settingsClient;
37 |
38 | private int activityRequestCode;
39 | private LocationOptions locationOptions;
40 | private LocationRequest locationRequest;
41 |
42 | private boolean isSingleUpdate = false;
43 | private final LocationCallback locationCallback = new LocationCallback() {
44 | @Override
45 | public void onLocationResult(LocationResult locationResult) {
46 | locationChangeListener.onLocationChange(
47 | FusedLocationProvider.this,
48 | locationResult.getLastLocation()
49 | );
50 |
51 | if (isSingleUpdate) {
52 | timeoutHandler.removeCallbacks(timeoutRunnable);
53 | fusedLocationProviderClient.removeLocationUpdates(locationCallback);
54 | }
55 | }
56 |
57 | @Override
58 | public void onLocationAvailability(LocationAvailability locationAvailability) {
59 | if (!locationAvailability.isLocationAvailable() &&
60 | !LocationUtils.isLocationEnabled(context)
61 | ) {
62 | locationChangeListener.onLocationError(
63 | FusedLocationProvider.this,
64 | LocationError.POSITION_UNAVAILABLE,
65 | "Unable to retrieve location."
66 | );
67 | }
68 | }
69 | };
70 | private final Handler timeoutHandler = new Handler(Looper.getMainLooper());
71 | private final Runnable timeoutRunnable = new Runnable() {
72 | @Override
73 | public void run() {
74 | locationChangeListener.onLocationError(
75 | FusedLocationProvider.this,
76 | LocationError.TIMEOUT,
77 | null
78 | );
79 | fusedLocationProviderClient.removeLocationUpdates(locationCallback);
80 | }
81 | };
82 |
83 | public FusedLocationProvider(ReactApplicationContext context, LocationChangeListener locationChangeListener) {
84 | this.context = context;
85 | this.fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(context);
86 | this.locationChangeListener = locationChangeListener;
87 | this.settingsClient = LocationServices.getSettingsClient(context);
88 | }
89 |
90 | @SuppressLint("MissingPermission")
91 | @Override
92 | public void getCurrentLocation(final LocationOptions locationOptions) {
93 | this.isSingleUpdate = true;
94 | this.locationOptions = locationOptions;
95 | this.locationRequest = buildLocationRequest(locationOptions);
96 |
97 | fusedLocationProviderClient.getLastLocation()
98 | .addOnSuccessListener(new OnSuccessListener() {
99 | @Override
100 | public void onSuccess(Location location) {
101 | if (location != null &&
102 | LocationUtils.getLocationAge(location) < locationOptions.getMaximumAge()
103 | ) {
104 | Log.i(RNFusedLocationModule.TAG, "returning cached location.");
105 | locationChangeListener.onLocationChange(FusedLocationProvider.this, location);
106 | return;
107 | }
108 |
109 | checkLocationSettings();
110 | }
111 | })
112 | .addOnFailureListener(new OnFailureListener() {
113 | @Override
114 | public void onFailure(@NonNull Exception e) {
115 | checkLocationSettings();
116 | }
117 | });
118 | }
119 |
120 | @Override
121 | public boolean onActivityResult(int requestCode, int resultCode) {
122 | if (requestCode != activityRequestCode) {
123 | return false;
124 | }
125 |
126 | if (resultCode == Activity.RESULT_OK) {
127 | startLocationUpdates();
128 | return true;
129 | }
130 |
131 | boolean forceRequestLocation = locationOptions.isForceRequestLocation();
132 | boolean locationEnabled = LocationUtils.isLocationEnabled(context);
133 |
134 | if (forceRequestLocation && locationEnabled) {
135 | startLocationUpdates();
136 | } else {
137 | locationChangeListener.onLocationError(
138 | FusedLocationProvider.this,
139 | locationEnabled ? LocationError.SETTINGS_NOT_SATISFIED : LocationError.POSITION_UNAVAILABLE,
140 | null
141 | );
142 | }
143 |
144 | return true;
145 | }
146 |
147 | @Override
148 | public void requestLocationUpdates(LocationOptions locationOptions) {
149 | this.isSingleUpdate = false;
150 | this.locationOptions = locationOptions;
151 | this.locationRequest = buildLocationRequest(locationOptions);
152 | checkLocationSettings();
153 | }
154 |
155 | @Override
156 | public void removeLocationUpdates() {
157 | fusedLocationProviderClient.removeLocationUpdates(locationCallback);
158 | }
159 |
160 | private LocationRequest buildLocationRequest(LocationOptions options) {
161 | LocationRequest locationRequest = new LocationRequest();
162 | int priority = getLocationPriority(options.getAccuracy());
163 |
164 | locationRequest.setPriority(priority)
165 | .setInterval(options.getInterval())
166 | .setFastestInterval(options.getFastestInterval())
167 | .setSmallestDisplacement(isSingleUpdate ? 0 : options.getDistanceFilter());
168 |
169 | return locationRequest;
170 | }
171 |
172 | private void checkLocationSettings() {
173 | LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
174 | builder.addLocationRequest(locationRequest);
175 | LocationSettingsRequest locationSettingsRequest = builder.build();
176 |
177 | settingsClient.checkLocationSettings(locationSettingsRequest)
178 | .addOnSuccessListener(new OnSuccessListener() {
179 | @Override
180 | public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
181 | startLocationUpdates();
182 | }
183 | })
184 | .addOnFailureListener(new OnFailureListener() {
185 | @Override
186 | public void onFailure(@NonNull Exception e) {
187 | ApiException exception = (ApiException) e;
188 |
189 | switch (exception.getStatusCode()) {
190 | case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
191 | boolean showLocationDialog = locationOptions.isShowLocationDialog();
192 | boolean forceRequestLocation = locationOptions.isForceRequestLocation();
193 | boolean locationEnabled = LocationUtils.isLocationEnabled(context);
194 |
195 | if (!showLocationDialog) {
196 | if (forceRequestLocation && locationEnabled) {
197 | startLocationUpdates();
198 | } else {
199 | locationChangeListener.onLocationError(
200 | FusedLocationProvider.this,
201 | locationEnabled ? LocationError.SETTINGS_NOT_SATISFIED : LocationError.POSITION_UNAVAILABLE,
202 | null
203 | );
204 | }
205 |
206 | break;
207 | }
208 |
209 | try {
210 | ResolvableApiException resolvable = (ResolvableApiException) exception;
211 | Activity activity = context.getCurrentActivity();
212 |
213 | if (activity == null) {
214 | locationChangeListener.onLocationError(
215 | FusedLocationProvider.this,
216 | LocationError.INTERNAL_ERROR,
217 | "Tried to open location dialog while not attached to an Activity."
218 | );
219 | break;
220 | }
221 |
222 | activityRequestCode = getActivityRequestCode();
223 | resolvable.startResolutionForResult(activity, activityRequestCode);
224 | } catch (IntentSender.SendIntentException | ClassCastException sie) {
225 | locationChangeListener.onLocationError(
226 | FusedLocationProvider.this,
227 | LocationError.INTERNAL_ERROR,
228 | null
229 | );
230 | }
231 |
232 | break;
233 | case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
234 | if (LocationUtils.isOnAirplaneMode(context) &&
235 | LocationUtils.isProviderEnabled(context, LocationManager.GPS_PROVIDER)
236 | ) {
237 | startLocationUpdates();
238 | break;
239 | }
240 | default:
241 | locationChangeListener.onLocationError(
242 | FusedLocationProvider.this,
243 | LocationError.SETTINGS_NOT_SATISFIED,
244 | null
245 | );
246 | break;
247 | }
248 | }
249 | });
250 | }
251 |
252 | private int getActivityRequestCode() {
253 | Random random = new Random();
254 | return random.nextInt(10000);
255 | }
256 |
257 | private int getLocationPriority(LocationAccuracy locationAccuracy) {
258 | switch (locationAccuracy) {
259 | case high:
260 | return LocationRequest.PRIORITY_HIGH_ACCURACY;
261 | case balanced:
262 | return LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
263 | case low:
264 | return LocationRequest.PRIORITY_LOW_POWER;
265 | case passive:
266 | return LocationRequest.PRIORITY_NO_POWER;
267 | default:
268 | throw new IllegalStateException("Unexpected value: " + locationAccuracy);
269 | }
270 | }
271 |
272 | @SuppressLint("MissingPermission")
273 | private void startLocationUpdates() {
274 | fusedLocationProviderClient.requestLocationUpdates(locationRequest, locationCallback, Looper.getMainLooper());
275 |
276 | if (isSingleUpdate) {
277 | long timeout = locationOptions.getTimeout();
278 |
279 | if (timeout > 0 && timeout != Long.MAX_VALUE) {
280 | timeoutHandler.postDelayed(timeoutRunnable, timeout);
281 | }
282 | }
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationAccuracy.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | public enum LocationAccuracy {
4 | high,
5 | balanced,
6 | low,
7 | passive
8 | }
9 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationChangeListener.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import android.location.Location;
4 |
5 | import androidx.annotation.Nullable;
6 |
7 | public interface LocationChangeListener {
8 | void onLocationChange(LocationProvider locationProvider, Location location);
9 |
10 | void onLocationError(LocationProvider locationProvider, LocationError error, @Nullable String message);
11 | }
12 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationError.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | public enum LocationError {
4 | PERMISSION_DENIED(1),
5 | POSITION_UNAVAILABLE(2),
6 | TIMEOUT(3),
7 | PLAY_SERVICE_NOT_AVAILABLE(4),
8 | SETTINGS_NOT_SATISFIED(5),
9 | INTERNAL_ERROR(-1);
10 |
11 | private final int value;
12 |
13 | LocationError(int value) {
14 | this.value = value;
15 | }
16 |
17 | public int getValue() {
18 | return value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationManagerProvider.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.location.Criteria;
6 | import android.location.Location;
7 | import android.location.LocationListener;
8 | import android.location.LocationManager;
9 | import android.os.Bundle;
10 | import android.os.Handler;
11 | import android.os.Looper;
12 | import android.util.Log;
13 |
14 | import androidx.annotation.Nullable;
15 |
16 | import com.facebook.react.bridge.ReactApplicationContext;
17 |
18 | import java.util.List;
19 |
20 | public class LocationManagerProvider implements LocationProvider {
21 | private final LocationManager locationManager;
22 | private final LocationChangeListener locationChangeListener;
23 |
24 | private boolean isSingleUpdate = false;
25 | private final LocationListener locationListener = new LocationListener() {
26 | @Override
27 | public void onLocationChanged(Location location) {
28 | locationChangeListener.onLocationChange(LocationManagerProvider.this, location);
29 |
30 | if (isSingleUpdate) {
31 | timeoutHandler.removeCallbacks(timeoutRunnable);
32 | removeLocationUpdates();
33 | }
34 | }
35 |
36 | @Override
37 | public void onStatusChanged(String provider, int status, Bundle extras) {
38 | if (status == android.location.LocationProvider.AVAILABLE) {
39 | onProviderEnabled(provider);
40 | } else {
41 | onProviderDisabled(provider);
42 | }
43 | }
44 |
45 | @Override
46 | public void onProviderEnabled(String provider) {
47 | //
48 | }
49 |
50 | @Override
51 | public void onProviderDisabled(String provider) {
52 | locationChangeListener.onLocationError(
53 | LocationManagerProvider.this,
54 | LocationError.POSITION_UNAVAILABLE,
55 | null
56 | );
57 | }
58 | };
59 | private final Handler timeoutHandler = new Handler(Looper.getMainLooper());
60 | private final Runnable timeoutRunnable = new Runnable() {
61 | @Override
62 | public void run() {
63 | locationChangeListener.onLocationError(
64 | LocationManagerProvider.this,
65 | LocationError.TIMEOUT,
66 | null
67 | );
68 | removeLocationUpdates();
69 | }
70 | };
71 |
72 | public LocationManagerProvider(ReactApplicationContext context, LocationChangeListener locationChangeListener) {
73 | this.locationChangeListener = locationChangeListener;
74 | this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
75 | }
76 |
77 | @SuppressLint("MissingPermission")
78 | @Override
79 | public void getCurrentLocation(LocationOptions locationOptions) {
80 | this.isSingleUpdate = true;
81 | String provider = getBestProvider(locationOptions.getAccuracy());
82 |
83 | if (provider == null) {
84 | locationChangeListener.onLocationError(
85 | LocationManagerProvider.this,
86 | LocationError.POSITION_UNAVAILABLE,
87 | null
88 | );
89 | return;
90 | }
91 |
92 | Location location = locationManager.getLastKnownLocation(provider);
93 |
94 | if (location != null &&
95 | LocationUtils.getLocationAge(location) < locationOptions.getMaximumAge()
96 | ) {
97 | Log.i(RNFusedLocationModule.TAG, "returning cached location.");
98 | locationChangeListener.onLocationChange(LocationManagerProvider.this, location);
99 | return;
100 | }
101 |
102 | startLocationUpdates(
103 | provider,
104 | locationOptions.getInterval(),
105 | 0,
106 | locationOptions.getTimeout()
107 | );
108 | }
109 |
110 | @Override
111 | public boolean onActivityResult(int requestCode, int resultCode) {
112 | return false;
113 | }
114 |
115 | @Override
116 | public void requestLocationUpdates(LocationOptions locationOptions) {
117 | this.isSingleUpdate = false;
118 | String provider = getBestProvider(locationOptions.getAccuracy());
119 |
120 | if (provider == null) {
121 | locationChangeListener.onLocationError(
122 | LocationManagerProvider.this,
123 | LocationError.POSITION_UNAVAILABLE,
124 | null
125 | );
126 | return;
127 | }
128 |
129 | startLocationUpdates(
130 | provider,
131 | locationOptions.getInterval(),
132 | locationOptions.getDistanceFilter(),
133 | locationOptions.getTimeout()
134 | );
135 | }
136 |
137 | @SuppressLint("MissingPermission")
138 | @Override
139 | public void removeLocationUpdates() {
140 | locationManager.removeUpdates(locationListener);
141 | }
142 |
143 | @Nullable
144 | private String getBestProvider(LocationAccuracy locationAccuracy) {
145 | Criteria criteria = getProviderCriteria(locationAccuracy);
146 | String provider = locationManager.getBestProvider(criteria, true);
147 |
148 | if (provider == null) {
149 | List providers = locationManager.getProviders(true);
150 | provider = providers.size() > 0 ? providers.get(0) : null;
151 | }
152 |
153 | return provider;
154 | }
155 |
156 | private Criteria getProviderCriteria(LocationAccuracy locationAccuracy) {
157 | int accuracy;
158 | int baseAccuracy;
159 | int power;
160 |
161 | switch (locationAccuracy) {
162 | case high:
163 | accuracy = Criteria.ACCURACY_HIGH;
164 | baseAccuracy = Criteria.ACCURACY_FINE;
165 | power = Criteria.POWER_HIGH;
166 | break;
167 | case balanced:
168 | accuracy = Criteria.ACCURACY_MEDIUM;
169 | baseAccuracy = Criteria.ACCURACY_COARSE;
170 | power = Criteria.POWER_MEDIUM;
171 | break;
172 | case low:
173 | accuracy = Criteria.ACCURACY_LOW;
174 | baseAccuracy = Criteria.ACCURACY_COARSE;
175 | power = Criteria.POWER_LOW;
176 | break;
177 | case passive:
178 | accuracy = Criteria.NO_REQUIREMENT;
179 | baseAccuracy = Criteria.NO_REQUIREMENT;
180 | power = Criteria.NO_REQUIREMENT;
181 | break;
182 | default:
183 | throw new IllegalStateException("Unexpected value: " + locationAccuracy);
184 | }
185 |
186 | Criteria criteria = new Criteria();
187 | criteria.setAccuracy(baseAccuracy);
188 | criteria.setBearingAccuracy(accuracy);
189 | criteria.setHorizontalAccuracy(accuracy);
190 | criteria.setPowerRequirement(power);
191 | criteria.setSpeedAccuracy(accuracy);
192 | criteria.setVerticalAccuracy(accuracy);
193 |
194 | return criteria;
195 | }
196 |
197 | @SuppressLint("MissingPermission")
198 | private void startLocationUpdates(String provider, long minTime, float minDistance, long timeout) {
199 | locationManager.requestLocationUpdates(
200 | provider,
201 | minTime,
202 | minDistance,
203 | locationListener,
204 | Looper.getMainLooper()
205 | );
206 |
207 | if (isSingleUpdate) {
208 | if (timeout > 0 && timeout != Long.MAX_VALUE) {
209 | timeoutHandler.postDelayed(timeoutRunnable, timeout);
210 | }
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationOptions.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.bridge.ReadableMap;
6 | import com.facebook.react.bridge.ReadableType;
7 |
8 | public class LocationOptions {
9 | private static final float DEFAULT_DISTANCE_FILTER = 100;
10 | private static final long DEFAULT_INTERVAL = 10 * 1000; /* 10 secs */
11 | private static final long DEFAULT_FASTEST_INTERVAL = 5 * 1000; /* 5 sec */
12 |
13 | private final LocationAccuracy accuracy;
14 | private final long interval;
15 | private final long fastestInterval;
16 | private final float distanceFilter;
17 | private final long timeout;
18 | private final double maximumAge;
19 | private final boolean showLocationDialog;
20 | private final boolean forceRequestLocation;
21 | private final boolean forceLocationManager;
22 |
23 | private LocationOptions(
24 | LocationAccuracy accuracy,
25 | long interval,
26 | long fastestInterval,
27 | float distanceFilter,
28 | long timeout,
29 | double maximumAge,
30 | boolean showLocationDialog,
31 | boolean forceRequestLocation,
32 | boolean forceLocationManager
33 | ) {
34 | this.accuracy = accuracy;
35 | this.interval = interval;
36 | this.fastestInterval = fastestInterval;
37 | this.distanceFilter = distanceFilter;
38 | this.timeout = timeout;
39 | this.maximumAge = maximumAge;
40 | this.showLocationDialog = showLocationDialog;
41 | this.forceRequestLocation = forceRequestLocation;
42 | this.forceLocationManager = forceLocationManager;
43 | }
44 |
45 | public static LocationOptions fromReadableMap(ReadableMap map) {
46 | LocationAccuracy accuracy = getAccuracy(map);
47 | long interval = map.hasKey("interval")
48 | ? (long) map.getDouble("interval")
49 | : DEFAULT_INTERVAL;
50 | long fastestInterval = map.hasKey("fastestInterval")
51 | ? (long) map.getDouble("fastestInterval")
52 | : DEFAULT_FASTEST_INTERVAL;
53 | float distanceFilter = map.hasKey("distanceFilter")
54 | ? (float) map.getDouble("distanceFilter")
55 | : DEFAULT_DISTANCE_FILTER;
56 | long timeout = map.hasKey("timeout")
57 | ? (long) map.getDouble("timeout")
58 | : Long.MAX_VALUE;
59 | double maximumAge = map.hasKey("maximumAge")
60 | ? map.getDouble("maximumAge")
61 | : Double.POSITIVE_INFINITY;
62 | boolean showLocationDialog =
63 | !map.hasKey("showLocationDialog") || map.getBoolean("showLocationDialog");
64 | boolean forceRequestLocation =
65 | map.hasKey("forceRequestLocation") && map.getBoolean("forceRequestLocation");
66 | boolean forceLocationManager =
67 | map.hasKey("forceLocationManager") && map.getBoolean("forceLocationManager");
68 |
69 | return new LocationOptions(
70 | accuracy,
71 | interval,
72 | fastestInterval,
73 | distanceFilter,
74 | timeout,
75 | maximumAge,
76 | showLocationDialog,
77 | forceRequestLocation,
78 | forceLocationManager
79 | );
80 | }
81 |
82 | public LocationAccuracy getAccuracy() {
83 | return accuracy;
84 | }
85 |
86 | public long getInterval() {
87 | return interval;
88 | }
89 |
90 | public long getFastestInterval() {
91 | return fastestInterval;
92 | }
93 |
94 | public float getDistanceFilter() {
95 | return distanceFilter;
96 | }
97 |
98 | public long getTimeout() {
99 | return timeout;
100 | }
101 |
102 | public double getMaximumAge() {
103 | return maximumAge;
104 | }
105 |
106 | public boolean isShowLocationDialog() {
107 | return showLocationDialog;
108 | }
109 |
110 | public boolean isForceRequestLocation() {
111 | return forceRequestLocation;
112 | }
113 |
114 | public boolean isForceLocationManager() {
115 | return forceLocationManager;
116 | }
117 |
118 | /**
119 | * Determine location priority from user provided accuracy level
120 | */
121 | private static LocationAccuracy getAccuracy(@NonNull ReadableMap options) {
122 | String accuracy = "";
123 | boolean highAccuracy = options.hasKey("enableHighAccuracy")
124 | && options.getBoolean("enableHighAccuracy");
125 |
126 | if (options.hasKey("accuracy") && options.getType("accuracy") == ReadableType.Map) {
127 | ReadableMap accuracyMap = options.getMap("accuracy");
128 |
129 | if (accuracyMap != null &&
130 | accuracyMap.hasKey("android") &&
131 | accuracyMap.getType("android") == ReadableType.String
132 | ) {
133 | String value = accuracyMap.getString("android");
134 |
135 | if (value != null) {
136 | accuracy = value;
137 | }
138 | }
139 | }
140 |
141 | switch (accuracy) {
142 | case "high":
143 | return LocationAccuracy.high;
144 | case "balanced":
145 | return LocationAccuracy.balanced;
146 | case "low":
147 | return LocationAccuracy.low;
148 | case "passive":
149 | return LocationAccuracy.passive;
150 | default:
151 | return highAccuracy ? LocationAccuracy.high : LocationAccuracy.balanced;
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationProvider.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | public interface LocationProvider {
4 | void getCurrentLocation(LocationOptions locationOptions);
5 |
6 | boolean onActivityResult(int requestCode, int resultCode);
7 |
8 | void requestLocationUpdates(LocationOptions locationOptions);
9 |
10 | void removeLocationUpdates();
11 | }
12 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/LocationUtils.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import android.content.ContentResolver;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.location.Location;
7 | import android.location.LocationManager;
8 | import android.Manifest;
9 | import android.os.Build;
10 | import android.os.SystemClock;
11 | import android.text.TextUtils;
12 | import android.provider.Settings;
13 |
14 | import androidx.annotation.Nullable;
15 | import androidx.core.app.ActivityCompat;
16 |
17 | import com.facebook.react.bridge.Arguments;
18 | import com.facebook.react.bridge.WritableMap;
19 |
20 | import com.google.android.gms.common.ConnectionResult;
21 | import com.google.android.gms.common.GoogleApiAvailability;
22 |
23 | public class LocationUtils {
24 | /**
25 | * Calculates the age of a location fix in milliseconds
26 | */
27 | public static long getLocationAge(Location location) {
28 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
29 | return (SystemClock.elapsedRealtimeNanos() - location.getElapsedRealtimeNanos()) / 1000000;
30 | }
31 |
32 | return System.currentTimeMillis() - location.getTime();
33 | }
34 |
35 | /**
36 | * Check if location permissions are granted.
37 | */
38 | public static boolean hasLocationPermission(Context context) {
39 | return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
40 | ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
41 | }
42 |
43 | /**
44 | * Check if google play service is available on device.
45 | */
46 | public static boolean isGooglePlayServicesAvailable(Context context) {
47 | int result = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context);
48 |
49 | // TODO: Handle other possible success types.
50 | return result == ConnectionResult.SUCCESS || result == ConnectionResult.SERVICE_UPDATING;
51 | }
52 |
53 | /**
54 | * Check if airplane mode is on/off
55 | */
56 | @SuppressWarnings("deprecation")
57 | public static boolean isOnAirplaneMode(Context context) {
58 | ContentResolver contentResolver = context.getContentResolver();
59 |
60 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
61 | return Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
62 | }
63 |
64 | return Settings.System.getInt(contentResolver, Settings.System.AIRPLANE_MODE_ON, 0) != 0;
65 | }
66 |
67 | /**
68 | * Check if location is enabled on the device.
69 | * https://androidx.tech/artifacts/core/core/1.8.0-alpha02-source/androidx/core/location/LocationManagerCompat.java.html
70 | */
71 | public static boolean isLocationEnabled(Context context) {
72 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
73 | return !TextUtils.isEmpty(
74 | Settings.Secure.getString(context.getContentResolver(),
75 | Settings.Secure.LOCATION_PROVIDERS_ALLOWED));
76 | }
77 |
78 | if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
79 | return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE,
80 | Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF;
81 | }
82 |
83 | LocationManager locationManager;
84 |
85 | try {
86 | locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
87 | } catch (Exception e) {
88 | return false;
89 | }
90 |
91 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
92 | return locationManager.isLocationEnabled();
93 | }
94 |
95 | return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
96 | || locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
97 | }
98 |
99 | /**
100 | * Check if a specific location provider is enabled or not
101 | */
102 | public static boolean isProviderEnabled(Context context, String provider) {
103 | try {
104 | LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
105 | return lm.isProviderEnabled(provider);
106 | } catch (Exception e) {
107 | return false;
108 | }
109 | }
110 |
111 | /**
112 | * Build error response for error callback.
113 | */
114 | public static WritableMap buildError(LocationError locationError, @Nullable String message) {
115 | String msg = message;
116 |
117 | if (msg == null) {
118 | msg = getDefaultErrorMessage(locationError);
119 | }
120 |
121 | WritableMap error = Arguments.createMap();
122 | error.putInt("code", locationError.getValue());
123 | error.putString("message", msg);
124 |
125 | return error;
126 | }
127 |
128 | public static WritableMap locationToMap(Location location) {
129 | WritableMap map = Arguments.createMap();
130 | WritableMap coords = Arguments.createMap();
131 |
132 | coords.putDouble("latitude", location.getLatitude());
133 | coords.putDouble("longitude", location.getLongitude());
134 | coords.putDouble("altitude", location.getAltitude());
135 | coords.putDouble("accuracy", location.getAccuracy());
136 | coords.putDouble("heading", location.getBearing());
137 | coords.putDouble("speed", location.getSpeed());
138 |
139 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
140 | coords.putDouble("altitudeAccuracy", location.getVerticalAccuracyMeters());
141 | }
142 |
143 | map.putMap("coords", coords);
144 | map.putString("provider", location.getProvider());
145 | map.putDouble("timestamp", location.getTime());
146 |
147 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
148 | map.putBoolean("mocked", location.isFromMockProvider());
149 | }
150 |
151 | return map;
152 | }
153 |
154 | private static String getDefaultErrorMessage(LocationError locationError) {
155 | switch (locationError) {
156 | case PERMISSION_DENIED:
157 | return "Location permission not granted.";
158 | case POSITION_UNAVAILABLE:
159 | return "No location provider available.";
160 | case TIMEOUT:
161 | return "Location request timed out.";
162 | case PLAY_SERVICE_NOT_AVAILABLE:
163 | return "Google play service is not available.";
164 | case SETTINGS_NOT_SATISFIED:
165 | return "Location settings are not satisfied.";
166 | case INTERNAL_ERROR:
167 | default:
168 | return "Internal error occurred";
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/RNFusedLocationModule.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.location.Location;
6 | import android.util.Log;
7 |
8 | import androidx.annotation.NonNull;
9 | import androidx.annotation.Nullable;
10 |
11 | import com.facebook.react.bridge.ActivityEventListener;
12 | import com.facebook.react.bridge.Callback;
13 | import com.facebook.react.bridge.ReactApplicationContext;
14 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
15 | import com.facebook.react.bridge.ReactMethod;
16 | import com.facebook.react.bridge.ReadableMap;
17 | import com.facebook.react.bridge.WritableMap;
18 | import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
19 |
20 | import java.util.HashMap;
21 | import java.util.Set;
22 |
23 | public class RNFusedLocationModule extends ReactContextBaseJavaModule implements ActivityEventListener, LocationChangeListener {
24 | public static final String TAG = "RNFusedLocation";
25 | private final HashMap pendingRequests;
26 | @Nullable private LocationProvider continuousLocationProvider;
27 |
28 | public RNFusedLocationModule(ReactApplicationContext reactContext) {
29 | super(reactContext);
30 |
31 | reactContext.addActivityEventListener(this);
32 | this.pendingRequests = new HashMap<>();
33 |
34 | Log.i(TAG, TAG + " initialized");
35 | }
36 |
37 | @NonNull
38 | @Override
39 | public String getName() {
40 | return TAG;
41 | }
42 |
43 | @Override
44 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
45 | if (continuousLocationProvider != null &&
46 | continuousLocationProvider.onActivityResult(requestCode, resultCode)
47 | ) {
48 | return;
49 | }
50 |
51 | Set providers = pendingRequests.keySet();
52 |
53 | for (LocationProvider locationProvider: providers) {
54 | if (locationProvider.onActivityResult(requestCode, resultCode)) {
55 | return;
56 | }
57 | }
58 | }
59 |
60 | @Override
61 | public void onNewIntent(Intent intent) {
62 | //
63 | }
64 |
65 | @Override
66 | public void onLocationChange(LocationProvider locationProvider, Location location) {
67 | WritableMap locationData = LocationUtils.locationToMap(location);
68 |
69 | if (locationProvider.equals(continuousLocationProvider)) {
70 | emitEvent("geolocationDidChange", locationData);
71 | return;
72 | }
73 |
74 | PendingLocationRequest request = pendingRequests.get(locationProvider);
75 |
76 | if (request != null) {
77 | request.successCallback.invoke(locationData);
78 | pendingRequests.remove(locationProvider);
79 | }
80 | }
81 |
82 | @Override
83 | public void onLocationError(LocationProvider locationProvider, LocationError error, @Nullable String message) {
84 | WritableMap errorData = LocationUtils.buildError(error, message);
85 |
86 | if (locationProvider.equals(continuousLocationProvider)) {
87 | emitEvent("geolocationError", errorData);
88 | return;
89 | }
90 |
91 | PendingLocationRequest request = pendingRequests.get(locationProvider);
92 |
93 | if (request != null) {
94 | request.errorCallback.invoke(errorData);
95 | pendingRequests.remove(locationProvider);
96 | }
97 | }
98 |
99 | @ReactMethod
100 | public void getCurrentPosition(ReadableMap options, final Callback success, final Callback error) {
101 | ReactApplicationContext context = getContext();
102 |
103 | if (!LocationUtils.hasLocationPermission(context)) {
104 | error.invoke(LocationUtils.buildError(LocationError.PERMISSION_DENIED, null));
105 | return;
106 | }
107 |
108 | LocationOptions locationOptions = LocationOptions.fromReadableMap(options);
109 | final LocationProvider locationProvider = createLocationProvider(locationOptions.isForceLocationManager());
110 |
111 | pendingRequests.put(locationProvider, new PendingLocationRequest(success, error));
112 | locationProvider.getCurrentLocation(locationOptions);
113 | }
114 |
115 | @ReactMethod
116 | public void startObserving(ReadableMap options) {
117 | ReactApplicationContext context = getContext();
118 |
119 | if (!LocationUtils.hasLocationPermission(context)) {
120 | emitEvent(
121 | "geolocationError",
122 | LocationUtils.buildError(LocationError.PERMISSION_DENIED, null)
123 | );
124 | return;
125 | }
126 |
127 | LocationOptions locationOptions = LocationOptions.fromReadableMap(options);
128 |
129 | if (continuousLocationProvider == null) {
130 | continuousLocationProvider = createLocationProvider(locationOptions.isForceLocationManager());
131 | }
132 |
133 | continuousLocationProvider.requestLocationUpdates(locationOptions);
134 | }
135 |
136 | @ReactMethod
137 | public void stopObserving() {
138 | if (continuousLocationProvider != null) {
139 | continuousLocationProvider.removeLocationUpdates();
140 | continuousLocationProvider = null;
141 | }
142 | }
143 |
144 | @ReactMethod
145 | public void addListener(String eventName) {
146 | // Keep: Required for RN built in Event Emitter Calls.
147 | }
148 |
149 | @ReactMethod
150 | public void removeListeners(Integer count) {
151 | // Keep: Required for RN built in Event Emitter Calls.
152 | }
153 |
154 | private LocationProvider createLocationProvider(boolean forceLocationManager) {
155 | ReactApplicationContext context = getContext();
156 | boolean playServicesAvailable = LocationUtils.isGooglePlayServicesAvailable(context);
157 |
158 | if (forceLocationManager || !playServicesAvailable) {
159 | return new LocationManagerProvider(context, this);
160 | }
161 |
162 | return new FusedLocationProvider(context, this);
163 | }
164 |
165 | private void emitEvent(String eventName, WritableMap data) {
166 | getContext().getJSModule(RCTDeviceEventEmitter.class).emit(eventName, data);
167 | }
168 |
169 | private ReactApplicationContext getContext() {
170 | return getReactApplicationContext();
171 | }
172 |
173 | private static class PendingLocationRequest {
174 | final Callback successCallback;
175 | final Callback errorCallback;
176 |
177 | public PendingLocationRequest(Callback success, Callback error) {
178 | this.successCallback = success;
179 | this.errorCallback = error;
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/android/src/main/java/com/agontuk/RNFusedLocation/RNFusedLocationPackage.java:
--------------------------------------------------------------------------------
1 | package com.agontuk.RNFusedLocation;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import com.facebook.react.ReactPackage;
6 | import com.facebook.react.bridge.JavaScriptModule;
7 | import com.facebook.react.bridge.NativeModule;
8 | import com.facebook.react.bridge.ReactApplicationContext;
9 | import com.facebook.react.uimanager.ViewManager;
10 |
11 | import java.util.Arrays;
12 | import java.util.Collections;
13 | import java.util.List;
14 |
15 | public class RNFusedLocationPackage implements ReactPackage {
16 | @NonNull
17 | @Override
18 | public List createViewManagers(@NonNull ReactApplicationContext reactContext) {
19 | return Collections.emptyList();
20 | }
21 |
22 | public List> createJSModules() {
23 | return Collections.emptyList();
24 | }
25 |
26 | @NonNull
27 | @Override
28 | public List createNativeModules(@NonNull ReactApplicationContext reactContext) {
29 | return Arrays.asList(
30 | new RNFusedLocationModule(reactContext)
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/docs/accuracy.md:
--------------------------------------------------------------------------------
1 | # Accuracy Level
2 |
3 | ## Android
4 | [Source](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest#constants)
5 |
6 | | Name | Description |
7 | | -- | -- |
8 | | high | This will return the finest location available. |
9 | | balanced | Block level accuracy considered to be about 100 meter accuracy. |
10 | | low | City level accuracy is considered to be about 10km accuracy. |
11 | | passive | No locations will be returned unless a different client has requested location updates in which case this request will act as a passive listener to those locations. |
12 |
13 | ## iOS
14 | [Source](https://developer.apple.com/documentation/corelocation/cllocationaccuracy)
15 |
16 | | Name | Description |
17 | | -- | -- |
18 | | bestForNavigation | The highest possible accuracy that uses additional sensor data to facilitate navigation apps. |
19 | | best | The best level of accuracy available. |
20 | | nearestTenMeters | Accurate to within ten meters of the desired target. |
21 | | hundredMeters | Accurate to within one hundred meters. |
22 | | kilometer | Accurate to the nearest kilometer. |
23 | | threeKilometers | Accurate to the nearest three kilometers. |
24 | | reduced | Used when an app does not need accurate location data. |
25 |
--------------------------------------------------------------------------------
/docs/setup.md:
--------------------------------------------------------------------------------
1 | # Setup
2 |
3 | ## iOS
4 | ### 1) Linking
5 | For RN 0.60 or higher, no manual linking is needed. After installing the package, just run `pod install` from inside ios directory. It'll automatically pickup the package and install it.
6 |
7 |
8 | 0.59 or below
9 |
10 | ### Using Cocoapods
11 | - Update your `Podfile`
12 | ```
13 | pod 'react-native-geolocation-service', path: '../node_modules/react-native-geolocation-service'
14 | ```
15 | - Then run `pod install` from ios directory
16 |
17 | ### Manually linking
18 |
19 | #### `Open project.xcodeproj in Xcode`
20 |
21 | Drag `RNFusedLocation.xcodeproj` to your project on Xcode (usually under the Libraries group on Xcode):
22 |
23 | 
24 |
25 | #### Link `libRNFusedLocation.a` binary with libraries
26 |
27 | Click on your main project file (the one that represents the `.xcodeproj`) select `Build Phases` and drag the static library from the `Products` folder inside the Library you are importing to `Link Binary With Libraries` (or use the `+` sign and choose library from the list):
28 |
29 | 
30 |
31 |
32 | ### 2) Enable Swift Support
33 | Since the iOS implementation is written in swift, you need to add swift support in your project. It can be done just by adding an empty swift file and a bridging header in your project folder. You have to do it from xcode, otherwise swift compiler flag won't be updated.
34 | - Select `File -> New -> File` from xcode
35 | - Choose Swift file, name it anything
36 | - Click `Next` and say yes when prompted if you’d like to generate a bridging header (important)
37 |
38 | ### 3) Update `info.plist`
39 | There are three info.plist keys for location service
40 | - NSLocationWhenInUseUsageDescription
41 | - NSLocationAlwaysUsageDescription
42 | - NSLocationAlwaysAndWhenInUseUsageDescription
43 |
44 | Unless you need background location update, adding only the first key will be enough. To enable background location update, you need to add all the keys in `info.plist` and add location as a background mode in the `Signing & Capabilities -> Capability` tab in Xcode.
45 |
46 | ## Android
47 |
48 | ### 1) Linking
49 | For RN 0.60 or higher, no manual linking is needed. You can override following gradle properties from your root build.gradle file.
50 |
51 | ```gradle
52 | ext {
53 | compileSdkVersion = 28
54 | buildToolsVersion = "28.0.3"
55 | minSdkVersion = 16
56 | targetSdkVersion = 28
57 |
58 | // Any of the following will work
59 | googlePlayServicesVersion = "17.0.0"
60 | // playServicesVersion = "17.0.0"
61 | // playServicesLocationVersion = "17.0.0"
62 | }
63 | ```
64 |
65 |
66 | 0.59 or below
67 |
68 | 1. In `android/app/build.gradle`
69 |
70 | ```gradle
71 | ...
72 | dependencies {
73 | ...
74 | implementation project(':react-native-geolocation-service')
75 | }
76 | ```
77 |
78 | If you've defined [project-wide properties](https://developer.android.com/studio/build/gradle-tips#configure-project-wide-properties) (recommended) in your root build.gradle, this library will detect the presence of the following properties:
79 |
80 | ```gradle
81 | buildscript {
82 | /**
83 | + Project-wide Gradle configuration properties
84 | */
85 | ext {
86 | compileSdkVersion = 28
87 | minSdkVersion = 16
88 | targetSdkVersion = 28
89 | buildToolsVersion = "28.0.3"
90 | googlePlayServicesVersion = "17.0.0"
91 | }
92 | repositories { ... }
93 | dependencies { ... }
94 | }
95 | ```
96 |
97 | If you do not have *project-wide properties* defined and have a different play-services version than the one included in this library, use the following instead. But play service version should be `11+` or the library won't work.
98 |
99 | ```gradle
100 | ...
101 | dependencies {
102 | ...
103 | implementation(project(':react-native-geolocation-service')) {
104 | exclude group: 'com.google.android.gms', module: 'play-services-location'
105 | }
106 | implementation 'com.google.android.gms:play-services-location:'
107 | }
108 | ```
109 |
110 | 2. In `android/setting.gradle`
111 |
112 | ```gradle
113 | ...
114 | include ':react-native-geolocation-service'
115 | project(':react-native-geolocation-service').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-geolocation-service/android')
116 | ```
117 |
118 | 3. In `MainApplication.java`
119 |
120 | ```java
121 | ...
122 | import com.agontuk.RNFusedLocation.RNFusedLocationPackage;
123 |
124 | public class MainApplication extends Application implements ReactApplication {
125 | ...
126 | @Override
127 | protected List getPackages() {
128 | return Arrays.asList(
129 | ...
130 | new RNFusedLocationPackage()
131 | );
132 | }
133 | }
134 | ```
135 |
136 |
137 | ### 2) Permissions
138 | Add permission in your `AndroidManifest.xml` file based on your project requirement.
139 | ```xml
140 |
141 |
142 | ```
143 |
--------------------------------------------------------------------------------
/example/.buckconfig:
--------------------------------------------------------------------------------
1 |
2 | [android]
3 | target = Google Inc.:Google APIs:23
4 |
5 | [maven_repositories]
6 | central = https://repo1.maven.org/maven2
7 |
--------------------------------------------------------------------------------
/example/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/example/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: '@react-native-community',
4 | parser: '@typescript-eslint/parser',
5 | plugins: ['@typescript-eslint'],
6 | overrides: [
7 | {
8 | files: ['*.ts', '*.tsx'],
9 | rules: {
10 | '@typescript-eslint/no-shadow': ['error'],
11 | 'no-shadow': 'off',
12 | 'no-undef': 'off',
13 | },
14 | },
15 | ],
16 | };
17 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 |
35 | # node.js
36 | #
37 | node_modules/
38 | npm-debug.log
39 | yarn-error.log
40 |
41 | # BUCK
42 | buck-out/
43 | \.buckd/
44 | *.keystore
45 | !debug.keystore
46 |
47 | # fastlane
48 | #
49 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
50 | # screenshots whenever they are needed.
51 | # For more information about the recommended setup visit:
52 | # https://docs.fastlane.tools/best-practices/source-control/
53 |
54 | **/fastlane/report.xml
55 | **/fastlane/Preview.html
56 | **/fastlane/screenshots
57 | **/fastlane/test_output
58 |
59 | # Bundle artifact
60 | *.jsbundle
61 |
62 | # Ruby / CocoaPods
63 | /ios/Pods/
64 | /vendor/bundle/
65 |
--------------------------------------------------------------------------------
/example/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | bracketSameLine: true,
4 | bracketSpacing: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | };
8 |
--------------------------------------------------------------------------------
/example/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.7.5
2 |
--------------------------------------------------------------------------------
/example/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/example/@types/index.d.ts:
--------------------------------------------------------------------------------
1 | declare module '@voximplant/react-native-foreground-service' {
2 | export default class VIForegroundService {
3 | static getInstance(): VIForegroundService;
4 |
5 | async createNotificationChannel(channelConfig: any);
6 |
7 | async startService(notificationConfig: any);
8 |
9 | async stopService();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/example/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby '2.7.5'
5 |
6 | gem 'cocoapods', '~> 1.11', '>= 1.11.2'
7 |
--------------------------------------------------------------------------------
/example/_node-version:
--------------------------------------------------------------------------------
1 | 16
2 |
--------------------------------------------------------------------------------
/example/android/app/_BUCK:
--------------------------------------------------------------------------------
1 | # To learn about Buck see [Docs](https://buckbuild.com/).
2 | # To run your application with Buck:
3 | # - install Buck
4 | # - `npm start` - to start the packager
5 | # - `cd android`
6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"`
7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck
8 | # - `buck install -r android/app` - compile, install and run application
9 | #
10 |
11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12 |
13 | lib_deps = []
14 |
15 | create_aar_targets(glob(["libs/*.aar"]))
16 |
17 | create_jar_targets(glob(["libs/*.jar"]))
18 |
19 | android_library(
20 | name = "all-libs",
21 | exported_deps = lib_deps,
22 | )
23 |
24 | android_library(
25 | name = "app-code",
26 | srcs = glob([
27 | "src/main/java/**/*.java",
28 | ]),
29 | deps = [
30 | ":all-libs",
31 | ":build_config",
32 | ":res",
33 | ],
34 | )
35 |
36 | android_build_config(
37 | name = "build_config",
38 | package = "com.geoloc",
39 | )
40 |
41 | android_resource(
42 | name = "res",
43 | package = "com.geoloc",
44 | res = "src/main/res",
45 | )
46 |
47 | android_binary(
48 | name = "app",
49 | keystore = "//android/keystores:debug",
50 | manifest = "src/main/AndroidManifest.xml",
51 | package_type = "debug",
52 | deps = [
53 | ":app-code",
54 | ],
55 | )
56 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 |
3 | import com.android.build.OutputFile
4 | import org.apache.tools.ant.taskdefs.condition.Os
5 |
6 | /**
7 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
8 | * and bundleReleaseJsAndAssets).
9 | * These basically call `react-native bundle` with the correct arguments during the Android build
10 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
11 | * bundle directly from the development server. Below you can see all the possible configurations
12 | * and their defaults. If you decide to add a configuration block, make sure to add it before the
13 | * `apply from: "../../node_modules/react-native/react.gradle"` line.
14 | *
15 | * project.ext.react = [
16 | * // the name of the generated asset file containing your JS bundle
17 | * bundleAssetName: "index.android.bundle",
18 | *
19 | * // the entry file for bundle generation. If none specified and
20 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is
21 | * // default. Can be overridden with ENTRY_FILE environment variable.
22 | * entryFile: "index.android.js",
23 | *
24 | * // https://reactnative.dev/docs/performance#enable-the-ram-format
25 | * bundleCommand: "ram-bundle",
26 | *
27 | * // whether to bundle JS and assets in debug mode
28 | * bundleInDebug: false,
29 | *
30 | * // whether to bundle JS and assets in release mode
31 | * bundleInRelease: true,
32 | *
33 | * // whether to bundle JS and assets in another build variant (if configured).
34 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
35 | * // The configuration property can be in the following formats
36 | * // 'bundleIn${productFlavor}${buildType}'
37 | * // 'bundleIn${buildType}'
38 | * // bundleInFreeDebug: true,
39 | * // bundleInPaidRelease: true,
40 | * // bundleInBeta: true,
41 | *
42 | * // whether to disable dev mode in custom build variants (by default only disabled in release)
43 | * // for example: to disable dev mode in the staging build type (if configured)
44 | * devDisabledInStaging: true,
45 | * // The configuration property can be in the following formats
46 | * // 'devDisabledIn${productFlavor}${buildType}'
47 | * // 'devDisabledIn${buildType}'
48 | *
49 | * // the root of your project, i.e. where "package.json" lives
50 | * root: "../../",
51 | *
52 | * // where to put the JS bundle asset in debug mode
53 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
54 | *
55 | * // where to put the JS bundle asset in release mode
56 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release",
57 | *
58 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
59 | * // require('./image.png')), in debug mode
60 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
61 | *
62 | * // where to put drawable resources / React Native assets, e.g. the ones you use via
63 | * // require('./image.png')), in release mode
64 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
65 | *
66 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means
67 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
68 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle
69 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
70 | * // for example, you might want to remove it from here.
71 | * inputExcludes: ["android/**", "ios/**"],
72 | *
73 | * // override which node gets called and with what additional arguments
74 | * nodeExecutableAndArgs: ["node"],
75 | *
76 | * // supply additional arguments to the packager
77 | * extraPackagerArgs: []
78 | * ]
79 | */
80 |
81 | project.ext.react = [
82 | enableHermes: true, // clean and rebuild if changing
83 | ]
84 |
85 | apply from: "../../node_modules/react-native/react.gradle"
86 |
87 | /**
88 | * Set this to true to create two separate APKs instead of one:
89 | * - An APK that only works on ARM devices
90 | * - An APK that only works on x86 devices
91 | * The advantage is the size of the APK is reduced by about 4MB.
92 | * Upload all the APKs to the Play Store and people will download
93 | * the correct one based on the CPU architecture of their device.
94 | */
95 | def enableSeparateBuildPerCPUArchitecture = false
96 |
97 | /**
98 | * Run Proguard to shrink the Java bytecode in release builds.
99 | */
100 | def enableProguardInReleaseBuilds = false
101 |
102 | /**
103 | * The preferred build flavor of JavaScriptCore.
104 | *
105 | * For example, to use the international variant, you can use:
106 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
107 | *
108 | * The international variant includes ICU i18n library and necessary data
109 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
110 | * give correct results when using with locales other than en-US. Note that
111 | * this variant is about 6MiB larger per architecture than default.
112 | */
113 | def jscFlavor = 'org.webkit:android-jsc:+'
114 |
115 | /**
116 | * Whether to enable the Hermes VM.
117 | *
118 | * This should be set on project.ext.react and that value will be read here. If it is not set
119 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
120 | * and the benefits of using Hermes will therefore be sharply reduced.
121 | */
122 | def enableHermes = project.ext.react.get("enableHermes", false);
123 |
124 | /**
125 | * Architectures to build native code for.
126 | */
127 | def reactNativeArchitectures() {
128 | def value = project.getProperties().get("reactNativeArchitectures")
129 | return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
130 | }
131 |
132 | android {
133 | ndkVersion rootProject.ext.ndkVersion
134 |
135 | compileSdkVersion rootProject.ext.compileSdkVersion
136 |
137 | defaultConfig {
138 | applicationId "com.geoloc"
139 | minSdkVersion rootProject.ext.minSdkVersion
140 | targetSdkVersion rootProject.ext.targetSdkVersion
141 | versionCode 1
142 | versionName "1.0"
143 | buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
144 |
145 | if (isNewArchitectureEnabled()) {
146 | // We configure the CMake build only if you decide to opt-in for the New Architecture.
147 | externalNativeBuild {
148 | cmake {
149 | arguments "-DPROJECT_BUILD_DIR=$buildDir",
150 | "-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
151 | "-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
152 | "-DNODE_MODULES_DIR=$rootDir/../node_modules",
153 | "-DANDROID_STL=c++_shared"
154 | }
155 | }
156 | if (!enableSeparateBuildPerCPUArchitecture) {
157 | ndk {
158 | abiFilters (*reactNativeArchitectures())
159 | }
160 | }
161 | }
162 | }
163 |
164 | if (isNewArchitectureEnabled()) {
165 | // We configure the NDK build only if you decide to opt-in for the New Architecture.
166 | externalNativeBuild {
167 | cmake {
168 | path "$projectDir/src/main/jni/CMakeLists.txt"
169 | }
170 | }
171 | def reactAndroidProjectDir = project(':ReactAndroid').projectDir
172 | def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
173 | dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
174 | from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
175 | into("$buildDir/react-ndk/exported")
176 | }
177 | def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
178 | dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
179 | from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
180 | into("$buildDir/react-ndk/exported")
181 | }
182 | afterEvaluate {
183 | // If you wish to add a custom TurboModule or component locally,
184 | // you should uncomment this line.
185 | // preBuild.dependsOn("generateCodegenArtifactsFromSchema")
186 | preDebugBuild.dependsOn(packageReactNdkDebugLibs)
187 | preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
188 |
189 | // Due to a bug inside AGP, we have to explicitly set a dependency
190 | // between configureCMakeDebug* tasks and the preBuild tasks.
191 | // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
192 | configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild)
193 | configureCMakeDebug.dependsOn(preDebugBuild)
194 | reactNativeArchitectures().each { architecture ->
195 | tasks.findByName("configureCMakeDebug[${architecture}]")?.configure {
196 | dependsOn("preDebugBuild")
197 | }
198 | tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure {
199 | dependsOn("preReleaseBuild")
200 | }
201 | }
202 | }
203 | }
204 |
205 | splits {
206 | abi {
207 | reset()
208 | enable enableSeparateBuildPerCPUArchitecture
209 | universalApk false // If true, also generate a universal APK
210 | include (*reactNativeArchitectures())
211 | }
212 | }
213 | signingConfigs {
214 | debug {
215 | storeFile file('debug.keystore')
216 | storePassword 'android'
217 | keyAlias 'androiddebugkey'
218 | keyPassword 'android'
219 | }
220 | }
221 | buildTypes {
222 | debug {
223 | signingConfig signingConfigs.debug
224 | }
225 | release {
226 | // Caution! In production, you need to generate your own keystore file.
227 | // see https://reactnative.dev/docs/signed-apk-android.
228 | signingConfig signingConfigs.debug
229 | minifyEnabled enableProguardInReleaseBuilds
230 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
231 | }
232 | }
233 |
234 | // applicationVariants are e.g. debug, release
235 | applicationVariants.all { variant ->
236 | variant.outputs.each { output ->
237 | // For each separate APK per architecture, set a unique version code as described here:
238 | // https://developer.android.com/studio/build/configure-apk-splits.html
239 | // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
240 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
241 | def abi = output.getFilter(OutputFile.ABI)
242 | if (abi != null) { // null for the universal-debug, universal-release variants
243 | output.versionCodeOverride =
244 | defaultConfig.versionCode * 1000 + versionCodes.get(abi)
245 | }
246 |
247 | }
248 | }
249 | }
250 |
251 | dependencies {
252 | implementation fileTree(dir: "libs", include: ["*.jar"])
253 |
254 | //noinspection GradleDynamicVersion
255 | implementation "com.facebook.react:react-native:+" // From node_modules
256 |
257 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
258 |
259 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
260 | exclude group:'com.facebook.fbjni'
261 | }
262 |
263 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
264 | exclude group:'com.facebook.flipper'
265 | exclude group:'com.squareup.okhttp3', module:'okhttp'
266 | }
267 |
268 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
269 | exclude group:'com.facebook.flipper'
270 | }
271 |
272 | if (enableHermes) {
273 | //noinspection GradleDynamicVersion
274 | implementation("com.facebook.react:hermes-engine:+") { // From node_modules
275 | exclude group:'com.facebook.fbjni'
276 | }
277 | } else {
278 | implementation jscFlavor
279 | }
280 | }
281 |
282 | if (isNewArchitectureEnabled()) {
283 | // If new architecture is enabled, we let you build RN from source
284 | // Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
285 | // This will be applied to all the imported transtitive dependency.
286 | configurations.all {
287 | resolutionStrategy.dependencySubstitution {
288 | substitute(module("com.facebook.react:react-native"))
289 | .using(project(":ReactAndroid"))
290 | .because("On New Architecture we're building React Native from source")
291 | substitute(module("com.facebook.react:hermes-engine"))
292 | .using(project(":ReactAndroid:hermes-engine"))
293 | .because("On New Architecture we're building Hermes from source")
294 | }
295 | }
296 | }
297 |
298 | // Run this once to be able to run the application with BUCK
299 | // puts all compile dependencies into folder libs for BUCK to use
300 | task copyDownloadableDepsToLibs(type: Copy) {
301 | from configurations.implementation
302 | into 'libs'
303 | }
304 |
305 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
306 |
307 | def isNewArchitectureEnabled() {
308 | // To opt-in for the New Architecture, you can either:
309 | // - Set `newArchEnabled` to true inside the `gradle.properties` file
310 | // - Invoke gradle with `-newArchEnabled=true`
311 | // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
312 | return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
313 | }
314 |
--------------------------------------------------------------------------------
/example/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/example/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agontuk/react-native-geolocation-service/87448a0ab8c9bcc6c2ab0cc8b2db019ae5b400fd/example/android/app/debug.keystore
--------------------------------------------------------------------------------
/example/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/java/com/geoloc/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | *
This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.geoloc;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
22 | import com.facebook.react.ReactInstanceEventListener;
23 | import com.facebook.react.ReactInstanceManager;
24 | import com.facebook.react.bridge.ReactContext;
25 | import com.facebook.react.modules.network.NetworkingModule;
26 | import okhttp3.OkHttpClient;
27 |
28 | public class ReactNativeFlipper {
29 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
30 | if (FlipperUtils.shouldEnableFlipper(context)) {
31 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
32 |
33 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
34 | client.addPlugin(new ReactFlipperPlugin());
35 | client.addPlugin(new DatabasesFlipperPlugin(context));
36 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
37 | client.addPlugin(CrashReporterPlugin.getInstance());
38 |
39 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
40 | NetworkingModule.setCustomClientBuilder(
41 | new NetworkingModule.CustomClientBuilder() {
42 | @Override
43 | public void apply(OkHttpClient.Builder builder) {
44 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
45 | }
46 | });
47 | client.addPlugin(networkFlipperPlugin);
48 | client.start();
49 |
50 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
51 | // Hence we run if after all native modules have been initialized
52 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
53 | if (reactContext == null) {
54 | reactInstanceManager.addReactInstanceEventListener(
55 | new ReactInstanceEventListener() {
56 | @Override
57 | public void onReactContextInitialized(ReactContext reactContext) {
58 | reactInstanceManager.removeReactInstanceEventListener(this);
59 | reactContext.runOnNativeModulesQueueThread(
60 | new Runnable() {
61 | @Override
62 | public void run() {
63 | client.addPlugin(new FrescoFlipperPlugin());
64 | }
65 | });
66 | }
67 | });
68 | } else {
69 | client.addPlugin(new FrescoFlipperPlugin());
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
34 |
35 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/geoloc/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.geoloc;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.ReactRootView;
6 |
7 | public class MainActivity extends ReactActivity {
8 |
9 | /**
10 | * Returns the name of the main component registered from JavaScript. This is used to schedule
11 | * rendering of the component.
12 | */
13 | @Override
14 | protected String getMainComponentName() {
15 | return "GeoLoc";
16 | }
17 |
18 | /**
19 | * Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
20 | * you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
21 | * (Paper).
22 | */
23 | @Override
24 | protected ReactActivityDelegate createReactActivityDelegate() {
25 | return new MainActivityDelegate(this, getMainComponentName());
26 | }
27 |
28 | public static class MainActivityDelegate extends ReactActivityDelegate {
29 | public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
30 | super(activity, mainComponentName);
31 | }
32 |
33 | @Override
34 | protected ReactRootView createRootView() {
35 | ReactRootView reactRootView = new ReactRootView(getContext());
36 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
37 | reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
38 | return reactRootView;
39 | }
40 |
41 | @Override
42 | protected boolean isConcurrentRootEnabled() {
43 | // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
44 | // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
45 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/geoloc/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.geoloc;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactApplication;
7 | import com.facebook.react.ReactInstanceManager;
8 | import com.facebook.react.ReactNativeHost;
9 | import com.facebook.react.ReactPackage;
10 | import com.facebook.react.config.ReactFeatureFlags;
11 | import com.facebook.soloader.SoLoader;
12 | import com.geoloc.newarchitecture.MainApplicationReactNativeHost;
13 | import java.lang.reflect.InvocationTargetException;
14 | import java.util.List;
15 |
16 | public class MainApplication extends Application implements ReactApplication {
17 |
18 | private final ReactNativeHost mReactNativeHost =
19 | new ReactNativeHost(this) {
20 | @Override
21 | public boolean getUseDeveloperSupport() {
22 | return BuildConfig.DEBUG;
23 | }
24 |
25 | @Override
26 | protected List getPackages() {
27 | @SuppressWarnings("UnnecessaryLocalVariable")
28 | List packages = new PackageList(this).getPackages();
29 | // Packages that cannot be autolinked yet can be added manually here, for example:
30 | // packages.add(new MyReactNativePackage());
31 | return packages;
32 | }
33 |
34 | @Override
35 | protected String getJSMainModuleName() {
36 | return "index";
37 | }
38 | };
39 |
40 | private final ReactNativeHost mNewArchitectureNativeHost =
41 | new MainApplicationReactNativeHost(this);
42 |
43 | @Override
44 | public ReactNativeHost getReactNativeHost() {
45 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
46 | return mNewArchitectureNativeHost;
47 | } else {
48 | return mReactNativeHost;
49 | }
50 | }
51 |
52 | @Override
53 | public void onCreate() {
54 | super.onCreate();
55 | // If you opted-in for the New Architecture, we enable the TurboModule system
56 | ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
57 | SoLoader.init(this, /* native exopackage */ false);
58 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
59 | }
60 |
61 | /**
62 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like
63 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
64 | *
65 | * @param context
66 | * @param reactInstanceManager
67 | */
68 | private static void initializeFlipper(
69 | Context context, ReactInstanceManager reactInstanceManager) {
70 | if (BuildConfig.DEBUG) {
71 | try {
72 | /*
73 | We use reflection here to pick up the class that initializes Flipper,
74 | since Flipper library is not available in release mode
75 | */
76 | Class> aClass = Class.forName("com.geoloc.ReactNativeFlipper");
77 | aClass
78 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
79 | .invoke(null, context, reactInstanceManager);
80 | } catch (ClassNotFoundException e) {
81 | e.printStackTrace();
82 | } catch (NoSuchMethodException e) {
83 | e.printStackTrace();
84 | } catch (IllegalAccessException e) {
85 | e.printStackTrace();
86 | } catch (InvocationTargetException e) {
87 | e.printStackTrace();
88 | }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/geoloc/newarchitecture/MainApplicationReactNativeHost.java:
--------------------------------------------------------------------------------
1 | package com.geoloc.newarchitecture;
2 |
3 | import android.app.Application;
4 | import androidx.annotation.NonNull;
5 | import com.facebook.react.PackageList;
6 | import com.facebook.react.ReactInstanceManager;
7 | import com.facebook.react.ReactNativeHost;
8 | import com.facebook.react.ReactPackage;
9 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
10 | import com.facebook.react.bridge.JSIModulePackage;
11 | import com.facebook.react.bridge.JSIModuleProvider;
12 | import com.facebook.react.bridge.JSIModuleSpec;
13 | import com.facebook.react.bridge.JSIModuleType;
14 | import com.facebook.react.bridge.JavaScriptContextHolder;
15 | import com.facebook.react.bridge.ReactApplicationContext;
16 | import com.facebook.react.bridge.UIManager;
17 | import com.facebook.react.fabric.ComponentFactory;
18 | import com.facebook.react.fabric.CoreComponentsRegistry;
19 | import com.facebook.react.fabric.FabricJSIModuleProvider;
20 | import com.facebook.react.fabric.ReactNativeConfig;
21 | import com.facebook.react.uimanager.ViewManagerRegistry;
22 | import com.geoloc.BuildConfig;
23 | import com.geoloc.newarchitecture.components.MainComponentsRegistry;
24 | import com.geoloc.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | /**
29 | * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
30 | * TurboModule delegates and the Fabric Renderer.
31 | *
32 | *
Please note that this class is used ONLY if you opt-in for the New Architecture (see the
33 | * `newArchEnabled` property). Is ignored otherwise.
34 | */
35 | public class MainApplicationReactNativeHost extends ReactNativeHost {
36 | public MainApplicationReactNativeHost(Application application) {
37 | super(application);
38 | }
39 |
40 | @Override
41 | public boolean getUseDeveloperSupport() {
42 | return BuildConfig.DEBUG;
43 | }
44 |
45 | @Override
46 | protected List getPackages() {
47 | List packages = new PackageList(this).getPackages();
48 | // Packages that cannot be autolinked yet can be added manually here, for example:
49 | // packages.add(new MyReactNativePackage());
50 | // TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
51 | // packages.add(new TurboReactPackage() { ... });
52 | // If you have custom Fabric Components, their ViewManagers should also be loaded here
53 | // inside a ReactPackage.
54 | return packages;
55 | }
56 |
57 | @Override
58 | protected String getJSMainModuleName() {
59 | return "index";
60 | }
61 |
62 | @NonNull
63 | @Override
64 | protected ReactPackageTurboModuleManagerDelegate.Builder
65 | getReactPackageTurboModuleManagerDelegateBuilder() {
66 | // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
67 | // for the new architecture and to use TurboModules correctly.
68 | return new MainApplicationTurboModuleManagerDelegate.Builder();
69 | }
70 |
71 | @Override
72 | protected JSIModulePackage getJSIModulePackage() {
73 | return new JSIModulePackage() {
74 | @Override
75 | public List getJSIModules(
76 | final ReactApplicationContext reactApplicationContext,
77 | final JavaScriptContextHolder jsContext) {
78 | final List specs = new ArrayList<>();
79 |
80 | // Here we provide a new JSIModuleSpec that will be responsible of providing the
81 | // custom Fabric Components.
82 | specs.add(
83 | new JSIModuleSpec() {
84 | @Override
85 | public JSIModuleType getJSIModuleType() {
86 | return JSIModuleType.UIManager;
87 | }
88 |
89 | @Override
90 | public JSIModuleProvider getJSIModuleProvider() {
91 | final ComponentFactory componentFactory = new ComponentFactory();
92 | CoreComponentsRegistry.register(componentFactory);
93 |
94 | // Here we register a Components Registry.
95 | // The one that is generated with the template contains no components
96 | // and just provides you the one from React Native core.
97 | MainComponentsRegistry.register(componentFactory);
98 |
99 | final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
100 |
101 | ViewManagerRegistry viewManagerRegistry =
102 | new ViewManagerRegistry(
103 | reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
104 |
105 | return new FabricJSIModuleProvider(
106 | reactApplicationContext,
107 | componentFactory,
108 | ReactNativeConfig.DEFAULT_CONFIG,
109 | viewManagerRegistry);
110 | }
111 | });
112 | return specs;
113 | }
114 | };
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/geoloc/newarchitecture/components/MainComponentsRegistry.java:
--------------------------------------------------------------------------------
1 | package com.geoloc.newarchitecture.components;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.proguard.annotations.DoNotStrip;
5 | import com.facebook.react.fabric.ComponentFactory;
6 | import com.facebook.soloader.SoLoader;
7 |
8 | /**
9 | * Class responsible to load the custom Fabric Components. This class has native methods and needs a
10 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
11 | * folder for you).
12 | *
13 | *
Please note that this class is used ONLY if you opt-in for the New Architecture (see the
14 | * `newArchEnabled` property). Is ignored otherwise.
15 | */
16 | @DoNotStrip
17 | public class MainComponentsRegistry {
18 | static {
19 | SoLoader.loadLibrary("fabricjni");
20 | }
21 |
22 | @DoNotStrip private final HybridData mHybridData;
23 |
24 | @DoNotStrip
25 | private native HybridData initHybrid(ComponentFactory componentFactory);
26 |
27 | @DoNotStrip
28 | private MainComponentsRegistry(ComponentFactory componentFactory) {
29 | mHybridData = initHybrid(componentFactory);
30 | }
31 |
32 | @DoNotStrip
33 | public static MainComponentsRegistry register(ComponentFactory componentFactory) {
34 | return new MainComponentsRegistry(componentFactory);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/example/android/app/src/main/java/com/geoloc/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java:
--------------------------------------------------------------------------------
1 | package com.geoloc.newarchitecture.modules;
2 |
3 | import com.facebook.jni.HybridData;
4 | import com.facebook.react.ReactPackage;
5 | import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.soloader.SoLoader;
8 | import java.util.List;
9 |
10 | /**
11 | * Class responsible to load the TurboModules. This class has native methods and needs a
12 | * corresponding C++ implementation/header file to work correctly (already placed inside the jni/
13 | * folder for you).
14 | *
15 | *