dpa99c / cordova-plugin-request-location-accuracy

Cordova/Phonegap plugin for Android and iOS to request enabling/changing of Location Services by triggering a native dialog
96 stars 62 forks source link

Cordova Request Location Accuracy Plugin Latest Stable Version Total Downloads

Table of Contents

Overview

This Cordova/Phonegap plugin for Android and iOS to request enabling/changing of Location Services by triggering a native dialog from within the app, avoiding the need for the user to leave your app to change location settings manually.

donate

I dedicate a considerable amount of my free time to developing and maintaining this Cordova plugin, along with my other Open Source software. To help ensure this plugin is kept updated, new features are added and bugfixes are implemented quickly, please donate a couple of dollars (or a little more if you can stretch) as this will help me to afford to dedicate time to its maintenance. Please consider donating if you're using this plugin in an app that makes you money, if you're being paid to make the app, if you're asking for new features or priority bug fixes.

Why is this plugin not just part of cordova-diagnostic-plugin?

Because:

However, since this plugin requires runtime authorization to use location to be granted by the user, you may want to use the diagnostic plugin to check for/request location permission.

Android overview

Example Android app screencapture

On Android, this plugin allows an app to request a specific accuracy for Location Services. If the requested accuracy is higher than the current Location Mode setting of the device, the user is asked to confirm the change with a Yes/No dialog.

For example, if a navigation app that requires GPS, the plugin is able to switch on Location Services or change the Location Mode from low accuracy to high accuracy, without the user needing to leave the app to do this manually on the Location Settings page.

It uses the Google Play Services Location API (v7+) to change the device location settings. In case the user doesn't have an up-to-date version of Google Play Services or there's some other problem accessing it, you may want to use another of my plugins, cordova.plugins.diagnostic as a fallback. This is able to switch the user directly to the Location Settings page where they can manually change the Location Mode.

Android Play Services library dependency

This plugin depends on the Google Play Services library so you must install the "Google Repository" package under the "Extras" section in Android SDK Manager. SDK Manager

Play Services version

iOS overview

Example iOS app screencapture

If Location Services is turned OFF on an iOS device, no app on the device can access the location.

It is not programmatically possible to switch Location Services ON or to directly open the Location Services page in the Settings app.

The best that can be done by direct programmatic invocation of the Settings app is to open the app's own permissions page - the switchToSettings() of cordova-diagnostic-plugin enables you to do this. However, the user must still manually navigate from the app permissions page in the Settings app to the Location Services setting on the Privacy page.

If Location Services is turned OFF, this plugin enables an app to display a native iOS system dialog which gives user the option of directly opening the Privacy page in the Settings app which contains the switch to turn Location Services ON.

In order to show the native dialog allowing direct opening of the Privacy page, a location must be requested via the native location manager. So why can't you just use cordova-plugin-geolocation to request the location? Because when Location Services is OFF, the app reports that use of location is unauthorized, and cordova-plugin-geolocation will not request a location if it determines location is unauthorized: see this Cordova issue.

iOS "Cancel" button caveat

As highlighted by issue #16, there is one scenario in which the iOS implementation of this plugin fails: if, upon successfully showing the native dialog, the user presses "Cancel" instead of "Settings", any subsequent requests via this plugin will not show the dialog again. Ever! This is because iOS assumes that if the user pressed "Cancel", they don't want your app to use their location, so iOS prevents you from asking them again to switch on Location Services.

There's no way to tell which button the user pressed in the native dialog or whether "Cancel" was pressed and the dialog is not being shown. Consequently, if the user has pressed "Cancel" in the native dialog, any subsequent calls to the plugin will still result in the success callback being invoked, since (as far as the plugin is concerned), it successfully requested a location from the native location manager.

The best approach to workaround this is to recheck the state of Location Services using canRequest() on each resume event. If the user has pressed "Settings", your app will be put in the background while the Settings app is brought into the foreground, so when the user returns to your app, it will resume from the background.

Example project

An example project illustrating use of this plugin can be found here: https://github.com/dpa99c/cordova-plugin-request-location-accuracy-example

Installation

Using the Cordova/Phonegap CLI

# Install with default Play Services Location library version
$ cordova plugin add cordova-plugin-request-location-accuracy

# Install with custom Play Services Location library version
$ cordova plugin add cordova-plugin-request-location-accuracy --variable PLAY_SERVICES_LOCATION_VERSION=15.0.0

PhoneGap Build

Add the following xml to your config.xml to use the latest version of this plugin from npm:

<plugin name="cordova-plugin-request-location-accuracy" source="npm" />

Usage

The plugin is exposed via the cordova.plugins.locationAccuracy object.

Android & iOS

request()

This is the main plugin method.

Android

Requests a specific accuracy for Location Services.

cordova.plugins.locationAccuracy.request(successCallback, errorCallback, accuracy)

Parameters:

Example usage:

cordova.plugins.locationAccuracy.canRequest(function(canRequest){
    if(canRequest){
        cordova.plugins.locationAccuracy.request(function (success){
            console.log("Successfully requested accuracy: "+success.message);
        }, function (error){
           console.error("Accuracy request failed: error code="+error.code+"; error message="+error.message);
           if(error.code !== cordova.plugins.locationAccuracy.ERROR_USER_DISAGREED){
               if(window.confirm("Failed to automatically set Location Mode to 'High Accuracy'. Would you like to switch to the Location Settings page and do this manually?")){
                   cordova.plugins.diagnostic.switchToLocationSettings();
               }
           }
        }, cordova.plugins.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY);
    }else{
        // request location permission and try again
    }
});

iOS

If Location Services is OFF, invokes the native dialog to directly open the Location Services page in the Settings app.

cordova.plugins.locationAccuracy.request(successCallback, errorCallback)

Parameters:

Example usage:

cordova.plugins.locationAccuracy.canRequest(function(canRequest){
    if(canRequest){
        cordova.plugins.locationAccuracy.request(function(){
            console.log("Successfully made request to invoke native Location Services dialog");
        }, function(){
            console.error("Failed to invoke native Location Services dialog");
        });
    }else{
        // request location permission and try again
    }
});

isRequesting()

Indicates if a request is currently in progress.

cordova.plugins.locationAccuracy.isRequesting(successCallback);

Parameters:

Example usage:

 cordova.plugins.locationAccuracy.isRequesting(function(requesting){
    console.log("A request " + (requesting ? "is" : "is not") + " currently in progress");
 });

canRequest()

Indicates if a request is possible to invoke a request. On iOS, this will return true if Location Services is currently OFF and request is not currently in progress. On Android, this will return true if the app has authorization to use location.

cordova.plugins.locationAccuracy.canRequest(successCallback);

Parameters:

Example usage:

cordova.plugins.locationAccuracy.canRequest(function(canRequest){
    console.log("A request " + (canRequest ? "can" : "cannot") + " currently be made");
});

Android-only

Request constants

The location accuracy which is to be requested is defined as a set of REQUEST constants on the cordova.plugins.locationAccuracy object:

See https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest#constants

Callback constants

Both the successCallback() and errorCallback() functions will be passed an object which contains both a descriptive message and a code indicating the result of the operation. These constants are defined on the cordova.plugins.locationAccuracy object.

Success constants

The successCallback() function will be pass an object where the "code" key may correspond to the following values:

Error constants

The errorCallback() function will be pass an object where the "code" key may correspond to the following values:

Full Android & iOS example

The following example illustrates how to use the plugin cross-platform on both Android & iOS, and also how to use cordova-diagnostic-plugin to request runtime permission to use location if necessary.

var platform;
function onDeviceReady(){
    platform = cordova.platformId;
}

function onError(error) {
    console.error("The following error occurred: " + error);
}

function handleLocationAuthorizationStatus(status) {
    switch (status) {
        case cordova.plugins.diagnostic.permissionStatus.GRANTED:
            if(platform === "ios"){
                onError("Location services is already switched ON");
            }else{
                _makeRequest();
            }
            break;
        case cordova.plugins.diagnostic.permissionStatus.NOT_REQUESTED:
            requestLocationAuthorization();
            break;
        case cordova.plugins.diagnostic.permissionStatus.DENIED:
            if(platform === "android"){
                onError("User denied permission to use location");
            }else{
                _makeRequest();
            }
            break;
        case cordova.plugins.diagnostic.permissionStatus.DENIED_ALWAYS:
            // Android only
            onError("User denied permission to use location");
            break;
        case cordova.plugins.diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
            // iOS only
            onError("Location services is already switched ON");
            break;
    }
}

function requestLocationAuthorization() {
    cordova.plugins.diagnostic.requestLocationAuthorization(handleLocationAuthorizationStatus, onError);
}

function requestLocationAccuracy() {
    cordova.plugins.diagnostic.getLocationAuthorizationStatus(handleLocationAuthorizationStatus, onError);
}

function _makeRequest(){
    cordova.plugins.locationAccuracy.canRequest(function(canRequest){
        if (canRequest) {
            cordova.plugins.locationAccuracy.request(function () {
                    handleSuccess("Location accuracy request successful");
                }, function (error) {
                    onError("Error requesting location accuracy: " + JSON.stringify(error));
                    if (error) {
                        // Android only
                        onError("error code=" + error.code + "; error message=" + error.message);
                        if (platform === "android" && error.code !== cordova.plugins.locationAccuracy.ERROR_USER_DISAGREED) {
                            if (window.confirm("Failed to automatically set Location Mode to 'High Accuracy'. Would you like to switch to the Location Settings page and do this manually?")) {
                                cordova.plugins.diagnostic.switchToLocationSettings();
                            }
                        }
                    }
                }, cordova.plugins.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY // iOS will ignore this
            );
        } else {
            // On iOS, this will occur if Location Services is currently on OR a request is currently in progress.
            // On Android, this will occur if the app doesn't have authorization to use location.
            onError("Cannot request location accuracy");
        }
    });
}

// Make the request
requestLocationAccuracy();

License

The MIT License

Copyright (c) 2016 Dave Alden (Working Edge Ltd.)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.