Skip to content

Commit

Permalink
feat(android): use Fused Location Provider on Geolocation plugin (#2409)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcesarmobile authored Feb 5, 2020
1 parent 717dd0a commit 7faec79
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 122 deletions.
1 change: 1 addition & 0 deletions android/capacitor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ dependencies {
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:customtabs:28.0.0'
implementation 'com.google.firebase:firebase-messaging:18.0.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
Expand Down
168 changes: 58 additions & 110 deletions android/capacitor/src/main/java/com/getcapacitor/plugin/Geolocation.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
package com.getcapacitor.plugin;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.os.Bundle;

import com.getcapacitor.JSObject;
import com.getcapacitor.NativePlugin;
Expand All @@ -17,15 +12,17 @@
import com.getcapacitor.PluginMethod;
import com.getcapacitor.PluginRequestCodes;

import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Geolocation plugin that uses the native location service instead of the browser API.
*
* https://developer.android.com/guide/topics/location/strategies.html
*/

@NativePlugin(
permissions={
Manifest.permission.ACCESS_COARSE_LOCATION,
Expand All @@ -34,30 +31,11 @@
permissionRequestCode = PluginRequestCodes.GEOLOCATION_REQUEST_PERMISSIONS
)
public class Geolocation extends Plugin {
private LocationManager locationManager;
private LocationListener locationListener;

Map<String, PluginCall> watchingCalls = new HashMap<>();

public void load() {
locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);

locationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
processLocation(location);
}

@Override
public void onStatusChanged(String s, int i, Bundle bundle) {}
private Map<String, PluginCall> watchingCalls = new HashMap<>();
private FusedLocationProviderClient fusedLocationClient;
private LocationCallback locationCallback;

@Override
public void onProviderEnabled(String s) {}

@Override
public void onProviderDisabled(String s) {}
};
}

@PluginMethod()
public void getCurrentPosition(PluginCall call) {
Expand All @@ -70,13 +48,7 @@ public void getCurrentPosition(PluginCall call) {
}

private void sendLocation(PluginCall call) {
String provider = getBestProviderForCall(call);
Location lastLocation = getBestLocation(provider);
if (lastLocation == null) {
call.error("location unavailable");
} else {
call.success(getJSObjectForLocation(lastLocation));
}
requestLocationUpdates(call);
}

@PluginMethod(returnType=PluginMethod.RETURN_CALLBACK)
Expand All @@ -92,9 +64,7 @@ public void watchPosition(PluginCall call) {

@SuppressWarnings("MissingPermission")
private void startWatch(PluginCall call) {
String provider = getBestProviderForCall(call);
locationManager.requestLocationUpdates(provider, 0, 0, locationListener);

requestLocationUpdates(call);
watchingCalls.put(call.getCallbackId(), call);
}

Expand All @@ -108,11 +78,9 @@ public void clearWatch(PluginCall call) {
removed.release(bridge);
}
}

if (watchingCalls.size() == 0) {
locationManager.removeUpdates(locationListener);
clearLocationUpdates();
}

call.success();
}

Expand All @@ -126,47 +94,6 @@ private void processLocation(Location location) {
}
}

/**
* Given a call and its options, find the best provider that satisfies those
* required options.
* @param call
* @return
*/
private String getBestProviderForCall(PluginCall call) {
Criteria locationCriteria = getCriteriaForCall(call);
return locationManager.getBestProvider(locationCriteria, true);
}

/**
* Get the best location we can, using the best provider we have available
* that satisfies the requirements of the client
* @param bestProvider
* @return
*/
@SuppressWarnings("MissingPermission")
private Location getBestLocation(String bestProvider) {
List<String> providers = locationManager.getProviders(true);

Location l = locationManager.getLastKnownLocation(bestProvider);
Location bestLocation = l;

if (bestLocation != null) {
return bestLocation;
}

for (String provider : providers) {
l = locationManager.getLastKnownLocation(provider);
if (l == null) {
continue;
}
if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
bestLocation = l;
}
}
return bestLocation;
}


@Override
protected void handleRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.handleRequestPermissionsResult(requestCode, permissions, grantResults);
Expand All @@ -193,29 +120,6 @@ protected void handleRequestPermissionsResult(int requestCode, String[] permissi
}
}

/**
* Given the call's options, return a Criteria object
* that will indicate which location provider we need to use.
* @param call
* @return
*/
private Criteria getCriteriaForCall(PluginCall call) {
boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false);
boolean altitudeRequired = call.getBoolean("altitudeRequired", false);
boolean speedRequired = call.getBoolean("speedRequired", false);
boolean bearingRequired = call.getBoolean("bearingRequired", false);

int timeout = call.getInt("timeout", 30000);
int maximumAge = call.getInt("maximumAge", 0);

Criteria c = new Criteria();
c.setAccuracy(enableHighAccuracy ? Criteria.ACCURACY_FINE : Criteria.ACCURACY_COARSE);
c.setAltitudeRequired(altitudeRequired);
c.setBearingRequired(bearingRequired);
c.setSpeedRequired(speedRequired);
return c;
}

private JSObject getJSObjectForLocation(Location location) {
JSObject ret = new JSObject();
JSObject coords = new JSObject();
Expand All @@ -233,5 +137,49 @@ private JSObject getJSObjectForLocation(Location location) {
return ret;
}

@SuppressWarnings("MissingPermission")
private void requestLocationUpdates(final PluginCall call) {
clearLocationUpdates();
boolean enableHighAccuracy = call.getBoolean("enableHighAccuracy", false);
int timeout = call.getInt("timeout", 10000);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext());

LocationRequest locationRequest = new LocationRequest();
locationRequest.setMaxWaitTime(timeout);
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(enableHighAccuracy ? LocationRequest.PRIORITY_HIGH_ACCURACY : LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

locationCallback = new LocationCallback(){
@Override
public void onLocationResult(LocationResult locationResult) {
if (call.getMethodName().equals("getCurrentPosition")) {
clearLocationUpdates();
}
Location lastLocation = locationResult.getLastLocation();
if (lastLocation == null) {
call.error("location unavailable");
} else {
call.success(getJSObjectForLocation(lastLocation));
}
}
@Override
public void onLocationAvailability(LocationAvailability availability) {
if (!availability.isLocationAvailable()) {
call.error("location unavailable");
clearLocationUpdates();
}
}
};

fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);
}

private void clearLocationUpdates() {
if (locationCallback != null) {
fusedLocationClient.removeLocationUpdates(locationCallback);
locationCallback = null;
}
}

}
13 changes: 1 addition & 12 deletions core/src/core-plugin-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -839,19 +839,8 @@ export interface GeolocationPosition {

export interface GeolocationOptions {
enableHighAccuracy?: boolean; // default: false
timeout?: number; // default: 10000,
timeout?: number; // default: 10000
maximumAge?: number; // default: 0
/**
* Whether your app needs altitude data or not. This can impact the
* sensor the device uses, increasing energy consumption.
* Note: altitude information may not be available even when
* passing true here. Similarly, altitude data maybe be returned
* even if this value is false, in the case where doing so requires
* no increased energy consumption.
*
* Default: false
*/
requireAltitude?: boolean; // default: false
}

export type GeolocationWatchCallback = (position: GeolocationPosition, err?: any) => void;
Expand Down

0 comments on commit 7faec79

Please sign in to comment.