From c8f9706dbb24e87b873aba322820a3af1e9d6a62 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 12 Jun 2026 18:02:32 +0100 Subject: [PATCH 1/6] fix(android): Only request permissions if declared --- .../plugins/geolocation/GeolocationErrors.kt | 5 ++++ .../plugins/geolocation/GeolocationPlugin.kt | 27 +++++++++++-------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationErrors.kt b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationErrors.kt index 7a606c6..258e6a5 100644 --- a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationErrors.kt +++ b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationErrors.kt @@ -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." + ) } \ No newline at end of file diff --git a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt index 8b8c1a5..6649081 100644 --- a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt +++ b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt @@ -116,7 +116,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 { @@ -178,19 +181,21 @@ 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 - } + private fun getAlias(call: PluginCall): String? { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { + return LOCATION_ALIAS } - return alias + val hasFine = isPermissionDeclared(LOCATION_ALIAS) + val hasCoarse = isPermissionDeclared(COARSE_LOCATION_ALIAS) + if (!hasFine && !hasCoarse) return null + val enableHighAccuracy = call.getBoolean("enableHighAccuracy") ?: false + return if (hasFine && enableHighAccuracy) LOCATION_ALIAS else COARSE_LOCATION_ALIAS } /** From 34228d1fdaaf5c7f14629be36347b68cd9c053f7 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Fri, 12 Jun 2026 18:12:59 +0100 Subject: [PATCH 2/6] chore(example-app): allow testing coarse permission --- example-app/src/js/capacitor-welcome.js | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/example-app/src/js/capacitor-welcome.js b/example-app/src/js/capacitor-welcome.js index 1e1acc7..f06aa21 100644 --- a/example-app/src/js/capacitor-welcome.js +++ b/example-app/src/js/capacitor-welcome.js @@ -1,3 +1,4 @@ +import { Capacitor } from '@capacitor/core'; import { SplashScreen } from '@capacitor/splash-screen'; import { Geolocation } from '@capacitor/geolocation'; @@ -63,6 +64,7 @@ window.customElements.define(

Below are the several features for the capacitor geolocation plugin. You may be prompted to grant location permission to the application.

+


@@ -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) { From 8092d495b6d6220ebf99ac11a69e3137147b221f Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 15 Jun 2026 11:16:36 +0100 Subject: [PATCH 3/6] docs: Additional error in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f9652fe..bbf7297 100644 --- a/README.md +++ b/README.md @@ -247,3 +247,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. | \ No newline at end of file From 3632eca675e3267d00852232a2197cf388425b35 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 15 Jun 2026 11:35:11 +0100 Subject: [PATCH 4/6] chore: minor Android Studio changes --- .../com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt index 6649081..09c3fdc 100644 --- a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt +++ b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt @@ -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 @@ -321,7 +320,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) { From d32393def93fbd25b029245b355705371d0097e4 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 15 Jun 2026 11:46:53 +0100 Subject: [PATCH 5/6] fix(android): Android < 12 with only COARSE in Manifest --- .../capacitorjs/plugins/geolocation/GeolocationPlugin.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt index 09c3fdc..5928e0c 100644 --- a/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt +++ b/android/src/main/kotlin/com/capacitorjs/plugins/geolocation/GeolocationPlugin.kt @@ -187,14 +187,13 @@ class GeolocationPlugin : Plugin() { * @return String with correct alias or null if no permissions can be requested */ private fun getAlias(call: PluginCall): String? { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) { - return LOCATION_ALIAS - } val hasFine = isPermissionDeclared(LOCATION_ALIAS) val hasCoarse = isPermissionDeclared(COARSE_LOCATION_ALIAS) if (!hasFine && !hasCoarse) return null val enableHighAccuracy = call.getBoolean("enableHighAccuracy") ?: false - return if (hasFine && enableHighAccuracy) LOCATION_ALIAS else COARSE_LOCATION_ALIAS + val shouldRequestFine = + hasFine && (Build.VERSION.SDK_INT < Build.VERSION_CODES.S || enableHighAccuracy) + return if (shouldRequestFine) LOCATION_ALIAS else COARSE_LOCATION_ALIAS } /** From 1ee4b102011af9b0130bbfa8e3dd3c45f2d88e48 Mon Sep 17 00:00:00 2001 From: OS-pedrogustavobilro Date: Mon, 15 Jun 2026 13:48:37 +0100 Subject: [PATCH 6/6] docs: Notes on permissions for Android and iOS --- README.md | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index bbf7297..72042be 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 `