1
votes

I have an app working with Cordova, and I have installed this plugin from the official repository:

Plugin installation: cordova plugin add cordova-plugin-geolocation

Repository: https://github.com/apache/cordova-plugin-geolocation

Device ready event:

onDeviceReady: function() {
            app.receivedEvent('deviceready');
            console.log('deviceready');
             loadPosition();

        }

In the deviceready event I call to this function:

Load Position Function:

function loadPosition() {

    // onSuccess Callback
    // This method accepts a Position object, which contains the
    // current GPS coordinates
    //
    var onSuccess = function (position) {

        localStorage.setItem("latitude", position.coords.latitude);
        localStorage.setItem("longitude", position.coords.longitude);
        localStorage.setItem("position", true);


    };

    // onError Callback receives a PositionError object
    //
    function onError(error) {
        localStorage.setItem("position", false);
        alertErrorPosition();
    }

    function alertErrorPosition() {
        navigator.notification.alert(
            'Necesitamos utilizar tu ubicación para poder hacer funcionar la aplicación. Reinicia la aplicación o vuélve a instalarla . Gracias.',  // message
            null,         // callback
            '¡Error!',            // title
            'Ok'                  // buttonName
            );
    }

    navigator.geolocation.getCurrentPosition(onSuccess, onError);
}

The problem:

On Android, I don't see the alert error if the App can't obtain the localization (for example, the GPS is not actived), but on iOS, if the user deny the access, I can see the error alert.

If the user has the GPS activated, I don't have any problem and the App obtain correctly the latitude and longitude.

I have tested it in the emulator and in a real device. I'm using Android Studio for testing.

Thanks!!

1
the geolocation plugin on android doesn't include any native code, so the problem is on the webview, it might call the error callback on some devices and might not call it on others depending on the android version and the phone vendorjcesarmobile

1 Answers

5
votes

The effect of turning off GPS on an Android device (e.g. changing setting Location Mode to "Battery Saving") varies depending on the Android version: either the OS is never able to retreive a high-accuracy position, so the TIMEOUT error occurs (PERMISSION_DENIED will not be received on Android) or a low accuracy position will be retrieved and passed instead using Wifi/cell triangulation.

I'd suggest using watchPosition() instead of getCurrentPosition() to retrieve the location; getCurrentPosition() makes a single request for the device position at that current point in time, so the position timeout may occur before the GPS hardware on the device has had a chance to get a position fix, whereas using watchPosition() you can setup a watcher which will call the success function each time the OS receives a location update from the GPS hardware. If you only want a single location, clear the watcher after receiving a position of sufficient accuracy. If GPS is turned off on the Android device when the watcher is added, it will continue to return a TIMEOUT error; my workaround for this is to clear and re-add the watcher after a number of consecutive errors. And before adding the watcher for the first time, you can use this plugin to check if GPS is turned on and if not, offer to redirect the user to the Android Location Settings page so they can turn it on.

So something along these lines:

var MAX_POSITION_ERRORS_BEFORE_RESET = 3,
MIN_ACCURACY_IN_METRES = 20,
positionWatchId = null, 
watchpositionErrorCount = 0,
options = {
    maximumAge: 60000, 
    timeout: 15000, 
    enableHighAccuracy: true
};

function addWatch(){
    positionWatchId = navigator.geolocation.watchPosition(onWatchPositionSuccess, onWatchPositionError, options);
}

function clearWatch(){
    navigator.geolocation.clearWatch(positionWatchId);
}

function onWatchPositionSuccess(position) {
    watchpositionErrorCount = 0;

    // Reject if accuracy is not sufficient
    if(position.coords.accuracy > MIN_ACCURACY_IN_METRES){
      return;        
    }

    // If only single position is required, clear watcher
    clearWatch();

    // Do something with position
    var lat = position.coords.latitude,   
    lon = position.coords.longitude;
}


function onWatchPositionError(err) {
    watchpositionErrorCount++;
    if (err.code == 3 // TIMEOUT
        && watchpositionErrorCount >= MAX_POSITION_ERRORS_BEFORE_RESET) {        
        clearWatch();
        addWatch();
        watchpositionErrorCount = 0;
    }

}

function checkIfLocationIsOn(){
    cordova.plugins.diagnostic.isLocationEnabled(function(enabled){
            console.log("Location is " + (enabled ? "enabled" : "disabled"));
            if(!enabled){
                navigator.notification.confirm("Your GPS is switched OFF - would you like to open the Settings page to turn it ON?", 
                    function(result){
                        if(result == 1){ // Yes
                            cordova.plugins.diagnostic.switchToLocationSettings();
                        }
                    }, "Open Location Settings?");
            }else{
                if(positionWatchId){
                    clearWatch();
                }
                addWatch();
            }
        }, function(error){
            console.error("The following error occurred: "+error);
        }
    );
}

document.addEventListener("deviceready", checkIfLocationIsOn, false); // Check on app startup
document.addEventListener("resume", checkIfLocationIsOn, false); // Check on resume app from background