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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.1.0]

## 2025-10-03

- Feature(android): Location fallback in case of Play Services failure or airplane mode.

## [1.0.3]

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.outsystems.plugins.geolocation",
"version": "1.0.3",
"version": "1.1.0",
"private": true,
"dependencies": {},
"scripts": {
Expand Down
14 changes: 13 additions & 1 deletion packages/cordova-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ cordova plugin add <path-to-repo-local-clone>
* [`getCurrentPosition(...)`](#getcurrentposition)
* [`watchPosition(...)`](#watchposition)
* [`clearWatch(...)`](#clearwatch)
* [Interfaces](#interfaces)
* [Type Aliases](#type-aliases)

</docgen-index>
Expand Down Expand Up @@ -80,12 +81,23 @@ Clear a given watch
--------------------


### Interfaces


#### Position

| Prop | Type | Description |
| ------------ | ------------------- | ----------- |
| **`line`** | <code>number</code> | &gt;= 1 |
| **`column`** | <code>number</code> | &gt;= 0 |


### Type Aliases


#### CurrentPositionOptions

<code>{ /** * High accuracy mode (such as GPS, if available) * * On Android 12+ devices it will be ignored if users didn't grant * ACCESS_FINE_LOCATION permissions (can be checked with location alias). * * @default false * @since 1.0.0 */ enableHighAccuracy?: boolean; /** * The maximum wait time in milliseconds for location updates. * * In Android, since version 4.0.0 of the plugin, timeout gets ignored for getCurrentPosition. * * @default 10000 * @since 1.0.0 */ timeout?: number; /** * The maximum age in milliseconds of a possible cached position that is acceptable to return * * @default 0 * @since 1.0.0 */ maximumAge?: number; /** * The minumum update interval for location updates. * * If location updates are available faster than this interval then an update * will only occur if the minimum update interval has expired since the last location update. * * This parameter is only available for Android. It has no effect on iOS or Web platforms. * * @default 5000 * @since 6.1.0 */ minimumUpdateInterval?: number; }</code>
<code>{ /** * High accuracy mode (such as GPS, if available) * * On Android 12+ devices it will be ignored if users didn't grant * ACCESS_FINE_LOCATION permissions (can be checked with location alias). * * @default false * @since 1.0.0 */ enableHighAccuracy?: boolean; /** * The maximum wait time in milliseconds for location updates. * * In Android, since version 4.0.0 of the plugin, timeout gets ignored for getCurrentPosition. * * @default 10000 * @since 1.0.0 */ timeout?: number; /** * The maximum age in milliseconds of a possible cached position that is acceptable to return * * @default 0 * @since 1.0.0 */ maximumAge?: number; /** * The minumum update interval for location updates. * * If location updates are available faster than this interval then an update * will only occur if the minimum update interval has expired since the last location update. * * This parameter is only available for Android. It has no effect on iOS or Web platforms. * * @default 5000 * @since 1.0.0 */ minimumUpdateInterval?: number; /** * This option applies to Android only. * * Whether to fall back to the Android framework's `LocationManager` in case Google Play Service's location settings checks fail. * This can happen for multiple reasons - e.g. device has no Play Services or device has no network connection (Airplane Mode) * If set to `false`, failures are propagated to the caller. * Note that `LocationManager` may not be as effective as Google Play Services implementation. * If the device's in airplane mode, only the GPS provider is used, which may take longer to return a location, depending on GPS signal. * This means that to receive location in such circumstances, you may need to provide a higher timeout. * * @default true * @since 1.1.0 */ enableLocationFallback?: boolean }</code>


#### Position
Expand Down
32 changes: 18 additions & 14 deletions packages/cordova-plugin/android/OSGeolocation.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package com.outsystems.plugins.geolocation

import com.google.android.gms.location.LocationServices
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import com.google.gson.Gson
import io.ionic.libs.iongeolocationlib.controller.IONGLOCController
import io.ionic.libs.iongeolocationlib.model.IONGLOCException
import io.ionic.libs.iongeolocationlib.model.IONGLOCLocationOptions
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import org.apache.cordova.CallbackContext
import org.apache.cordova.CordovaInterface
Expand All @@ -13,14 +20,6 @@ import org.apache.cordova.PermissionHelper
import org.apache.cordova.PluginResult
import org.json.JSONArray
import org.json.JSONObject
import android.Manifest
import android.content.pm.PackageManager
import androidx.activity.result.contract.ActivityResultContracts
import io.ionic.libs.iongeolocationlib.controller.IONGLOCController
import io.ionic.libs.iongeolocationlib.model.IONGLOCException
import io.ionic.libs.iongeolocationlib.model.IONGLOCLocationOptions
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableSharedFlow

/**
* Cordova bridge, inherits from CordovaPlugin
Expand All @@ -40,6 +39,7 @@ class OSGeolocation : CordovaPlugin() {
private const val TIMEOUT = "timeout"
private const val MAXIMUM_AGE = "maximumAge"
private const val ENABLE_HIGH_ACCURACY = "enableHighAccuracy"
private const val ENABLE_FALLBACK = "enableLocationFallback"
}

override fun initialize(cordova: CordovaInterface, webView: CordovaWebView) {
Expand All @@ -54,10 +54,7 @@ class OSGeolocation : CordovaPlugin() {
}
}

this.controller = IONGLOCController(
LocationServices.getFusedLocationProviderClient(cordova.context),
activityLauncher
)
this.controller = IONGLOCController(cordova.context, activityLauncher)
}

override fun onDestroy() {
Expand Down Expand Up @@ -103,7 +100,9 @@ class OSGeolocation : CordovaPlugin() {
val locationOptions = IONGLOCLocationOptions(
options.getLong(TIMEOUT),
options.getLong(MAXIMUM_AGE),
options.getBoolean(ENABLE_HIGH_ACCURACY))
options.getBoolean(ENABLE_HIGH_ACCURACY),
enableLocationManagerFallback = options.optBoolean(ENABLE_FALLBACK, true)
)

val locationResult = controller.getCurrentPosition(cordova.activity, locationOptions)

Expand Down Expand Up @@ -137,6 +136,7 @@ class OSGeolocation : CordovaPlugin() {
timeout = options.getLong(TIMEOUT),
maximumAge = options.getLong(MAXIMUM_AGE),
enableHighAccuracy = options.getBoolean(ENABLE_HIGH_ACCURACY),
enableLocationManagerFallback = options.optBoolean(ENABLE_FALLBACK, true)
)

controller.addWatch(cordova.activity, locationOptions, watchId).collect { result ->
Expand Down Expand Up @@ -175,6 +175,10 @@ class OSGeolocation : CordovaPlugin() {
callbackContext.sendError(OSGeolocationErrors.LOCATION_SETTINGS_ERROR)
}

is IONGLOCException.IONGLOCLocationAndNetworkDisabledException -> {
callbackContext.sendError(OSGeolocationErrors.NETWORK_LOCATION_DISABLED_ERROR)
}

is IONGLOCException.IONGLOCInvalidTimeoutException -> {
callbackContext.sendError(OSGeolocationErrors.INVALID_TIMEOUT)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/cordova-plugin/android/OSGeolocationErrors.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,9 @@ object OSGeolocationErrors {
code = formatErrorCode(16),
message = "Location settings error."
)

val NETWORK_LOCATION_DISABLED_ERROR = ErrorInfo(
code = formatErrorCode(17),
message = "Unable to retrieve location because device has both Network and Location turned off."
)
}
2 changes: 1 addition & 1 deletion packages/cordova-plugin/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ allprojects {
}

dependencies{
implementation("io.ionic.libs:iongeolocation-android:1.0.0")
implementation("io.ionic.libs:iongeolocation-android:2.0.0")
implementation("androidx.browser:browser:1.8.0")
implementation("com.google.android.gms:play-services-auth:21.2.0")
implementation("com.google.android.gms:play-services-location:21.3.0")
Expand Down
16 changes: 15 additions & 1 deletion packages/cordova-plugin/dist/definitions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,23 @@ export type CurrentPositionOptions = {
* This parameter is only available for Android. It has no effect on iOS or Web platforms.
*
* @default 5000
* @since 6.1.0
* @since 1.0.0
*/
minimumUpdateInterval?: number;
/**
* This option applies to Android only.
*
* Whether to fall back to the Android framework's `LocationManager` in case Google Play Service's location settings checks fail.
* This can happen for multiple reasons - e.g. device has no Play Services or device has no network connection (Airplane Mode)
* If set to `false`, failures are propagated to the caller.
* Note that `LocationManager` may not be as effective as Google Play Services implementation.
* If the device's in airplane mode, only the GPS provider is used, which may take longer to return a location, depending on GPS signal.
* This means that to receive location in such circumstances, you may need to provide a higher timeout.
*
* @default true
* @since 1.1.0
*/
enableLocationFallback?: boolean;
};
export type ClearWatchOptions = {
id: string;
Expand Down
31 changes: 16 additions & 15 deletions packages/cordova-plugin/dist/plugin.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@ function s(t) {
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
get(e, n) {
return new Proxy({}, {
get(w, r) {
return (c, p, n) => {
const i = t.Capacitor.Plugins[o];
get(w, o) {
return (c, p, r) => {
const i = t.Capacitor.Plugins[n];
if (i === void 0) {
n(new Error(`Capacitor plugin ${o} not found`));
r(new Error(`Capacitor plugin ${n} not found`));
return;
}
if (typeof i[r] != "function") {
n(new Error(`Method ${r} not found in Capacitor plugin ${o}`));
if (typeof i[o] != "function") {
r(new Error(`Method ${o} not found in Capacitor plugin ${n}`));
return;
}
(async () => {
try {
const a = await i[r](c);
const a = await i[o](c);
p(a);
} catch (a) {
n(a);
r(a);
}
})();
};
Expand All @@ -36,20 +36,21 @@ function u(t) {
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
return t.cordova.plugins[o];
get(e, n) {
return t.cordova.plugins[n];
}
}
);
}
function y(t = false) {
window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window);
function f(t = false) {
typeof window > "u" || (window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window));
}
const CurrentPositionOptionsDefault = {
enableHighAccuracy: false,
timeout: 1e3,
maximumAge: 0,
minimumUpdateInterval: 5e3
minimumUpdateInterval: 5e3,
enableLocationFallback: true
};
const ClearWatchOptionsDefault = {
id: "-1"
Expand Down Expand Up @@ -106,4 +107,4 @@ module.exports = {
watchPosition,
clearWatch
};
y(true);
f(true);
31 changes: 16 additions & 15 deletions packages/cordova-plugin/dist/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,25 @@
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
get(e, n) {
return new Proxy({}, {
get(w, r) {
return (c, p, n) => {
const i = t.Capacitor.Plugins[o];
get(w, o) {
return (c, p, r) => {
const i = t.Capacitor.Plugins[n];
if (i === void 0) {
n(new Error(`Capacitor plugin ${o} not found`));
r(new Error(`Capacitor plugin ${n} not found`));
return;
}
if (typeof i[r] != "function") {
n(new Error(`Method ${r} not found in Capacitor plugin ${o}`));
if (typeof i[o] != "function") {
r(new Error(`Method ${o} not found in Capacitor plugin ${n}`));
return;
}
(async () => {
try {
const a = await i[r](c);
const a = await i[o](c);
p(a);
} catch (a) {
n(a);
r(a);
}
})();
};
Expand All @@ -38,20 +38,21 @@
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
return t.cordova.plugins[o];
get(e, n) {
return t.cordova.plugins[n];
}
}
);
}
function y(t = false) {
window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window);
function f(t = false) {
typeof window > "u" || (window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window));
}
const CurrentPositionOptionsDefault = {
enableHighAccuracy: false,
timeout: 1e3,
maximumAge: 0,
minimumUpdateInterval: 5e3
minimumUpdateInterval: 5e3,
enableLocationFallback: true
};
const ClearWatchOptionsDefault = {
id: "-1"
Expand Down Expand Up @@ -108,5 +109,5 @@
watchPosition,
clearWatch
};
y(true);
f(true);
});
31 changes: 16 additions & 15 deletions packages/cordova-plugin/dist/plugin.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ function s(t) {
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
get(e, n) {
return new Proxy({}, {
get(w, r) {
return (c, p, n) => {
const i = t.Capacitor.Plugins[o];
get(w, o) {
return (c, p, r) => {
const i = t.Capacitor.Plugins[n];
if (i === void 0) {
n(new Error(`Capacitor plugin ${o} not found`));
r(new Error(`Capacitor plugin ${n} not found`));
return;
}
if (typeof i[r] != "function") {
n(new Error(`Method ${r} not found in Capacitor plugin ${o}`));
if (typeof i[o] != "function") {
r(new Error(`Method ${o} not found in Capacitor plugin ${n}`));
return;
}
(async () => {
try {
const a = await i[r](c);
const a = await i[o](c);
p(a);
} catch (a) {
n(a);
r(a);
}
})();
};
Expand All @@ -35,20 +35,21 @@ function u(t) {
t.CapacitorUtils.Synapse = new Proxy(
{},
{
get(e, o) {
return t.cordova.plugins[o];
get(e, n) {
return t.cordova.plugins[n];
}
}
);
}
function y(t = false) {
window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window);
function f(t = false) {
typeof window > "u" || (window.CapacitorUtils = window.CapacitorUtils || {}, window.Capacitor !== void 0 && !t ? s(window) : window.cordova !== void 0 && u(window));
}
const CurrentPositionOptionsDefault = {
enableHighAccuracy: false,
timeout: 1e3,
maximumAge: 0,
minimumUpdateInterval: 5e3
minimumUpdateInterval: 5e3,
enableLocationFallback: true
};
const ClearWatchOptionsDefault = {
id: "-1"
Expand Down Expand Up @@ -105,4 +106,4 @@ module.exports = {
watchPosition,
clearWatch
};
y(true);
f(true);
2 changes: 1 addition & 1 deletion packages/cordova-plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "com.outsystems.plugins.geolocation",
"displayName": "Geolocation",
"version": "1.0.1",
"version": "1.1.0",
"description": "Geolocation plugin for Cordova",
"scripts": {
"lint": "npm run eslint && npm run prettier -- --check",
Expand Down
Loading