Uncategorized

Location Permissions and Accuracy: Ionic 4

1*yHs0mieBtDhPEBVhhuSOJw

Building an App that captures the Device Location??!! Alright.
Let’s start with some approaches I tried and failed while developing a Hybrid App using the Ionic Framework.

I am using Ionic 4. The App mainly captures an User’s Location at certain events. Accuracy of the location is really important for the App to work correctly.

I tried multiple, literally all, native plugins available. Some failed, some were ‘OK’ and finally I tried a Mixture of Plugins to achieve what I was intending to do. Let’s see each Approach.

Approach 1: Geolocation Plugin
Ionic Native Geolocation plugin claims to provide the Latitude and Longitude of the Current Device Position. It does. 
The getCurrentPosition function sometimes acts weirdly. Like delay in response, incorrect Lat-Lng, etc. 
The main issue I faced was that the plugin, most of the time, returns a cached location. 
I have researched a lot related to this issue. Many suggested to use maximumAge option with a value as low as possible. Didn’t help. Some suggested to use enableHighAccuracy, but somehow all these suggestions didn’t worked for me.

Still, let’s have a look at how to implement and use this Plugin.

Installation

$ ionic cordova plugin add cordova-plugin-geolocation
$ npm install @ionic-native/geolocation

Add following in your app.module page:

import { Geolocation } from '@ionic-native/geolocation/ngx';

And export the same under providers:

providers: [
Geolocation,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],

Create a service, say location.service. And add the following code:

import { Injectable } from '@angular/core';
import { Geolocation, GeolocationOptions, Geoposition, PositionError } from '@ionic-native/geolocation/ngx';
@Injectable()
export class LocationsProvider {
options: GeolocationOptions;
currentPos: Geoposition;
constructor(
private geolocation: Geolocation,
) {}
getUserPosition() {
return new Promise((resolve, reject) => {
this.options = {
maximumAge: 3000,
enableHighAccuracy: true
};

this.geolocation.getCurrentPosition(this.options).then((pos: Geoposition) => {
this.currentPos = pos;
const location = {
lat: pos.coords.latitude,
lng: pos.coords.longitude,
time: new Date(),
};
console.log('loc', location);
resolve(pos);
}, (err: PositionError) => {
console.log("error : " + err.message);
reject(err.message);
});
});
}
}

The getCurrentPosition function will return the current position in latitude and longitude.

Approach 2: Background Geolocation

This is really an awesome plugin, has a lot of benefits. You can directly send the device location directly to your Database by just providing with an API. But this will happen only when the device is online. Obviously the API call will require an active internet to make a connection to your server. This plugin works even the App is in background, when subscribed.

This will actually help you when you want to track a User’s movement.

Use of Background Geolocation to show the User’s Movement on a Google Map

Installation

ionic cordova plugin add @mauron85/cordova-plugin-background-geolocation
npm install @ionic-native/background-geolocation

Add following in your app.module page:

import { BackgroundGeolocation } from '@ionic-native/background-geolocation/ngx';

And export the same under providers:

providers: [
BackgroundGeolocation,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],

Create a service, say activity-tracker.service. And add the following code:

import { Injectable, NgZone } from '@angular/core';
import { Geolocation, Geoposition, GeolocationOptions } from '@ionic-native/geolocation/ngx';
import { BackgroundGeolocation, BackgroundGeolocationResponse, BackgroundGeolocationConfig } from '@ionic-native/background-geolocation/ngx';
@Injectable({
providedIn: 'root'
})
export class LocationTrackerProvider {
public watch: any;
public lat: number = 0;
public lng: number = 0;
options: GeolocationOptions;
currentPos: Geoposition;
time: any;
 constructor(
public zone: NgZone,
public backgroundGeolocation: BackgroundGeolocation,
private geolocation: Geolocation,
) {}
async startTracking() {
let config: BackgroundGeolocationConfig = {
desiredAccuracy: 10,
stationaryRadius: 0,
distanceFilter: 0,
notificationTitle: 'Background Tracking',
notificationText: 'Enabled',
debug: false,
interval: 5*60*1000,
stopOnTerminate: false,
url: 'http://medtrix.medimojo.co/api/v3/store-geo-location',
httpHeaders: {
'Content-Type': 'application/json'
},
postTemplate: {
lat: '@latitude',
lng: '@longitude',
time: '@time',
},
}
this.backgroundGeolocation.configure(config).then((location: BackgroundGeolocationResponse) => {
this.zone.run(() => {
this.lat = location.latitude;
this.lng = location.longitude;
this.time = location.time;
});
}, (err) => {
console.log(err);
});
// Turn ON the background-geolocation system.
this.backgroundGeolocation.start();
// Foreground Tracking
let options = {
frequency: 3000,
enableHighAccuracy: true
};
this.watch = this.geolocation.watchPosition(options)
.pipe(filter((p: any) => p.code === undefined))
.subscribe((position: Geoposition) => {
  // Run update inside of Angular's zone
this.zone.run(() => {
this.lat = position.coords.latitude;
this.lng = position.coords.longitude;
this.time = new Date();
    const location = {
src: 'bg trcker',
lat: position.coords.latitude,
lng: position.coords.longitude,
time: new Date(),
};
});
});
}
  stopTracking() {
// console.log('stopTracking');
this.backgroundGeolocation.finish();
this.watch.unsubscribe();
}
}

Final Approach:
The main reason behind these location related issue is, the modern custom Android OS tampers with the GPS service of the Device in order to save battery consumption and provide a higher battery backup.

Location sources includes Global Positioning System (GPS) and location inferred from network signals such as IP address, RFID, WiFi and Bluetooth MAC addresses, and GSM/CDMA cell IDs.
When the device enters power saving mode, it starts returning the previously cached location stored in the device. Or it might be when there is no Mobile Service, no WiFi. 
Also sometimes user Denies location accessing permissions to the App.

To deal with all these issues, I finally implemented a solution, which I will describe now.

Plugins required

Geolocation:
ionic cordova plugin add cordova-plugin-geolocation
npm install @ionic-native/geolocation
Location Accuracy
ionic cordova plugin add cordova-plugin-request-location-accuracy
npm install @ionic-native/location-accuracy
Android Permissions
ionic cordova plugin add cordova-plugin-android-permissions
npm install @ionic-native/android-permissions

Add following in your app.module page:

Geolocation:
import { BackgroundGeolocation } from '@ionic-native/background-geolocation/ngx';
Location Accuracy
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
Android Permissions
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';

And export the same under providers:

providers: [
Geolocation,
AndroidPermissions,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
],

Create a service, say plugin.service. And add the following code:

import { Injectable } from '@angular/core';
import { Diagnostic } from '@ionic-native/diagnostic/ngx';
import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
import { Geolocation, GeolocationOptions, Geoposition, PositionError } from '@ionic-native/geolocation/ngx';
import { LocationAccuracy } from '@ionic-native/location-accuracy/ngx';
import { reject } from 'q';
@Injectable()
export class PluginProvider {
options: GeolocationOptions;
currentPos: Geoposition;
subscription: any;
locationCoords: any;
apiResponse: any;
constructor(
private diagnostic: Diagnostic,
private androidPermissions: AndroidPermissions,
private geolocation: Geolocation,
private locationAccuracy: LocationAccuracy
) {
   this.locationCoords = {
latitude: "",
longitude: "",
accuracy: "",
timestamp: ""
}
}
//To check whether Location Service is enabled or Not
async locationStatus() {
return new Promise((resolve, reject) => {
this.diagnostic.isLocationEnabled().then((isEnabled) => {
console.log(isEnabled);
if (isEnabled === false) {
resolve(false);
} else if (isEnabled === true) {
resolve(true);
}
})
.catch((e) => {
// this.showToast('Please turn on Location');
reject(false);
});
});
}
async checkLocationEnabled() {
return new Promise((resolve, reject) => {
this.diagnostic.isLocationEnabled().then((isEnabled) => {
console.log(isEnabled);
if (isEnabled === false) {
this.showToast('Please turn on Location Service');
resolve(false);
} else if (isEnabled === true) {
this.checkGPSPermission().then((response) => {
console.log(response, 'checkGPSPermission-checkLocationEnabled');
this.apiResponse = response;
if(this.apiResponse === false) {
reject(false);
} else {
resolve(this.apiResponse);
}
})
.catch((e) => {
console.log(e, 'checkGPSPermission-checkLocationEnabled');
reject(false);
});
}
})
.catch((e) => {
this.showToast('Please turn on Location');
reject(false);
});
});
}
//Check if application having GPS access permission
async checkGPSPermission() {
return new Promise((resolve, reject) => {
this.androidPermissions.checkPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION).then(
result => {
console.log(result.hasPermission);
if (result.hasPermission) {
console.log('hasPermission-YES');
//If having permission show 'Turn On GPS' dialogue
this.askToTurnOnGPS().then((response) => {
console.log(response, 'askToTurnOnGPS-checkGPSPermission');
if (this.apiResponse === false) {
reject(this.apiResponse);
} else {
resolve(this.apiResponse);
}
});
} else {
console.log('hasPermission-NO');
//If not having permission ask for permission
this.requestGPSPermission().then((response) => {
console.log(response, 'requestGPSPermission-checkGPSPermission');
this.apiResponse = response;
if (this.apiResponse === false) {
reject(this.apiResponse);
} else {
resolve(this.apiResponse);
}
});
}
},
err => {
alert(err);
reject(false);
});
});
}
async requestGPSPermission() {
return new Promise((resolve, reject) => {
this.locationAccuracy.canRequest().then((canRequest: boolean) => {
if (canRequest) {
console.log("4");
} else {
//Show 'GPS Permission Request' dialogue
this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.ACCESS_COARSE_LOCATION).then(() => {
// call method to turn on GPS
this.askToTurnOnGPS().then((response) => {
console.log(response, 'askToTurnOnGPS-requestGPSPermission');
this.apiResponse = response;
if (this.apiResponse === false) {
reject(this.apiResponse);
} else {
resolve(this.apiResponse);
}
});
},
error => {
//Show alert if user click on 'No Thanks'
alert('requestPermission Error requesting location permissions ' + error);
reject(false);
});
}
});
});
}
async askToTurnOnGPS() {
return new Promise((resolve, reject) => {
this.locationAccuracy.request(this.locationAccuracy.REQUEST_PRIORITY_HIGH_ACCURACY).then((resp) => {
console.log(resp, 'location accuracy');
// When GPS Turned ON call method to get Accurate location coordinates
if(resp['code'] === 0) {
resolve(this.apiResponse);
this.getLocationCoordinates().then((cords) => {
console.log(cords, 'coords');
this.apiResponse = cords;
if(this.apiResponse === false) {
reject(false);
} else {
resolve(this.apiResponse);
}
});
}
error => {
alert('Error requesting location permissions');
reject(false);
}
});
});
}
async getLocationCoordinates() {
return new Promise((resolve, reject) => {
this.geolocation.getCurrentPosition().then((resp) => {
this.locationCoords.latitude = resp.coords.latitude;
this.locationCoords.longitude = resp.coords.longitude;
this.locationCoords.accuracy = resp.coords.accuracy;
this.locationCoords.timestamp = resp.timestamp;
console.log(resp, 'get locc');
resolve(this.locationCoords);
}).catch((error) => {
alert('Error getting location');
reject(false);
});
});
}
}

The above service handles all your cases, from location permissions to fetching an accurate location.

This fixed the location-related issue in my app.
I hope this will guide you if you are building an App that requires capturing the Location.

P.S.: This observation is wholly based on my experience. There might be some mistakes in my understanding as well.

Rajesh Mishra

I'm a developer who loves sharing insights, technical how-tos, and lessons learned from the world of code. While much of what I write may not be groundbreaking, I believe in documenting for future me—and for anyone else who might find it useful. Beyond tech, I also dive into life's experiences and moments, reflecting on personal growth and sharing stories that resonate. Whether you're here for practical tips or a fresh perspective on life, I hope you find something meaningful.

Leave a Reply

Your email address will not be published. Required fields are marked *