Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ Apple requires privacy descriptions to be specified in `Info.plist` for location
- `NSLocationAlwaysAndWhenInUseUsageDescription` (`Privacy - Location Always and When In Use Usage Description`)
- `NSLocationWhenInUseUsageDescription` (`Privacy - Location When In Use Usage Description`)

> [!NOTE]
> This Capacitor plugin does not support background geolocation directly. However, it relies on
> [`ion-ios-geolocation`](https://github.com/ionic-team/ion-ios-geolocation), which can report
> location in the background. As a result, Apple requires you to include a
> `NSLocationAlwaysAndWhenInUseUsageDescription` entry in your `Info.plist`. Since this permission
> prompt won’t appear to users, you can safely use the same description string as for
> `NSLocationWhenInUseUsageDescription`.
:::info[Background Location Usage Strings]

This Capacitor plugin does not support background geolocation directly. However, it relies on
[`ion-ios-geolocation`](https://github.com/ionic-team/ion-ios-geolocation), which can report
location in the background. As a result, Apple requires you to include a
`NSLocationAlwaysAndWhenInUseUsageDescription` entry in your `Info.plist`. Since this permission
prompt won’t appear to users, you can safely use the same description string as for
`NSLocationWhenInUseUsageDescription`.

:::info

Read about [Configuring `Info.plist`](https://capacitorjs.com/docs/ios/configuration#configuring-infoplist) in the [iOS Guide](https://capacitorjs.com/docs/ios) for more information on setting iOS permissions in Xcode

Expand All @@ -39,6 +42,12 @@ This plugin requires the following permissions be added to your `AndroidManifest

The first two permissions ask for location data, both fine and coarse, and the last line is optional but necessary if your app _requires_ GPS to function. You may leave it out, though keep in mind that this may mean your app is installed on devices lacking GPS hardware.

:::note

If you only require approximate location (variable accuracy but usually around 2 kilometers), you may just declare `ACCESS_COARSE_LOCATION` and `<uses-feature`, and use `enableHighAccuracy=false` when requesting location

:::note

Read about [Setting Permissions](https://capacitorjs.com/docs/android/configuration#setting-permissions) in the [Android Guide](https://capacitorjs.com/docs/android) for more information on setting Android permissions.


Expand Down Expand Up @@ -247,3 +256,4 @@ The following table list all the plugin errors:
| OS-PLUG-GLOC-0015 | Android | Google Play Services error. |
| OS-PLUG-GLOC-0016 | Android | Location settings error. |
| OS-PLUG-GLOC-0017 | Android | Unable to retrieve location because device has both Network and Location turned off. |
| OS-PLUG-GLOC-0018 | Android | Location permissions are not declared in manifest. Make sure at least ACCESS_COARSE_LOCATION is declared in AndroidManifest.xml, and optionally ACCESS_FINE_LOCATION if you require precise location access. |
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,9 @@ object GeolocationErrors {
code = formatErrorCode(17),
message = "Unable to retrieve location because device has both Network and Location turned off."
)

val LOCATION_MANIFEST_PERMISSIONS_MISSING = ErrorInfo(
code = formatErrorCode(18),
message = "Location permissions are not declared in manifest. Make sure at least ACCESS_COARSE_LOCATION is declared in AndroidManifest.xml, and optionally ACCESS_FINE_LOCATION if you require precise location access."
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import com.getcapacitor.PluginMethod
import com.getcapacitor.annotation.CapacitorPlugin
import com.getcapacitor.annotation.Permission
import com.getcapacitor.annotation.PermissionCallback
import com.google.android.gms.location.LocationServices
import io.ionic.libs.iongeolocationlib.controller.IONGLOCController
import io.ionic.libs.iongeolocationlib.model.IONGLOCException
import io.ionic.libs.iongeolocationlib.model.IONGLOCLocationOptions
Expand Down Expand Up @@ -116,7 +115,10 @@ class GeolocationPlugin : Plugin() {
callbackName: String,
onPermissionGranted: () -> Unit
) {
val alias = getAlias(call)
val alias = getAlias(call) ?: run {
call.sendError(GeolocationErrors.LOCATION_MANIFEST_PERMISSIONS_MISSING)
return
}
if (getPermissionState(alias) != PermissionState.GRANTED) {
requestPermissionForAlias(alias, call, callbackName)
} else {
Expand Down Expand Up @@ -178,19 +180,20 @@ class GeolocationPlugin : Plugin() {
}

/**
* Gets the appropriate permission alias
* Gets the appropriate permission alias, based on the Android version,
* the permissions declared in the manifest
* and the enableHighAccuracy option provided by the caller.
* @param call the plugin call
* @return String with correct alias
* @return String with correct alias or null if no permissions can be requested
*/
private fun getAlias(call: PluginCall): String {
var alias = LOCATION_ALIAS
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val enableHighAccuracy = call.getBoolean("enableHighAccuracy") ?: false
if (!enableHighAccuracy) {
alias = COARSE_LOCATION_ALIAS
}
}
return alias
private fun getAlias(call: PluginCall): String? {
val hasFine = isPermissionDeclared(LOCATION_ALIAS)
val hasCoarse = isPermissionDeclared(COARSE_LOCATION_ALIAS)
if (!hasFine && !hasCoarse) return null
val enableHighAccuracy = call.getBoolean("enableHighAccuracy") ?: false
val shouldRequestFine =
hasFine && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || enableHighAccuracy)
return if (shouldRequestFine) LOCATION_ALIAS else COARSE_LOCATION_ALIAS
}

/**
Expand Down Expand Up @@ -316,7 +319,7 @@ class GeolocationPlugin : Plugin() {
}

/**
* Extension function to return a unsuccessful plugin result
* Extension function to return an unsuccessful plugin result
* @param error error class representing the error to return, containing a code and message
*/
private fun PluginCall.sendError(error: GeolocationErrors.ErrorInfo) {
Expand Down
23 changes: 21 additions & 2 deletions example-app/src/js/capacitor-welcome.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Capacitor } from '@capacitor/core';
import { SplashScreen } from '@capacitor/splash-screen';
import { Geolocation } from '@capacitor/geolocation';

Expand Down Expand Up @@ -63,6 +64,7 @@ window.customElements.define(
<p>Below are the several features for the capacitor geolocation plugin. You may be prompted to grant location permission to the application.</p>
<button id="check-permission" class="button">Check permission</button>
<button id="request-permission" class="button">Request permission</button>
<button id="request-coarse-permission" class="button" style="display: none;">Request coarse permission</button>
<br><br>
<label for="timeoutInput">timeout: </label>
<input type="number" id="timeoutInput" name="timeoutInput"><br>
Expand Down Expand Up @@ -98,13 +100,30 @@ window.customElements.define(
connectedCallback() {
const self = this;

const isAndroid = Capacitor.getPlatform() === 'android';

if (isAndroid) {
self.shadowRoot.querySelector('#request-coarse-permission').style.display = 'inline-block';
}

function permissionStatusToString(permissionStatus) {
if (isAndroid) {
return `Permissions are:\nlocation (fine+coarse) = ${permissionStatus.location}\ncoarseLocation = ${permissionStatus.coarseLocation}`;
}
return `Permissions are:\nlocation = ${permissionStatus.location}`;
}

self.shadowRoot.querySelector('#check-permission').addEventListener('click', async function (e) {
const permissionStatus = await Geolocation.checkPermissions();
alert(`Permissions are:\nlocation = ${permissionStatus.location}`);
alert(permissionStatusToString(permissionStatus));
});
self.shadowRoot.querySelector('#request-permission').addEventListener('click', async function (e) {
const permissionStatus = await Geolocation.requestPermissions();
alert(`Permissions are:\nlocation = ${permissionStatus.location}`);
alert(permissionStatusToString(permissionStatus));
});
self.shadowRoot.querySelector('#request-coarse-permission').addEventListener('click', async function (e) {
const permissionStatus = await Geolocation.requestPermissions({ permissions: ['coarseLocation'] });
alert(permissionStatusToString(permissionStatus));
});

self.shadowRoot.querySelector('#current-location').addEventListener('click', async function (e) {
Expand Down