diff --git a/.gitignore b/.gitignore
index e344955..6c7bf4b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,3 +22,4 @@ gen
out
# Gradle builds
/build
+WhitelistPlugin.java
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8566741..fcfcb36 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# cordova-plugin-advanced-geolocation - Changelog
+## Version 1.0.0 - August 17, 2016
+
+Has breaking changes. This is a v1 implementation so any improvements and suggestions are welcome!
+
+**Enhancements**
+* Handles Android 6 permissions with native system prompts. Continues to handle previous Android versions exactly the same as before.
+* Improved incompatible version protection. If using this library on an unsupported platform it should protect against incompatibility errors where functionality is not available on a specific Android version. If you come across something that fails please open an issue.
+* Significantly improved error handling. Errors are now reported as JSON Objects that include an error number and message. Errors messages are now pervasively collected where possible.
+* Improved sample app and fixed various bugs.
+
+**Known Issues**
+* Does not provide a rationale message explaining why the library requires location information. There is a GPSPermsDeniedDialogFragment in the project and other stubs reserved for either custom implementation or as inclusion for future functionality.
+
+
## Version 0.5.1 - July 13, 2016
No breaking changes.
diff --git a/api_reference.md b/api_reference.md
index 22bdb36..14b7fe3 100644
--- a/api_reference.md
+++ b/api_reference.md
@@ -147,6 +147,7 @@ A full set of detailed information is available via the [`android.telephony`](ht
* There are minimum device SDK requirements. API level 18 is the current minimum to take advantage of this specific functionality. The plugin will turn off this functionality if the API level is less than 18.
* Activating cellular data may result in additional network charges for the user.
* This information is not gauranteed.
+* This information may still be provided by the device even if Location is turned off.
* The `TelephonyManager` API may not work correctly on all devices.
* To make use of this data you'll need access to a cell tower database. We don't provide cell tower location data, however there are databases and services available. One example provider is the [OpenCellId organization](http://wiki.opencellid.org/wiki/View_the_data).
* Take extra steps to protect the input data when using this API. Check for `null` or `Integer.MAX_VALUE`.
diff --git a/cordova_create.sh b/cordova_create.sh
new file mode 100644
index 0000000..7f52f2f
--- /dev/null
+++ b/cordova_create.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+
+set -e # Exit script on any error
+
+# "This is a shell script for manually creating a cordova repo"
+# "for the purpose of making changes or updating the github repo"
+
+read -p "Press [Enter] to create Cordova project…"
+
+cordova create cordova-geo com.esri.cordova.geolocation AdvancedGeolocation "$1" || exit 1
+
+read -p "Press [Enter] to continue…"
+
+echo "cd to new project directory"
+cd cordova-geo/
+
+echo "add android platform to project"
+cordova platform add android
+
+echo "cd to android platform directory"
+cd platforms/android/
+
+echo "clone github repo to temp dir"
+git clone https://github.com/andygup/cordova-plugin-advanced-geolocation.git temp
+
+echo "moving temp dir into android dir"
+mv temp/.gitignore .
+mv temp/.git .
+
+echo "creating www directory and copy contents over"
+mkdir -p assets/www/plugins/cordova-plugin-advanced-geolocation/www
+cp temp/www/AdvancedGeolocation.js assets/www/plugins/cordova-plugin-advanced-geolocation/www/
+cp temp/sample/map.js assets/www/js/
+cp temp/sample/blue-pin.png assets/www/img/
+cp temp/sample/green-pin.png assets/www/img/
+cp temp/sample/sample-map.html assets/www/
+cp -r src/ java/
+
+echo "deleting temp directory"
+rm -rf temp
+
+echo "reset git"
+git reset --hard HEAD # git will think you deleted all the important files
+git status # most likely there will be a MainActivity.java untracked
+
+echo "DONE! Please see shell script for additional manual tasks."
+
+<
+
+
+
+
+# Added plugin to config.xml
+
+
+
+
+
+# Add reference to cordova_plugins.js
+
+module.exports = [
+ {
+ "file": "plugins/cordova-plugin-advanced-geolocation/www/AdvancedGeolocation.js",
+ "id": "cordova-plugin-advanced-geolocation.AdvancedGeolocation",
+ "clobbers": [
+ "AdvancedGeolocation"
+ ]
+ }
+];
+
+Wrap AdvancedGeolocation.js in
+
+cordova.define("cordova-plugin-advanced-geolocation.AdvancedGeolocation", function(require, exports, module) {
+
+});
+
+AndroidManifest.xml
+
+
+
+# In config.xml switch out index.html to sample-map.html
+# Debug the app and see if everything works!
+
+
+COMMENT
diff --git a/cordova_create2.sh b/cordova_create2.sh
deleted file mode 100755
index 3c782c5..0000000
--- a/cordova_create2.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-
-
-mkdir -p assets/www/plugins/cordova-plugin-advanced-geolocation/www
-cp www/AdvancedGeolocation.js assets/www/plugins/cordova-plugin-advanced-geolocation/www/
-cp sample/map.js assets/www/js/
-cp sample/blue-pin.png assets/www/img/
-cp sample/green-pin.png assets/www/img/
-cp sample/sample-map.html assets/www/
-cp -r src/ java/
\ No newline at end of file
diff --git a/sample/map.js b/sample/map.js
index 3af293d..ef65cda 100644
--- a/sample/map.js
+++ b/sample/map.js
@@ -29,6 +29,7 @@ var app = {
var count = 0;
var satDiv = document.getElementById("satData");
+ var locationDiv = document.getElementById("locationData");
// Displays GPS-derived locations
var greenGPSSymbol = new PictureMarkerSymbol({
@@ -88,7 +89,12 @@ var app = {
var satellites = "Satellite Data: " + date.toUTCString() + " ";
for( var key in json){
- if(json.hasOwnProperty(key) && key.toLowerCase() != "provider" && key.toLowerCase() != "timestamp"){
+
+ if(json.hasOwnProperty(key)
+ && key.toLowerCase() != "provider"
+ && key.toLowerCase() != "timestamp"
+ && key.toLowerCase() != "error"){
+
satellites +=
"PRN: " + json[key].PRN +
", fix: " + json[key].usedInFix +
@@ -97,7 +103,7 @@ var app = {
}
}
- satData.innerHTML = satellites;
+ satDiv.innerHTML = satellites;
}
// Initialize the geolocation plugin
@@ -122,6 +128,8 @@ var app = {
switch(jsonObject.provider){
case "gps":
if(jsonObject.latitude != "0.0"){
+ console.log("GPS location detected - lat:" +
+ jsonObject.latitude + ", lon: " + jsonObject.longitude);
var point = new Point(jsonObject.longitude, jsonObject.latitude);
map.centerAt(point);
addGraphic( greenGPSSymbol, point);
@@ -130,6 +138,8 @@ var app = {
case "network":
if(jsonObject.latitude != "0.0"){
+ console.log("Network location detected - lat:" +
+ jsonObject.latitude + ", lon: " + jsonObject.longitude);
var point = new Point(jsonObject.longitude, jsonObject.latitude);
map.centerAt(point);
addGraphic( blueNetworkSymbol, point);
@@ -144,7 +154,6 @@ var app = {
case "cell_info":
console.log("cell_info JSON: " + data);
- satDiv.innerHTML = data;
break;
case "cell_location":
@@ -158,7 +167,9 @@ var app = {
}
},
function(error){
- console.log("ERROR! " + JSON.stringify(error));
+ console.log("Error JSON: " + JSON.stringify(error));
+ var e = JSON.parse(error);
+ console.log("Error no.: " + e.error + ", Message: " + e.msg + ", Provider: " + e.provider);
},
/////////////////////////////////////////
//
diff --git a/sample/sample-map.html b/sample/sample-map.html
index b22e837..33d275d 100644
--- a/sample/sample-map.html
+++ b/sample/sample-map.html
@@ -40,7 +40,7 @@
Hello World Map
-
+
-
+
- GPS
- NETWORK
+ GPS
+ NETWORK
+
diff --git a/src/com/esri/cordova/geolocation/AdvancedGeolocation.java b/src/com/esri/cordova/geolocation/AdvancedGeolocation.java
index 833bbbf..9215959 100644
--- a/src/com/esri/cordova/geolocation/AdvancedGeolocation.java
+++ b/src/com/esri/cordova/geolocation/AdvancedGeolocation.java
@@ -16,20 +16,28 @@
*/
package com.esri.cordova.geolocation;
-import com.esri.cordova.geolocation.controllers.CellLocationController;
-import com.esri.cordova.geolocation.fragments.GPSAlertDialogFragment;
-import com.esri.cordova.geolocation.controllers.GPSController;
-import com.esri.cordova.geolocation.controllers.NetworkLocationController;
-import com.esri.cordova.geolocation.fragments.NetworkUnavailableDialogFragment;
+import android.Manifest;
+import android.app.Activity;
import android.app.DialogFragment;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
+import android.preference.PreferenceManager;
import android.util.Log;
+import com.esri.cordova.geolocation.controllers.CellLocationController;
+import com.esri.cordova.geolocation.controllers.GPSController;
+import com.esri.cordova.geolocation.controllers.NetworkLocationController;
+import com.esri.cordova.geolocation.controllers.PermissionsController;
+import com.esri.cordova.geolocation.fragments.GPSAlertDialogFragment;
+import com.esri.cordova.geolocation.fragments.NetworkUnavailableDialogFragment;
+import com.esri.cordova.geolocation.utils.ErrorMessages;
+import com.esri.cordova.geolocation.utils.JSONHelper;
+
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
@@ -43,17 +51,19 @@
import java.util.concurrent.TimeUnit;
-public class AdvancedGeolocation extends CordovaPlugin {
+public class AdvancedGeolocation extends CordovaPlugin{
public static final String PROVIDERS_ALL = "all";
public static final String PROVIDERS_SOME = "some";
public static final String PROVIDERS_GPS = "gps";
public static final String PROVIDERS_NETWORK = "network";
public static final String PROVIDERS_CELL = "cell";
+ public static final String PROVIDER_PRIMARY = "application"; // references this main controller and not tied to a sensor
+
private static final String TAG = "GeolocationPlugin";
- private static final String SHARED_PREFS_NAME = "LocationSettings";
private static final String SHARED_PREFS_ACTION = "action";
private static final int MIN_API_LEVEL = 18;
+ private static final int REQUEST_LOCATION_PERMS_CODE = 10;
private static long _minDistance = 0;
private static long _minTime = 0;
@@ -67,14 +77,20 @@ public class AdvancedGeolocation extends CordovaPlugin {
private static GPSController _gpsController = null;
private static NetworkLocationController _networkLocationController = null;
private static CellLocationController _cellLocationController = null;
- private static LocationManager _locationManager;
private static CordovaInterface _cordova;
+ private static Activity _cordovaActivity;
private static CallbackContext _callbackContext;
+ private static SharedPreferences _sharedPreferences;
+ private static PermissionsController _permissionsController;
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
_cordova = cordova;
+ _cordovaActivity = cordova.getActivity();
+ _sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_cordovaActivity);
+ _permissionsController = new PermissionsController(_cordovaActivity, _cordova);
+ _permissionsController.handleOnInitialize();
removeActionPreferences();
Log.d(TAG, "Initialized");
}
@@ -84,7 +100,8 @@ public boolean execute(final String action, final JSONArray args, final Callback
Log.d(TAG, "Action = " + action);
- setSharedPreferences(action);
+ // Save this action so we can refer to it when the app restarts
+ setSharedPreferences(SHARED_PREFS_ACTION, action);
if (args != null) {
parseArgs(args);
@@ -94,36 +111,120 @@ public boolean execute(final String action, final JSONArray args, final Callback
}
private boolean runAction(final String action){
- _locationManager = (LocationManager) _cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
- final boolean networkLocationEnabled = _locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
- final boolean gpsEnabled = _locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
- final boolean networkEnabled = isInternetConnected(_cordova.getActivity().getApplicationContext());
-
- // If warnings are disabled then skip initializing alert dialog fragments
- if(!_noWarn && (!networkLocationEnabled || !gpsEnabled || !networkEnabled)){
- alertDialog(gpsEnabled, networkLocationEnabled, networkEnabled);
+
+ if(action.equals("start")){
+ validatePermissions();
+ return true;
+ }
+ if(action.equals("stop")){
+ stopLocation();
+ return true;
+ }
+ if(action.equals("kill")){
+ onDestroy();
return true;
}
+ else {
+ return false;
+ }
+ }
+
+ /**
+ * Cordova callback when querying for permissions
+ * Overrides in CordovaPlugin
+ * FYI: you can verify device permissions using:
+ * adb shell pm list permissions -d -g
+ * Reference: http://stackoverflow.com/questions/30719047/android-m-check-runtime-permission-how-to-determine-if-the-user-checked-nev
+ * @param requestCode The request code we assign - it's basically a token
+ * @param permissions The requested permissions - never null
+ * @param grantResults PERMISSION_GRANTED
or PERMISSION_DENIED
- never null
+ * @throws JSONException
+ */
+ public void onRequestPermissionResult(int requestCode, String[] permissions,
+ int[] grantResults) throws JSONException
+ {
+ // 1st Try - ALLOW or DENY. There is no don't ask again check box.
+ // If ALLOW the proceed and all is good
+ // If DENY then retry with NEVER ASK AGAIN prompt
+ // If ALLOW then proceed and all is good
+ // If DENY again with never ask again checked, then lock down the app and don't ask again
+ // If DENY again without checking never ask again, then recheck on next app launch
+ // have to remember to manually reactivate
+ //
+ // IMPORTANT! When this event completes the onResume event will fire!
+
+ // TEST CASES
+ // Start -> Allow -> minimize -> open app
+ // Start -> Allow -> minimize -> Change perms to deny geo -> open app
+ // Start -> Deny -> Deny -> minimize -> open app
+ // Start -> Deny -> Deny -> minimize -> Change perms to allow geo -> open app
+ // Start -> Deny -> Deny and check no ask -> minimize -> open app
+ // Start -> Deny -> Deny and check no ask -> minimize -> Change perms to allow geo -> open app
+ // Repeat test cases except shut off device geo permissions
+ //
+ // Reference for Permission Denied Workflow: https://material.google.com/patterns/permissions.html#permissions-denied-permissions
+
+ if(requestCode == REQUEST_LOCATION_PERMS_CODE){
+
+ // If permission was granted then go ahead
+ if(grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED){
+ Log.d(TAG,"GEO PERMISSIONS GRANTED.");
+ _permissionsController.handleOnRequestAllowed();
+ }
+ // If permission was denied then we can't run geolocation - permission DISABLED
+ else{
+ Log.w(TAG,"GEO PERMISSIONS DENIED.");
+ _permissionsController.handleOnRequestDenied();
+
+ // User doesn't want to see any more preference-related dialog boxes
+ if(_permissionsController.getShowRationale() == _permissionsController.DENIED_NOASK){
+ Log.w(TAG, "requestPermissions() Callback: " + ErrorMessages.LOCATION_SERVICES_DENIED_NOASK().message);
+ setSharedPreferences(_permissionsController.SHARED_PREFS_LOCATION_KEY, _permissionsController.SHARED_PREFS_GEO_DENIED_NOASK);
+ }
+ }
+ }
+ }
+
+ private void validatePermissions(){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ // Reference: Permission Groups https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
+ // As of July 2016 - ACCESS_WIFI_STATE and ACCESS_NETWORK_STATE are not considered dangerous permissions
+ Log.d(TAG, "validatePermissions()");
- if(networkLocationEnabled || gpsEnabled) {
- if(action.equals("start")){
+ final int showRationale = _permissionsController.getShowRationale();
+
+ if(_permissionsController.getAppPermissions()){
startLocation();
- return true;
}
- if(action.equals("stop")){
- stopLocation();
- return true;
+ // The user has said to never ask again about activating location services
+ else if(showRationale == _permissionsController.DENIED_NOASK){
+ Log.w(TAG, ErrorMessages.LOCATION_SERVICES_DENIED_NOASK().message);
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(PROVIDER_PRIMARY, ErrorMessages.LOCATION_SERVICES_DENIED_NOASK()));
+ }
+ else if(showRationale == _permissionsController.ALLOW) {
+ requestPermissions();
+ }
+ else if(showRationale == _permissionsController.DENIED) {
+ Log.w(TAG, "Rationale already shown, geolocation denied twice");
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(PROVIDER_PRIMARY, ErrorMessages.LOCATION_SERVICES_DENIED()));
}
- if(action.equals("kill")){
- onDestroy();
- return true;
+ }
+ else {
+ final LocationManager _locationManager = (LocationManager) _cordovaActivity.getSystemService(Context.LOCATION_SERVICE);
+ final boolean networkLocationEnabled = _locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
+ final boolean gpsEnabled = _locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
+ final boolean networkEnabled = isInternetConnected(_cordovaActivity.getApplicationContext());
+
+ // If warnings are disabled then skip initializing alert dialog fragments
+ if(!_noWarn && (!networkLocationEnabled || !gpsEnabled || !networkEnabled)){
+ alertDialog(gpsEnabled, networkLocationEnabled, networkEnabled);
}
else {
- return false;
+ startLocation();
}
}
-
- return false;
}
private void startLocation(){
@@ -131,58 +232,58 @@ private void startLocation(){
// Misc. note: If you see the message "Attempted to send a second callback for ID:" then you need
// to make sure to set pluginResult.setKeepCallback(true);
- final boolean networkEnabled = isInternetConnected(_cordova.getActivity().getApplicationContext());
+ final boolean networkEnabled = isInternetConnected(_cordovaActivity.getApplicationContext());
+ ExecutorService threadPool = cordova.getThreadPool();
if(_providers.equalsIgnoreCase(PROVIDERS_ALL)){
_gpsController = new GPSController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _returnSatelliteData, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_gpsController);
+ threadPool.execute(_gpsController);
_networkLocationController = new NetworkLocationController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_networkLocationController);
+ threadPool.execute(_networkLocationController);
// Reference: https://developer.android.com/reference/android/telephony/TelephonyManager.html#getAllCellInfo()
// Reference: https://developer.android.com/reference/android/telephony/CellIdentityWcdma.html (added at API 18)
if (Build.VERSION.SDK_INT < MIN_API_LEVEL){
- Log.e(TAG, "Cell Data option is not available on Android API versions < 18");
+ cellDataNotAllowed();
}
else {
_cellLocationController = new CellLocationController(networkEnabled,_cordova,_callbackContext);
- cordova.getThreadPool().execute(_cellLocationController);
+ threadPool.execute(_cellLocationController);
}
}
if(_providers.equalsIgnoreCase(PROVIDERS_SOME)){
_gpsController = new GPSController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _returnSatelliteData, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_gpsController);
+ threadPool.execute(_gpsController);
_networkLocationController = new NetworkLocationController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_networkLocationController);
+ threadPool.execute(_networkLocationController);
}
if(_providers.equalsIgnoreCase(PROVIDERS_GPS)){
_gpsController = new GPSController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _returnSatelliteData, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_gpsController);
+ threadPool.execute(_gpsController);
}
if(_providers.equalsIgnoreCase(PROVIDERS_NETWORK)){
_networkLocationController = new NetworkLocationController(
_cordova, _callbackContext, _minDistance, _minTime, _useCache, _buffer, _bufferSize);
- cordova.getThreadPool().execute(_networkLocationController);
+ threadPool.execute(_networkLocationController);
}
if(_providers.equalsIgnoreCase(PROVIDERS_CELL)){
// Reference: https://developer.android.com/reference/android/telephony/TelephonyManager.html#getAllCellInfo()
// Reference: https://developer.android.com/reference/android/telephony/CellIdentityWcdma.html
if (Build.VERSION.SDK_INT < MIN_API_LEVEL){
- Log.e(TAG, "Cell Data option is not available on Android API versions < 18");
- sendCallback(PluginResult.Status.ERROR, "Cell Data option is not available on Android API versions < 18");
+ cellDataNotAllowed();
}
else {
_cellLocationController = new CellLocationController(networkEnabled,_cordova,_callbackContext);
- cordova.getThreadPool().execute(_cellLocationController);
+ threadPool.execute(_cellLocationController);
}
}
}
@@ -192,24 +293,19 @@ private void startLocation(){
*/
private void stopLocation(){
- if(_locationManager != null){
- if(_gpsController != null){
- // Gracefully attempt to stop location
- _gpsController.stopLocation();
-
- // make sure there are no references
- _gpsController = null;
- }
- if(_networkLocationController != null){
- // Gracefully attempt to stop location
- _networkLocationController.stopLocation();
+ if(_gpsController != null){
+ // Gracefully attempt to stop location
+ _gpsController.stopLocation();
- // make sure there are no references
- _networkLocationController = null;
- }
+ // make sure there are no references
+ _gpsController = null;
+ }
+ if(_networkLocationController != null){
+ // Gracefully attempt to stop location
+ _networkLocationController.stopLocation();
// make sure there are no references
- _locationManager = null;
+ _networkLocationController = null;
}
// CellLocationController does not require LocationManager
@@ -224,40 +320,75 @@ private void stopLocation(){
Log.d(TAG, "Stopping geolocation");
}
+ //
+ //
+ // PREFERENCES
+ //
+ //
+
+ private String getSharedPreferences(String key){
+ return _sharedPreferences.getString(key,"");
+ }
+
+ /**
+ * Stores shared preferences so they can be retrieved after the app
+ * is minimized then resumed.
+ * @param value String
+ * @param key String
+ */
+ private void setSharedPreferences(String key, String value){
+ _sharedPreferences.edit().putString(key, value).apply();
+ Log.d(TAG, "prefs: " + key + ", " + _sharedPreferences.getString(key,""));
+ }
+
+ private void removeActionPreferences(){
+ _sharedPreferences.edit().remove(SHARED_PREFS_ACTION).apply();
+ }
+
+ private void requestPermissions(){
+ final String[] perms = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
+ _cordova.requestPermissions(this, REQUEST_LOCATION_PERMS_CODE, perms);
+ }
+
+ private void cellDataNotAllowed(){
+ Log.w(TAG, ErrorMessages.CELL_DATA_NOT_ALLOWED().message);
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(PROVIDER_PRIMARY, ErrorMessages.CELL_DATA_NOT_ALLOWED()));
+ }
+
+ //
+ //
+ // EVENTS
+ //
+ //
+
/**
- * This is a device event
+ * Retrieves shared preferences to find out what action was requested when the app
+ * originally launched. Resumes based on that last action.
* @param multitasking Unused in this API. Flag indicating if multitasking is turned on for app
* and is inherited from Cordova.
*/
public void onResume(boolean multitasking){
Log.d(TAG, "onResume");
- if(_locationManager != null){
- startLocation();
- }
- else {
- final SharedPreferences preferences = _cordova.getActivity().getSharedPreferences(SHARED_PREFS_NAME,0);
- final String action = preferences.getString(SHARED_PREFS_ACTION,"");
- if(!action.equals("")){
- runAction(action);
- }
+
+ final String action = getSharedPreferences(SHARED_PREFS_ACTION);
+ if(!action.equals("")) {
+ runAction(action);
}
}
public void onStart(){
Log.d(TAG, "onStart");
- if(_locationManager != null){
- startLocation();
- }
}
public void onPause(boolean multitasking){
- stopLocation();
Log.d(TAG, "onPause");
+ stopLocation();
}
public void onStop(){
- stopLocation();
Log.d(TAG, "onStop");
+ stopLocation();
}
public void onDestroy(){
@@ -266,10 +397,17 @@ public void onDestroy(){
stopLocation();
removeActionPreferences();
shutdownAndAwaitTermination(_cordova.getThreadPool());
- _cordova.getActivity().finish();
+ _cordovaActivity.finish();
}
}
+
+ //
+ //
+ // UTILITY METHODS
+ //
+ //
+
/**
* Shutdown cordova thread pool. This assumes we are in control of all tasks running
* in the thread pool.
@@ -309,27 +447,35 @@ private static void sendCallback(PluginResult.Status status, String message){
_callbackContext.sendPluginResult(result);
}
- private void alertDialog(boolean gpsEnabled, boolean networkLocationEnabled, boolean celllularEnabled){
+ /**
+ * For working with pre-Android M security permissions
+ * @param gpsEnabled If the cacheManifest and system allow gps
+ * @param networkLocationEnabled If the cacheManifest and system allow network location access
+ * @param cellularEnabled If the cacheManifest and system allow cellular data access
+ */
+ private void alertDialog(boolean gpsEnabled, boolean networkLocationEnabled, boolean cellularEnabled){
if(!gpsEnabled || !networkLocationEnabled){
- sendCallback(PluginResult.Status.ERROR, "Location services not enabled!");
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(PROVIDER_PRIMARY, ErrorMessages.LOCATION_SERVICES_UNAVAILABLE()));
final DialogFragment gpsFragment = new GPSAlertDialogFragment();
- gpsFragment.show(_cordova.getActivity().getFragmentManager(), "GPSAlert");
+ gpsFragment.show(_cordovaActivity.getFragmentManager(), "GPSAlert");
}
- if(!celllularEnabled){
- sendCallback(PluginResult.Status.ERROR, "Internet not available!");
+ if(!cellularEnabled){
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(PROVIDER_PRIMARY, ErrorMessages.CELL_DATA_NOT_AVAILABLE()));
final DialogFragment networkUnavailableFragment = new NetworkUnavailableDialogFragment();
- networkUnavailableFragment.show(_cordova.getActivity().getFragmentManager(), "NetworkUnavailableAlert");
+ networkUnavailableFragment.show(_cordovaActivity.getFragmentManager(), "NetworkUnavailableAlert");
}
}
/**
* Check for Network
connection.
* Checks for generic Exceptions and writes them to logcat as CheckConnectivity Exception
.
- * Make sure AndroidManifest.xml has appropriate permissions. API 23+ compliant.
+ * Make sure AndroidManifest.xml has appropriate permissions.
* @param con Application context
* @return Boolean
*/
@@ -346,27 +492,12 @@ private Boolean isInternetConnected(Context con){
}
}
catch(Exception e){
- Log.d(TAG,"CheckConnectivity Exception: " + e.getMessage());
+ Log.e(TAG,"CheckConnectivity Exception: " + e.getMessage());
}
return connected;
}
- private void removeActionPreferences(){
- _cordova.getActivity().getSharedPreferences(SHARED_PREFS_NAME,0).edit().remove(SHARED_PREFS_ACTION).commit();
- }
-
- private void setSharedPreferences(String action){
- SharedPreferences settings = _cordova.getActivity().getSharedPreferences(SHARED_PREFS_NAME, 0);
- SharedPreferences.Editor editor = settings.edit();
-
- // Let's us differentiate between application paused and application start new.
- editor.putString(SHARED_PREFS_ACTION, action);
-
- // Use apply() since it runs in the background rather than commit()
- editor.apply();
- }
-
private void parseArgs(JSONArray args){
Log.d(TAG,"Execute args: " + args.toString());
if(args.length() > 0){
@@ -383,7 +514,8 @@ private void parseArgs(JSONArray args){
}
catch (Exception exc){
- Log.d(TAG,"Execute has incorrect config arguments: " + exc.getMessage());
+ Log.d(TAG, ErrorMessages.INCORRECT_CONFIG_ARGS + ", " + exc.getMessage());
+ sendCallback(PluginResult.Status.ERROR, ErrorMessages.INCORRECT_CONFIG_ARGS + ", " + exc.getMessage());
}
}
}
diff --git a/src/com/esri/cordova/geolocation/controllers/CellLocationController.java b/src/com/esri/cordova/geolocation/controllers/CellLocationController.java
index adca919..926d899 100644
--- a/src/com/esri/cordova/geolocation/controllers/CellLocationController.java
+++ b/src/com/esri/cordova/geolocation/controllers/CellLocationController.java
@@ -17,8 +17,15 @@
package com.esri.cordova.geolocation.controllers;
/**
+ * Provides information derived from the cellular service of the device. Not all functionality
+ * is available on all devices and it also depends on the cellular providers capabilities.
+ *
* IMPORTANT: This Class is only compatible with API Level 17 or greater
* Reference: https://developer.android.com/reference/android/telephony/CellInfo.html
+ *
+ * IMPORTANT: This class will continue provide information even if the system Location
+ * settings is turned off as per security permission guidelines at Android 6.0 (API 23), reference:
+ * https://developer.android.com/guide/topics/security/permissions.html
*/
import android.content.Context;
@@ -36,6 +43,7 @@
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
+import com.esri.cordova.geolocation.utils.ErrorMessages;
import com.esri.cordova.geolocation.utils.JSONHelper;
import org.apache.cordova.CallbackContext;
@@ -48,7 +56,6 @@ public final class CellLocationController implements Runnable{
public static final String CELLINFO_PROVIDER = "cell";
private static final String TAG = "GeolocationPlugin";
- private static final int MIN_BUILD_VER = 21;
private static CallbackContext _callbackContext; // Threadsafe
private static TelephonyManager _telephonyManager = null;
private static PhoneStateListener _phoneStateListener = null;
@@ -80,6 +87,11 @@ public void run(){
Looper.loop();
}
}
+ else {
+ Log.e(TAG, ErrorMessages.CELL_DATA_MIN_VERSION().message);
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(CELLINFO_PROVIDER, ErrorMessages.CELL_DATA_MIN_VERSION()));
+ }
}
public void startLocation(){
@@ -89,7 +101,11 @@ public void startLocation(){
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
- Log.d(TAG, "Failing gracefully after detecting an uncaught exception on CellLocationController thread. " + throwable.getMessage());
+ Log.e(TAG, "Failing gracefully after detecting an uncaught exception on CellLocationController thread. "
+ + throwable.getMessage());
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(CELLINFO_PROVIDER, ErrorMessages.UNCAUGHT_THREAD_EXCEPTION()));
+ stopLocation();
}
});
@@ -104,6 +120,8 @@ public void uncaughtException(Thread thread, Throwable throwable) {
}
else {
Log.e(TAG, "Unable to start CellLocationController: no internet connection.");
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(CELLINFO_PROVIDER, ErrorMessages.CELL_DATA_NOT_AVAILABLE()));
}
}
}
@@ -128,10 +146,13 @@ public void stopLocation(){
* the rate at which onCellInfoChanged() is called.
*/
private void getAllCellInfos(){
- if(_telephonyManager != null) {
+ if(_telephonyManager != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final List cellInfos = _telephonyManager.getAllCellInfo();
processCellInfos(cellInfos);
}
+ else {
+ Log.w(TAG, "Unable to provide cell info due to version restriction");
+ }
}
private void setPhoneStateListener(){
@@ -155,7 +176,7 @@ public void onCellLocationChanged(CellLocation location){
}
private static void processCellInfos(List cellInfos){
- if(cellInfos != null){
+ if(cellInfos != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
for(CellInfo cellInfo : cellInfos){
@@ -191,7 +212,7 @@ private static void processCellInfos(List cellInfos){
// * could be a device that doesn't support this capability.
// * could be incorrect permissions: ACCESS_COARSE_LOCATION
sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(CELLINFO_PROVIDER, "Cell location data is returning as null"));
+ JSONHelper.errorJSON(CELLINFO_PROVIDER, ErrorMessages.CELL_DATA_IS_NULL()));
}
}
@@ -201,9 +222,8 @@ private static void processCellInfos(List cellInfos){
*/
private static boolean versionCheck(){
boolean verified = true;
- final int version = Build.VERSION.SDK_INT;
- if(version < MIN_BUILD_VER){
- Log.e(TAG, "WARNING: A minimum SDK v17 is required for CellLocation to work, and minimum SDK v21 is REQUIRED for this library.");
+
+ if(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP){
verified = false;
}
diff --git a/src/com/esri/cordova/geolocation/controllers/GPSController.java b/src/com/esri/cordova/geolocation/controllers/GPSController.java
index 8a06590..c0fac67 100644
--- a/src/com/esri/cordova/geolocation/controllers/GPSController.java
+++ b/src/com/esri/cordova/geolocation/controllers/GPSController.java
@@ -17,8 +17,8 @@
package com.esri.cordova.geolocation.controllers;
-import android.location.GpsStatus;
import android.content.Context;
+import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -30,6 +30,7 @@
import com.esri.cordova.geolocation.model.Coordinate;
import com.esri.cordova.geolocation.model.InitStatus;
import com.esri.cordova.geolocation.model.LocationDataBuffer;
+import com.esri.cordova.geolocation.utils.ErrorMessages;
import com.esri.cordova.geolocation.utils.JSONHelper;
import org.apache.cordova.CallbackContext;
@@ -54,7 +55,6 @@ public final class GPSController implements Runnable {
private static LocationDataBuffer _locationDataBuffer = null;
private static final String TAG = "GeolocationPlugin";
- public static final String GPS_PROVIDER = "gps";
public GPSController(
CordovaInterface cordova,
@@ -92,12 +92,17 @@ public void run(){
public void startLocation(){
if(!Thread.currentThread().isInterrupted()){
+ Log.i(TAG,"Available location providers: " + _locationManager.getAllProviders().toString());
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
- Log.d(TAG, "Failing gracefully after detecting an uncaught exception on GPSController thread. "
+ Log.e(TAG, "Failing gracefully after detecting an uncaught exception on GPSController thread. "
+ throwable.getMessage());
+
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, ErrorMessages.UNCAUGHT_THREAD_EXCEPTION()));
+ stopLocation();
}
});
@@ -105,21 +110,38 @@ public void uncaughtException(Thread thread, Throwable throwable) {
_locationDataBuffer = new LocationDataBuffer(_bufferSize);
}
- final InitStatus l2 = setLocationListenerGPSProvider();
- InitStatus l3 = new InitStatus();
+ final InitStatus gpsListener = setLocationListenerGPSProvider();
+ InitStatus satelliteListener = new InitStatus();
if(_returnSatelliteData){
- l3 = setGPSStatusListener();
+ satelliteListener = setGPSStatusListener();
}
- if(!l2.success || !l3.success){
- sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, l2.exception + ", " + l3.exception));
+ if(!gpsListener.success || !satelliteListener.success){
+ if(gpsListener.exception == null){
+ // Handle custom error messages
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, gpsListener.error));
+ }
+ else {
+ // Handle system exceptions
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, gpsListener.exception));
+ }
}
else {
// Return cache immediate if requested, otherwise wait for a location provider
if(_returnCache){
- final Location location = _locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+
+ Location location = null;
+
+ try {
+ location = _locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ }
+ catch(SecurityException exc){
+ Log.e(TAG, exc.getMessage());
+ }
+
final String parsedLocation;
// If the provider is disabled or currently unavailable then null is returned
@@ -143,16 +165,25 @@ public void uncaughtException(Thread thread, Throwable throwable) {
public void stopLocation(){
if(_locationManager != null){
- if(_locationListenerGPSProvider != null){
- _locationManager.removeUpdates(_locationListenerGPSProvider);
- _locationListenerGPSProvider = null;
- }
+ Log.d(TAG, "Attempting to stop gps geolocation");
if(_gpsStatusListener != null){
_locationManager.removeGpsStatusListener(_gpsStatusListener);
_gpsStatusListener = null;
}
+ if(_locationListenerGPSProvider != null){
+
+ try {
+ _locationManager.removeUpdates(_locationListenerGPSProvider);
+ }
+ catch(SecurityException exc){
+ Log.e(TAG, exc.getMessage());
+ }
+
+ _locationListenerGPSProvider = null;
+ }
+
_locationManager = null;
// Clear all elements from the buffer
@@ -162,8 +193,9 @@ public void stopLocation(){
Thread.currentThread().interrupt();
}
-
- Log.d(TAG, "Stopping gps geolocation");
+ else{
+ Log.d(TAG, "GPS location already stopped");
+ }
}
/**
@@ -195,7 +227,6 @@ public void onGpsStatusChanged(int event) {
final InitStatus status = new InitStatus();
- _locationManager = (LocationManager) _cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
final Boolean gpsProviderEnabled = _locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if(gpsProviderEnabled){
@@ -211,7 +242,7 @@ public void onGpsStatusChanged(int event) {
else {
//GPS not enabled
status.success = false;
- status.exception = "GPS provider not enabled";
+ status.error = ErrorMessages.GPS_UNAVAILABLE();
}
return status;
@@ -254,18 +285,19 @@ public void onLocationChanged(Location location) {
public void onStatusChanged(String provider, int status, Bundle extras) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
- Log.d(TAG, "Location Status Changed: GPS Out of Service");
+ // Reference: https://developer.android.com/reference/android/location/LocationProvider.html#OUT_OF_SERVICE
+ Log.d(TAG, "Location Status Changed: " + ErrorMessages.GPS_OUT_OF_SERVICE().message);
sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, "GPS out of service"));
+ JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, ErrorMessages.GPS_OUT_OF_SERVICE()));
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
- Log.d(TAG, "Location Status Changed: GPS Temporarily Unavailable");
+ Log.d(TAG, "Location Status Changed: " + ErrorMessages.GPS_UNAVAILABLE().message);
sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, "GPS temporarily unavailable"));
+ JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, ErrorMessages.GPS_UNAVAILABLE()));
break;
case LocationProvider.AVAILABLE:
- Log.d(TAG, "Status Changed: GPS Available");
+ Log.d(TAG, "Location Status Changed: GPS Available");
break;
}
}
@@ -290,16 +322,17 @@ public void onProviderDisabled(String provider) {
_locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, _minTime, _minDistance, _locationListenerGPSProvider);
}
- catch(Exception exc){
- Log.d(TAG, "Unable to start GPS provider. " + exc.getMessage());
+ catch(SecurityException exc){
+ Log.e(TAG, "Unable to start GPS provider. " + exc.getMessage());
status.success = false;
status.exception = exc.getMessage();
}
}
else {
+ Log.w(TAG, ErrorMessages.GPS_UNAVAILABLE().message);
//GPS not enabled
status.success = false;
- status.exception = "GPS provider not enabled";
+ status.error = ErrorMessages.GPS_UNAVAILABLE();
}
return status;
diff --git a/src/com/esri/cordova/geolocation/controllers/NetworkLocationController.java b/src/com/esri/cordova/geolocation/controllers/NetworkLocationController.java
index 6eb77dd..ceced05 100644
--- a/src/com/esri/cordova/geolocation/controllers/NetworkLocationController.java
+++ b/src/com/esri/cordova/geolocation/controllers/NetworkLocationController.java
@@ -28,6 +28,7 @@
import com.esri.cordova.geolocation.model.Coordinate;
import com.esri.cordova.geolocation.model.InitStatus;
import com.esri.cordova.geolocation.model.LocationDataBuffer;
+import com.esri.cordova.geolocation.utils.ErrorMessages;
import com.esri.cordova.geolocation.utils.JSONHelper;
import org.apache.cordova.CallbackContext;
@@ -50,7 +51,6 @@ public final class NetworkLocationController implements Runnable {
private static LocationDataBuffer _locationDataBuffer = null;
private static final String TAG = "GeolocationPlugin";
- public static final String SATELLITE_PROVIDER = "satellite";
public NetworkLocationController(
CordovaInterface cordova,
@@ -90,7 +90,12 @@ public void startLocation(){
Thread.currentThread().setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
- Log.d(TAG, "Failing gracefully after detecting an uncaught exception on NetworkLocationController thread.");
+ Log.e(TAG, "Failing gracefully after detecting an uncaught exception on NetworkLocationController thread."
+ + throwable.getMessage());
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, ErrorMessages.UNCAUGHT_THREAD_EXCEPTION()));
+
+ stopLocation();
}
});
@@ -98,22 +103,41 @@ public void uncaughtException(Thread thread, Throwable throwable) {
_locationDataBuffer = new LocationDataBuffer(_bufferSize);
}
- final InitStatus l2 = setLocationListenerNetworkProvider();
+ final InitStatus networkListener = setLocationListenerNetworkProvider();
- if(!l2.success){
- sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.GPS_PROVIDER, l2.exception));
+ if(!networkListener.success){
+// sendCallback(PluginResult.Status.ERROR,
+// JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, networkListener.exception));
+
+ if(networkListener.exception == null){
+ // Handle custom error messages
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, networkListener.error));
+ }
+ else if(networkListener.error == null){
+ // Handle system exceptions
+ sendCallback(PluginResult.Status.ERROR,
+ JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, networkListener.exception));
+ }
}
else {
// Return cache immediate if requested, otherwise wait for a location provider
if(_returnCache){
- final Location location = _locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
- final String parsedLocation;
+
+ Location location = null;
+
+ try {
+ location = _locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+ }
+ catch(SecurityException exc){
+ Log.e(TAG, exc.getMessage());
+ sendCallback(PluginResult.Status.ERROR, exc.getMessage());
+ }
// If the provider is disabled or currently unavailable then null may be returned on some devices
if(location != null) {
- parsedLocation = JSONHelper.locationJSON(LocationManager.NETWORK_PROVIDER, location, true);
+ final String parsedLocation = JSONHelper.locationJSON(LocationManager.NETWORK_PROVIDER, location, true);
sendCallback(PluginResult.Status.OK, parsedLocation);
}
}
@@ -128,7 +152,15 @@ public void stopLocation(){
if(_locationManager != null){
if(_locationListenerNetworkProvider != null){
- _locationManager.removeUpdates(_locationListenerNetworkProvider);
+
+ try {
+ _locationManager.removeUpdates(_locationListenerNetworkProvider);
+ }
+
+ catch(SecurityException exc){
+ Log.e(TAG, exc.getMessage());
+ }
+
_locationListenerNetworkProvider = null;
}
@@ -191,14 +223,14 @@ public void onLocationChanged(Location location) {
public void onStatusChanged(String provider, int status, Bundle extras) {
switch (status) {
case LocationProvider.OUT_OF_SERVICE:
- Log.d(TAG, "Location Status Changed: Network Provider Out of Service");
+ Log.d(TAG, "Location Status Changed: " + ErrorMessages.NETWORK_PROVIDER_OUT_OF_SERVICE().message);
sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, "Network provider out of service"));
+ JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, ErrorMessages.NETWORK_PROVIDER_OUT_OF_SERVICE()));
break;
case LocationProvider.TEMPORARILY_UNAVAILABLE:
- Log.d(TAG, "Location Status Changed: Network Provider Temporarily Unavailable");
+ Log.d(TAG, "Location Status Changed: " + ErrorMessages.NETWORK_PROVIDER_UNAVAILABLE().message);
sendCallback(PluginResult.Status.ERROR,
- JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, "Network provider temporarily unavailable"));
+ JSONHelper.errorJSON(LocationManager.NETWORK_PROVIDER, ErrorMessages.NETWORK_PROVIDER_UNAVAILABLE()));
break;
case LocationProvider.AVAILABLE:
Log.d(TAG, "Location Status Changed: Network Provider Available");
@@ -225,14 +257,16 @@ public void onProviderDisabled(String provider) {
_locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, _minTime, _minDistance, _locationListenerNetworkProvider);
- } catch (Exception exc) {
- Log.d(TAG, "Unable to start network provider. " + exc.getMessage());
+ } catch (SecurityException exc) {
+ Log.e(TAG, "Unable to start network provider. " + exc.getMessage());
status.success = false;
status.exception = exc.getMessage();
}
}
else {
+ Log.w(TAG, ErrorMessages.NETWORK_PROVIDER_UNAVAILABLE().message);
status.success = false;
+ status.error = ErrorMessages.NETWORK_PROVIDER_UNAVAILABLE();
}
return status;
diff --git a/src/com/esri/cordova/geolocation/controllers/PermissionsController.java b/src/com/esri/cordova/geolocation/controllers/PermissionsController.java
new file mode 100644
index 0000000..4f2ec23
--- /dev/null
+++ b/src/com/esri/cordova/geolocation/controllers/PermissionsController.java
@@ -0,0 +1,126 @@
+/**
+ * @author Andy Gup
+ *
+ * Copyright 2016 Esri
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.esri.cordova.geolocation.controllers;
+
+import android.Manifest;
+import android.app.Activity;
+import android.os.Build;
+import android.util.Log;
+
+import org.apache.cordova.CordovaInterface;
+
+public class PermissionsController {
+
+ private static Activity _activity;
+ private static CordovaInterface _cordovaInterface;
+ private static int _denyCounter = 0;
+ private static final String TAG = "GeolocationPlugin";
+
+ public final int ALLOW = 0;
+ public final int DENIED = -1;
+ public final int DENIED_NOASK = -2;
+ public final String SHARED_PREFS_LOCATION_KEY = "LocationSettings";
+ public final String SHARED_PREFS_GEO_DENIED_NOASK = "geoDeniedNoAsk"; // denied and don't ask again
+
+ public PermissionsController(
+ Activity activity,
+ CordovaInterface cordovaInterface){
+ _activity = activity;
+ _cordovaInterface = cordovaInterface;
+ }
+
+ /**
+ * Define our rules for when user is prompted for permissions access
+ * @return int
+ */
+ public int getShowRationale(){
+
+ int rationale = DENIED;
+
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
+ final boolean fineLocationRationale = _activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
+ final boolean coarseLocationRationale = _activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ Log.d(TAG,"Counter: " + _denyCounter + ", display rationale? " + fineLocationRationale);
+
+ // Permissions denied once
+ if(fineLocationRationale && coarseLocationRationale && _denyCounter <= 1){
+ Log.d(TAG,"rationale 1: user denied perms at least once");
+ rationale = ALLOW;
+ }
+ // Start up > permissions denied twice
+ else if(fineLocationRationale && coarseLocationRationale && _denyCounter > 1){
+ Log.d(TAG,"rationale 2: user has denied perms more than once");
+ rationale = DENIED;
+ }
+ // Don't ask me again check box has been set
+ else if((!fineLocationRationale || !coarseLocationRationale) && _denyCounter > 1){
+ Log.d(TAG,"rationale 3: user has denied perms and asked to be never asked again");
+ rationale = DENIED_NOASK;
+ }
+ // App start up
+ else if(!fineLocationRationale || !coarseLocationRationale){
+ Log.d(TAG, "rationale 4: application startup - no perms are set yet");
+ rationale = ALLOW;
+ }
+ }
+
+ return rationale;
+ }
+
+ /**
+ * We only have a finite number of permissions for this plugin: ACCESS_FINE_LOCATION and
+ * ACCESS_COARSE_LOCATION.
+ * @return boolean
+ */
+ public boolean getAppPermissions(){
+ return _cordovaInterface.hasPermission(Manifest.permission.ACCESS_FINE_LOCATION) ||
+ _cordovaInterface.hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
+ }
+
+ /**
+ * When app is initialized we need to start tracking permissions
+ */
+ public void handleOnInitialize(){
+
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
+ final boolean fineLocationRationale = _activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION);
+ final boolean coarseLocationRationale = _activity.shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ if(fineLocationRationale || coarseLocationRationale){
+ _denyCounter++;
+ }
+ else {
+ _denyCounter = 0;
+ }
+ }
+ }
+
+ /**
+ * Track when permissions requests are denied
+ */
+ public void handleOnRequestDenied(){
+ _denyCounter++;
+ }
+
+ /**
+ * Track when permissions requests are allowed
+ */
+ public void handleOnRequestAllowed(){
+ _denyCounter = 0;
+ }
+}
diff --git a/src/com/esri/cordova/geolocation/fragments/GPSAlertDialogFragment.java b/src/com/esri/cordova/geolocation/fragments/GPSAlertDialogFragment.java
index e4ff50a..4d5837c 100755
--- a/src/com/esri/cordova/geolocation/fragments/GPSAlertDialogFragment.java
+++ b/src/com/esri/cordova/geolocation/fragments/GPSAlertDialogFragment.java
@@ -30,7 +30,7 @@ public class GPSAlertDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState){
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setMessage("GPS is not currently enabled. Click ok to proceed to Location Settings.")
+ builder.setMessage("GPS is not currently enabled. Click ok to proceed to Location Settings then restart app.")
.setTitle("Toggle GPS");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
diff --git a/src/com/esri/cordova/geolocation/fragments/GPSPermsDeniedDialogFragment.java b/src/com/esri/cordova/geolocation/fragments/GPSPermsDeniedDialogFragment.java
new file mode 100644
index 0000000..d92c8f7
--- /dev/null
+++ b/src/com/esri/cordova/geolocation/fragments/GPSPermsDeniedDialogFragment.java
@@ -0,0 +1,72 @@
+/**
+ * @author Andy Gup
+ *
+ * Copyright 2016 Esri
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.esri.cordova.geolocation.fragments;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+
+public class GPSPermsDeniedDialogFragment extends DialogFragment{
+
+ private static final String TAG = "GeolocationPlugin";
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState){
+
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ final String message = "Without this permission the app will not retrieve location data. "
+ + "Are you sure you want to deny this permission?";
+ builder.setMessage(message)
+ .setTitle("Permission Denied");
+ builder.setPositiveButton("RE-TRY", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+
+ try{
+ Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
+ settingsIntent.setData(uri);
+ getActivity().startActivity(settingsIntent);
+ }
+ catch (Exception exc){
+ Log.e(TAG, exc.getMessage());
+ }
+ }
+ });
+ builder.setNegativeButton("I'M SURE", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ GPSPermsDeniedDialogFragment.this.getDialog().cancel();
+ }
+ });
+
+ final AlertDialog dialog = builder.create();
+
+ return dialog;
+ }
+
+
+ @Override
+ public void onDetach(){
+ super.onDetach();
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/esri/cordova/geolocation/fragments/NetworkUnavailableDialogFragment.java b/src/com/esri/cordova/geolocation/fragments/NetworkUnavailableDialogFragment.java
index 00108b3..fad9536 100644
--- a/src/com/esri/cordova/geolocation/fragments/NetworkUnavailableDialogFragment.java
+++ b/src/com/esri/cordova/geolocation/fragments/NetworkUnavailableDialogFragment.java
@@ -30,7 +30,7 @@ public class NetworkUnavailableDialogFragment extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState){
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder.setMessage("Internet is not available. Check if alternative network is available. Click ok to proceed to Settings.")
+ builder.setMessage("Internet is not available. Check if alternative network is available. Click ok to proceed to Settings then restart app.")
.setTitle("Toggle Wireless Settings");
builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
diff --git a/src/com/esri/cordova/geolocation/model/Error.java b/src/com/esri/cordova/geolocation/model/Error.java
new file mode 100644
index 0000000..7905cfb
--- /dev/null
+++ b/src/com/esri/cordova/geolocation/model/Error.java
@@ -0,0 +1,23 @@
+/**
+ * @author Andy Gup
+ *
+ * Copyright 2016 Esri
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.esri.cordova.geolocation.model;
+
+
+public class Error {
+ public String number;
+ public String message;
+}
diff --git a/src/com/esri/cordova/geolocation/model/InitStatus.java b/src/com/esri/cordova/geolocation/model/InitStatus.java
index 45795b5..e54f211 100644
--- a/src/com/esri/cordova/geolocation/model/InitStatus.java
+++ b/src/com/esri/cordova/geolocation/model/InitStatus.java
@@ -20,6 +20,7 @@
public class InitStatus {
public boolean success = true;
public String exception = null;
+ public Error error = null;
public InitStatus(){
}
diff --git a/src/com/esri/cordova/geolocation/utils/ErrorMessages.java b/src/com/esri/cordova/geolocation/utils/ErrorMessages.java
new file mode 100644
index 0000000..eacefa6
--- /dev/null
+++ b/src/com/esri/cordova/geolocation/utils/ErrorMessages.java
@@ -0,0 +1,126 @@
+/**
+ * @author Andy Gup
+ *
+ * Copyright 2016 Esri
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.esri.cordova.geolocation.utils;
+
+import com.esri.cordova.geolocation.model.Error;
+
+/**
+ * This is a central repository for managing error messages and reducing duplication.
+ */
+public class ErrorMessages {
+
+ // Configuration errors are 900 series
+ public static final String INCORRECT_CONFIG_ARGS = "{\"error\": \"901\", \"msg\": \"There was a problem with the optional configuration arguments\"}";
+
+ public static Error CELL_DATA_NOT_AVAILABLE(){
+ final Error err = new Error();
+ err.number = "102";
+ err.message = "Cell data requested but unavailable. Check internet connection";
+
+ return err;
+ }
+
+ public static Error CELL_DATA_NOT_ALLOWED(){
+ final Error err = new Error();
+ err.number = "103";
+ err.message = "Cell Data option is not available on Android API versions < 18";
+
+ return err;
+ }
+
+ public static Error CELL_DATA_IS_NULL(){
+ final Error err = new Error();
+ err.number = "104";
+ err.message = "Cell data is returning null. This option may not be supported on the device";
+
+ return err;
+ }
+
+ public static Error CELL_DATA_MIN_VERSION(){
+ final Error err = new Error();
+ err.number = "105";
+ err.message = "WARNING: A minimum SDK v17 is required for CellLocation to work, and minimum SDK v21 is REQUIRED for this library";
+
+ return err;
+ }
+
+ public static Error LOCATION_SERVICES_UNAVAILABLE(){
+ final Error err = new Error();
+ err.number = "110";
+ err.message = "Neither GPS nor network location is available";
+
+ return err;
+ }
+
+ public static Error LOCATION_SERVICES_DENIED_NOASK(){
+ final Error err = new Error();
+ err.number = "111";
+ err.message = "Location services were denied by user with the flag to never ask again";
+
+ return err;
+ }
+
+ public static Error LOCATION_SERVICES_DENIED(){
+ final Error err = new Error();
+ err.number = "112";
+ err.message = "Location services were denied by user";
+
+ return err;
+ }
+
+ public static Error GPS_UNAVAILABLE(){
+ final Error err = new Error();
+ err.number = "120";
+ err.message = "GPS location requested but GPS is not available. Check system Location settings";
+
+ return err;
+ }
+
+ public static Error GPS_OUT_OF_SERVICE(){
+ final Error err = new Error();
+ err.number = "121";
+ err.message = "GPS is out of service";
+
+ return err;
+ }
+
+ public static Error UNCAUGHT_THREAD_EXCEPTION(){
+ final Error err = new Error();
+ err.number = "122";
+ err.message = "Uncaught thread exception. See logcat for full exception dump";
+
+ return err;
+ }
+
+ public static final String JSON_EXCEPTION = "{\"error\": \"130\", \"msg\":\"Problem in JSONHelper while processing JSON. \"}";
+
+ public static Error NETWORK_PROVIDER_UNAVAILABLE(){
+ final Error err = new Error();
+ err.number = "140";
+ err.message = "Network location requested but the provider is not available. Check system Location settings";
+
+ return err;
+ }
+
+ public static Error NETWORK_PROVIDER_OUT_OF_SERVICE(){
+ final Error err = new Error();
+ err.number = "141";
+ err.message = "Network location requested but it's out of service. Check your device";
+
+ return err;
+ }
+}
\ No newline at end of file
diff --git a/src/com/esri/cordova/geolocation/utils/JSONHelper.java b/src/com/esri/cordova/geolocation/utils/JSONHelper.java
index bc28d75..2a19733 100644
--- a/src/com/esri/cordova/geolocation/utils/JSONHelper.java
+++ b/src/com/esri/cordova/geolocation/utils/JSONHelper.java
@@ -19,6 +19,7 @@
import android.location.GpsSatellite;
import android.location.GpsStatus;
import android.location.Location;
+import android.os.Build;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
@@ -31,6 +32,8 @@
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
+import com.esri.cordova.geolocation.model.Error;
+
import org.json.JSONException;
import org.json.JSONObject;
@@ -75,7 +78,7 @@ public static String locationJSON(String provider, Location location, boolean ca
json.put("cached", cached);
}
catch (JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
@@ -110,13 +113,13 @@ public static String locationJSON(
try {
json.put("provider", provider);
+ json.put("timestamp", location.getTime());
json.put("latitude", location.getLatitude());
json.put("longitude", location.getLongitude());
json.put("altitude", location.getAltitude());
json.put("accuracy", location.getAccuracy());
json.put("bearing", location.getBearing());
json.put("speed", location.getSpeed());
- json.put("timestamp", location.getTime());
json.put("cached", cached);
json.put("buffer", buffer);
json.put("bufferSize", bufferSize);
@@ -125,7 +128,7 @@ public static String locationJSON(
json.put("bufferedAccuracy", bufferedAccuracy);
}
catch (JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
@@ -134,7 +137,7 @@ public static String locationJSON(
/**
* Converts CellInfoCdma into JSON
- * @param cellInfo
+ * @param cellInfo CellInfoCdma
* @return JSON
*/
public static String cellInfoCDMAJSON(CellInfoCdma cellInfo){
@@ -142,13 +145,13 @@ public static String cellInfoCDMAJSON(CellInfoCdma cellInfo){
final Calendar calendar = Calendar.getInstance();
final JSONObject json = new JSONObject();
- if(cellInfo != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && cellInfo != null) {
try {
json.put("provider", CELLINFO_PROVIDER);
json.put("type", CDMA);
json.put("timestamp", calendar.getTimeInMillis());
- CellIdentityCdma identityCdma = cellInfo.getCellIdentity();
+ final CellIdentityCdma identityCdma = cellInfo.getCellIdentity();
json.put("latitude", CdmaCellLocation.convertQuartSecToDecDegrees(identityCdma.getLatitude()));
json.put("longitude", CdmaCellLocation.convertQuartSecToDecDegrees(identityCdma.getLongitude()));
@@ -157,9 +160,10 @@ public static String cellInfoCDMAJSON(CellInfoCdma cellInfo){
json.put("systemId", identityCdma.getSystemId());
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
+
return json.toString();
}
@@ -168,7 +172,7 @@ public static String cellInfoCDMAJSON(CellInfoCdma cellInfo){
* Some devices may not work correctly:
* - Reference 1: https://code.google.com/p/android/issues/detail?id=191492
* - Reference 2: http://stackoverflow.com/questions/17815062/cellidentitygsm-on-android
- * @param cellInfo
+ * @param cellInfo CellInfoWcdma
* @return JSON
*/
public static String cellInfoWCDMAJSON(CellInfoWcdma cellInfo){
@@ -176,13 +180,13 @@ public static String cellInfoWCDMAJSON(CellInfoWcdma cellInfo){
final Calendar calendar = Calendar.getInstance();
final JSONObject json = new JSONObject();
- if(cellInfo != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && cellInfo != null) {
try {
json.put("provider", CELLINFO_PROVIDER);
json.put("type", WCDMA);
json.put("timestamp", calendar.getTimeInMillis());
- CellIdentityWcdma identityWcdma = cellInfo.getCellIdentity();
+ final CellIdentityWcdma identityWcdma = cellInfo.getCellIdentity();
json.put("cid", identityWcdma.getCid());
json.put("lac", identityWcdma.getLac());
@@ -191,7 +195,7 @@ public static String cellInfoWCDMAJSON(CellInfoWcdma cellInfo){
json.put("psc", identityWcdma.getPsc());
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
return json.toString();
@@ -199,7 +203,7 @@ public static String cellInfoWCDMAJSON(CellInfoWcdma cellInfo){
/**
* Converts CellInfoGsm into JSON
- * @param cellInfo
+ * @param cellInfo CellInfoGsm
* @return JSON
*/
public static String cellInfoGSMJSON(CellInfoGsm cellInfo){
@@ -207,13 +211,13 @@ public static String cellInfoGSMJSON(CellInfoGsm cellInfo){
final Calendar calendar = Calendar.getInstance();
final JSONObject json = new JSONObject();
- if(cellInfo != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && cellInfo != null) {
try {
json.put("provider", CELLINFO_PROVIDER);
json.put("type", GSM);
json.put("timestamp", calendar.getTimeInMillis());
- CellIdentityGsm identityGsm = cellInfo.getCellIdentity();
+ final CellIdentityGsm identityGsm = cellInfo.getCellIdentity();
json.put("cid", identityGsm.getCid());
json.put("lac", identityGsm.getLac());
@@ -221,7 +225,7 @@ public static String cellInfoGSMJSON(CellInfoGsm cellInfo){
json.put("mnc", identityGsm.getMnc());
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
return json.toString();
@@ -229,7 +233,7 @@ public static String cellInfoGSMJSON(CellInfoGsm cellInfo){
/**
* Converts CellInfoLte into JSON
- * @param cellInfo
+ * @param cellInfo CellInfoLte
* @return JSON
*/
public static String cellInfoLTEJSON(CellInfoLte cellInfo){
@@ -237,7 +241,7 @@ public static String cellInfoLTEJSON(CellInfoLte cellInfo){
final Calendar calendar = Calendar.getInstance();
final JSONObject json = new JSONObject();
- if(cellInfo != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && cellInfo != null) {
try {
json.put("provider", CELLINFO_PROVIDER);
json.put("type", LTE);
@@ -252,7 +256,7 @@ public static String cellInfoLTEJSON(CellInfoLte cellInfo){
json.put("tac", identityLte.getTac());
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
return json.toString();
@@ -261,7 +265,7 @@ public static String cellInfoLTEJSON(CellInfoLte cellInfo){
/**
* Parses data from PhoneStateListener.LISTEN_CELL_LOCATION.onCellLocationChanged
* http://developer.android.com/reference/android/telephony/cdma/CdmaCellLocation.html
- * @param location
+ * @param location CdmaCellLocation
* @return JSON
*/
public static String cdmaCellLocationJSON(CdmaCellLocation location){
@@ -269,7 +273,7 @@ public static String cdmaCellLocationJSON(CdmaCellLocation location){
final Calendar calendar = Calendar.getInstance();
final JSONObject json = new JSONObject();
- if(location != null){
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && location != null) {
try {
json.put("provider", CELLLOCATION_PROVIDER);
json.put("type", CDMA);
@@ -281,7 +285,7 @@ public static String cdmaCellLocationJSON(CdmaCellLocation location){
json.put("baseStationLongitude", CdmaCellLocation.convertQuartSecToDecDegrees(location.getBaseStationLongitude()));
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
@@ -291,7 +295,7 @@ public static String cdmaCellLocationJSON(CdmaCellLocation location){
/**
* Parses data from PhoneStateListener.LISTEN_CELL_LOCATION.onCellLocationChanged
* http://developer.android.com/reference/android/telephony/cdma/CdmaCellLocation.html
- * @param location
+ * @param location GsmCellLocation
* @return JSON
*/
public static String gsmCellLocationJSON(GsmCellLocation location){
@@ -309,7 +313,7 @@ public static String gsmCellLocationJSON(GsmCellLocation location){
json.put("psc", location.getPsc());
}
catch(JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
}
@@ -353,7 +357,7 @@ public static String satelliteDataJSON(GpsStatus gpsStatus){
}
}
catch (JSONException exc){
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
return json.toString();
@@ -374,9 +378,35 @@ public static String errorJSON(String provider, String error) {
json.put("error", error);
}
catch (JSONException exc) {
- Log.d(TAG, exc.getMessage());
+ logJSONException(exc);
}
return json.toString();
}
+
+ /**
+ * Helper method for reporting errors coming off a location provider
+ * @param provider Indicates if this error is coming from gps or network provider
+ * @param error The actual error being thrown by the provider
+ * @return Error string
+ */
+ public static String errorJSON(String provider, Error error) {
+
+ final JSONObject json = new JSONObject();
+
+ try {
+ json.put("provider", provider);
+ json.put("error", error.number);
+ json.put("msg", error.message);
+ }
+ catch (JSONException exc) {
+ logJSONException(exc);
+ }
+
+ return json.toString();
+ }
+
+ private static void logJSONException(JSONException exc){
+ Log.d(TAG, ErrorMessages.JSON_EXCEPTION + ", " + exc.getMessage());
+ }
}