Skip to content

Latest commit

 

History

History
379 lines (272 loc) · 22 KB

File metadata and controls

379 lines (272 loc) · 22 KB

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:

  • you may not wish to use the location features of the diagnostic plugin and therefore may not require it
  • you may not wish to use the features of this plugin
    • on Android, the dependency on the Google Play Services library increases the size of the app APK by about 2Mb

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.

Pre-requisites

IMPORTANT: 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. Otherwise the build will fail.

SDK Manager

Android build issues

  • This plugin depends on the Google Play Services library, which is referenced via Gradle during the Android build process.
  • The library version specified by the plugin attempts to keep up with the most recent major version release by Google.
    • See here for the version currently specified by this plugin.
    • See here for the list of recent versions from Google.
  • You may encounter build errors if other plugins in your Cordova project specify a different version of the Play Services library.
  • You can use cordova-android-play-services-gradle-release to override the Play Services library version specified by this plugin (and other plugins) in your Cordova project in order to align them and prevent build errors.
  • If your project includes a plugin which uses the Firebase library (such as phonegap-plugin-push, cordova-plugin-fcm, cordova-plugin-firebase) you may find your build still fails.
  • This is because the major versions of the Play Services and Firebase libraries need to align.
  • You can use cordova-android-firebase-gradle-release to override the Firebase library version to align with the Play Services library version specified via cordova-android-play-services-gradle-release in order to resolve this.
  • See #50 for an example.

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

$ cordova plugin add cordova-plugin-request-location-accuracy
$ phonegap plugin add cordova-plugin-request-location-accuracy

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:

  • {Function} successCallback - callback to be invoked on successful resolution of the requested accuracy. A single object argument will be passed which has two keys: "code" in an integer corresponding to a SUCCESS constant and indicates the reason for success; "message" is a string containing a description of the success.
  • {Function} errorCallback - callback to be invoked on failure to resolve the requested accuracy. A single object argument will be passed which has two keys: "code" in an integer corresponding to an ERROR constant and indicates the reason for failure; "message" is a string containing a description of the error.
  • {Integer} accuracy - The location accuracy to request defined by an integer corresponding to a REQUEST constant.

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:

  • {Function} successCallback - callback to be invoked on successful request to invoke the dialog.
  • {Function} errorCallback - callback to be invoked on failure to request a location and invoked the dialog.

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:

  • {Function} successCallback - callback to pass result to. This is passed a boolean argument indicating if a request is currently in progress.

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:

  • {Function} successCallback - callback to pass result to. This is passed a boolean argument indicating if a request can be made.

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:

  • cordova.plugins.locationAccuracy.REQUEST_PRIORITY_NO_POWER: Request location mode priority "no power": the best accuracy possible with zero additional power consumption.
  • cordova.plugins.locationAccuracy.REQUEST_PRIORITY_LOW_POWER: Request location mode priority "low power": "city" level accuracy (about 10km accuracy).
  • cordova.plugins.locationAccuracy.REQUEST_PRIORITY_BALANCED_POWER_ACCURACY: Request location mode priority "balanced power": "block" level accuracy (about 100 meter accuracy).
  • cordova.plugins.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY: Request location mode priority "high accuracy": the most accurate locations available. This will use GPS hardware to retrieve positions.

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:

  • cordova.plugins.locationAccuracy.SUCCESS_SETTINGS_SATISFIED: Success due to current location settings already satisfying requested accuracy.
  • cordova.plugins.locationAccuracy.SUCCESS_USER_AGREED: Success due to user agreeing to requested accuracy change

Error constants

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

  • cordova.plugins.locationAccuracy.ERROR_ALREADY_REQUESTING: Error due an unresolved request already being in progress.
  • cordova.plugins.locationAccuracy.ERROR_INVALID_ACTION: Error due invalid action requested.
  • cordova.plugins.locationAccuracy.ERROR_INVALID_ACCURACY: Error due invalid accuracy requested.
  • cordova.plugins.locationAccuracy.ERROR_EXCEPTION: Error due to exception in the native code.
  • cordova.plugins.locationAccuracy.ERROR_CANNOT_CHANGE_ACCURACY: Error due to not being able to change location accuracy to requested state.
  • cordova.plugins.locationAccuracy.ERROR_USER_DISAGREED: Error due to user rejecting requested accuracy change.
  • cordova.plugins.locationAccuracy.ERROR_GOOGLE_API_CONNECTION_FAILED: Error due to failure to connect to Google Play Services API. The "message" key will contain a detailed description of the Google Play Services error.

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.