Search code examples
cordovagoogle-mapsgeolocationtimeoutsencha-touch

Geolocation always timing out in cordova app


I am working on a Sencha Touch (2.3.1) application, packaged using Cordova 3.5.0-0.2.7. I am trying to read GPS coordinates using:

navigator.geolocation.getCurrentPosition()

But the request is always timing out on Android Phones, while it is working fine in Chrome emulator. I have tried using watchPosition() also.

Any help would be much appreciated.


Solution

  • If you've not already, install cordova-plugin-geolocation into your project - i.e. cordova plugin add cordova-plugin-geolocation - this will add the appropriate permissions to your Android manifest, as Mike Dailor rightly points out that you need.

    What options are you passing as the third parameter to getCurrentPosition()? The geolocationOptions object has 3 properties: timeout, maxAge and enableHighAccuracy.

    Assuming you want an accurate position (i.e. GPS tracking/sat-nav type of app), setting enableHighAccuracy: true causes your app to ask the OS retreive a position using the GPS hardware. In this case, you want to set a timeout value which allows enough time for the GPS hardware to obtain a fix for the first time, otherwise the timeout will occur before it has a chance to get a fix.

    Also bear in mind the that 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 consequetive errors.

    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;
        }
    
    }
    addWatch();