From 7faec79440adbb98bf1d5f708086ccaed5ad4186 Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Wed, 5 Feb 2020 18:41:53 +0100 Subject: [PATCH] feat(android): use Fused Location Provider on Geolocation plugin (#2409) --- android/capacitor/build.gradle | 1 + .../com/getcapacitor/plugin/Geolocation.java | 168 ++++++------------ core/src/core-plugin-definitions.ts | 13 +- 3 files changed, 60 insertions(+), 122 deletions(-) diff --git a/android/capacitor/build.gradle b/android/capacitor/build.gradle index 360549b795..b9f18a56f6 100644 --- a/android/capacitor/build.gradle +++ b/android/capacitor/build.gradle @@ -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' diff --git a/android/capacitor/src/main/java/com/getcapacitor/plugin/Geolocation.java b/android/capacitor/src/main/java/com/getcapacitor/plugin/Geolocation.java index 861b174d66..aa0638da17 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/plugin/Geolocation.java +++ b/android/capacitor/src/main/java/com/getcapacitor/plugin/Geolocation.java @@ -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; @@ -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, @@ -34,30 +31,11 @@ permissionRequestCode = PluginRequestCodes.GEOLOCATION_REQUEST_PERMISSIONS ) public class Geolocation extends Plugin { - private LocationManager locationManager; - private LocationListener locationListener; - - Map 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 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) { @@ -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) @@ -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); } @@ -108,11 +78,9 @@ public void clearWatch(PluginCall call) { removed.release(bridge); } } - if (watchingCalls.size() == 0) { - locationManager.removeUpdates(locationListener); + clearLocationUpdates(); } - call.success(); } @@ -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 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); @@ -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(); @@ -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; + } + } } diff --git a/core/src/core-plugin-definitions.ts b/core/src/core-plugin-definitions.ts index f2e13d6d65..409627b674 100644 --- a/core/src/core-plugin-definitions.ts +++ b/core/src/core-plugin-definitions.ts @@ -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;