From bab483bcc06cbf238c040edb4b406e2ecc7e607f Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Wed, 11 Jan 2017 16:44:51 -0500 Subject: [PATCH 01/16] remove v4 activity, again --- .../directions/DirectionsV4Activity.java | 184 ------------------ .../res/layout/activity_directions_v4.xml | 15 -- mapbox/app/src/main/res/values/strings.xml | 2 - 3 files changed, 201 deletions(-) delete mode 100644 mapbox/app/src/main/java/com/mapbox/services/android/testapp/directions/DirectionsV4Activity.java delete mode 100644 mapbox/app/src/main/res/layout/activity_directions_v4.xml diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/directions/DirectionsV4Activity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/directions/DirectionsV4Activity.java deleted file mode 100644 index 544158503..000000000 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/directions/DirectionsV4Activity.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.mapbox.services.android.testapp.directions; - -import android.graphics.Color; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.util.Log; -import android.widget.Toast; - -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.Style; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; -import com.mapbox.services.Constants; -import com.mapbox.services.android.testapp.R; -import com.mapbox.services.android.testapp.Utils; -import com.mapbox.services.api.ServicesException; -import com.mapbox.services.api.directions.v4.DirectionsCriteria; -import com.mapbox.services.api.directions.v4.MapboxDirections; -import com.mapbox.services.api.directions.v4.models.DirectionsResponse; -import com.mapbox.services.api.directions.v4.models.DirectionsRoute; -import com.mapbox.services.api.directions.v4.models.Waypoint; -import com.mapbox.services.commons.geojson.LineString; -import com.mapbox.services.commons.models.Position; - -import java.util.List; - -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -public class DirectionsV4Activity extends AppCompatActivity { - - private static final String LOG_TAG = "DirectionsV4Activity"; - - private MapView mapView = null; - private MapboxMap mapboxMap = null; - - private DirectionsRoute currentRoute = null; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_directions_v4); - - // Dupont Circle (Washington, DC) - final Waypoint origin = new Waypoint(-77.04341, 38.90962); - - // The White House (Washington, DC) - final Waypoint destination = new Waypoint(-77.0365, 38.8977); - - // Centroid - final LatLng centroid = new LatLng( - (origin.getLatitude() + destination.getLatitude()) / 2, - (origin.getLongitude() + destination.getLongitude()) / 2); - - // Set up a standard Mapbox map - mapView = (MapView) findViewById(R.id.mapview); - mapView.onCreate(savedInstanceState); - mapView.getMapAsync(new OnMapReadyCallback() { - @Override - public void onMapReady(MapboxMap mapboxMapReady) { - mapboxMap = mapboxMapReady; - - mapboxMap.setStyleUrl(Style.MAPBOX_STREETS); - - // Move map - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(centroid) - .zoom(14) - .build(); - mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); - - // Add origin and destination to the map - mapboxMap.addMarker(new MarkerOptions() - .position(new LatLng(origin.getLatitude(), origin.getLongitude())) - .title("Origin") - .snippet("Dupont Circle")); - mapboxMap.addMarker(new MarkerOptions() - .position(new LatLng(destination.getLatitude(), destination.getLongitude())) - .title("Destination") - .snippet("The White House")); - - // Get route from API - try { - getRoute(origin, destination); - } catch (ServicesException servicesException) { - servicesException.printStackTrace(); - } - } - }); - } - - private void getRoute(Waypoint origin, Waypoint destination) throws ServicesException { - MapboxDirections client = new MapboxDirections.Builder() - .setAccessToken(Utils.getMapboxAccessToken(this)) - .setOrigin(origin) - .setDestination(destination) - .setProfile(DirectionsCriteria.PROFILE_WALKING) - .build(); - - client.enqueueCall(new Callback() { - @Override - public void onResponse(Call call, Response response) { - // You can get generic HTTP info about the response - Log.d(LOG_TAG, "Response code: " + response.code()); - if (response.body() == null) { - Log.e(LOG_TAG, "No routes found, make sure you set the right user and access token."); - return; - } - - // Print some info about the route - currentRoute = response.body().getRoutes().get(0); - Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance()); - showMessage(String.format("Route is %d meters long.", currentRoute.getDistance())); - - // Draw the route on the map - drawRoute(currentRoute); - } - - @Override - public void onFailure(Call call, Throwable throwable) { - Log.e(LOG_TAG, "Error: " + throwable.getMessage()); - showMessage("Error: " + throwable.getMessage()); - } - }); - } - - private void drawRoute(DirectionsRoute route) { - // Convert LineString coordinates into LatLng[] - LineString lineString = route.asLineString(Constants.OSRM_PRECISION_V4); - List coordinates = lineString.getCoordinates(); - LatLng[] points = new LatLng[coordinates.size()]; - for (int i = 0; i < coordinates.size(); i++) { - points[i] = new LatLng( - coordinates.get(i).getLatitude(), - coordinates.get(i).getLongitude()); - } - - // Draw Points on MapView - mapboxMap.addPolyline(new PolylineOptions() - .add(points) - .color(Color.parseColor("#3887be")) - .width(5)); - } - - private void showMessage(String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - @Override - public void onResume() { - super.onResume(); - mapView.onResume(); - } - - @Override - public void onPause() { - super.onPause(); - mapView.onPause(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - mapView.onSaveInstanceState(outState); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mapView.onDestroy(); - } - - @Override - public void onLowMemory() { - super.onLowMemory(); - mapView.onLowMemory(); - } -} diff --git a/mapbox/app/src/main/res/layout/activity_directions_v4.xml b/mapbox/app/src/main/res/layout/activity_directions_v4.xml deleted file mode 100644 index 6d4003edc..000000000 --- a/mapbox/app/src/main/res/layout/activity_directions_v4.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/mapbox/app/src/main/res/values/strings.xml b/mapbox/app/src/main/res/values/strings.xml index 93aa6e281..c072740ba 100644 --- a/mapbox/app/src/main/res/values/strings.xml +++ b/mapbox/app/src/main/res/values/strings.xml @@ -26,7 +26,6 @@ Distance Directions v5 Route Utils v5 - Directions v4 Directions icons Reverse geocoding Geocoding widget @@ -46,7 +45,6 @@ Returns all travel times between many points. Show you how to get where you\'re going. - Deprecated Used for testing the RouteUtils class. Icons that can used for navigation. Pick a point on the map and receive a geocoded result. From 2862efee8bd0fecebf642de0287d1e0cc93e1faa Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Wed, 11 Jan 2017 16:46:30 -0500 Subject: [PATCH 02/16] replace PermissionsUtils with a more core PermissionsManager --- .../android/testapp/MainActivity.java | 28 +++- .../android/utils/PermissionsUtils.java | 122 ------------------ .../services/android/utils/package-info.java | 4 - .../permissions/PermissionsListener.java | 14 ++ .../permissions/PermissionsManager.java | 105 +++++++++++++++ 5 files changed, 141 insertions(+), 132 deletions(-) delete mode 100644 mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/PermissionsUtils.java delete mode 100644 mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/package-info.java create mode 100644 mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java create mode 100644 mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java index 85324f31c..d4446ef37 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java @@ -11,8 +11,11 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import android.widget.Toast; import com.mapbox.services.android.BuildConfig; +import com.mapbox.services.android.telemetry.permissions.PermissionsListener; +import com.mapbox.services.android.telemetry.permissions.PermissionsManager; import com.mapbox.services.android.testapp.directions.DirectionsV5Activity; import com.mapbox.services.android.testapp.directions.RouteUtilsV5Activity; import com.mapbox.services.android.testapp.distance.DistanceActivity; @@ -31,7 +34,6 @@ import com.mapbox.services.android.testapp.turf.TurfMidpointActivity; import com.mapbox.services.android.testapp.utils.MapMatchingActivity; import com.mapbox.services.android.testapp.utils.SimplifyPolylineActivity; -import com.mapbox.services.android.utils.PermissionsUtils; import java.util.ArrayList; import java.util.Arrays; @@ -41,11 +43,12 @@ * This activity shows how to use PermissionsUtils to request location permissions * from the user. It loads all the sample activities using a RecyclerView. */ -public class MainActivity extends AppCompatActivity { +public class MainActivity extends AppCompatActivity implements PermissionsListener { private static final String LOG_TAG = "MainActivity"; private RecyclerView recyclerView; + private PermissionsManager permissionsManager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -162,18 +165,31 @@ protected void onCreate(Bundle savedInstanceState) { recyclerView.setAdapter(adapter); // Check for location permission - if (!PermissionsUtils.isLocationGranted(this)) { + permissionsManager = new PermissionsManager(this, this); + if (!permissionsManager.areLocationPermissionsGranted()) { recyclerView.setEnabled(false); - PermissionsUtils.startPermissionFlow(this); + permissionsManager.requestLocationPermissions(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { - if (PermissionsUtils.isRequestSuccessful(requestCode, permissions, grantResults)) { + permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onExplanationNeeded(List permissionsToExplain) { + Toast.makeText(this, "This app needs location permissions in order to show its functionality.", + Toast.LENGTH_LONG).show(); + } + + @Override + public void onPermissionResult(boolean granted) { + if (granted) { recyclerView.setEnabled(true); } else { - PermissionsUtils.explainFallback(this); + Toast.makeText(this, "You didn't grant location permissions.", + Toast.LENGTH_LONG).show(); } } diff --git a/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/PermissionsUtils.java b/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/PermissionsUtils.java deleted file mode 100644 index c5325506f..000000000 --- a/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/PermissionsUtils.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.mapbox.services.android.utils; - -import android.Manifest; -import android.app.Activity; -import android.content.pm.PackageManager; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.widget.Toast; - -/** - * Utilities to handle the new permission system in 6.0. We only focus on ACCESS_FINE_LOCATION. - * - * @see Working with System Permissions - * @since 1.0.0 - */ -public class PermissionsUtils { - - private static final String permission = Manifest.permission.ACCESS_FINE_LOCATION; - - private static final int LOCATION_PERMISSIONS_REQUEST = 0; - - private static final String MESSAGE_RATIONALE = - "This app needs access to your GPS location to locate you on the map."; - - private static final String MESSAGE_FALLBACK = - "Access to your GPS location has been disabled."; - - /** - * We only need to check for ACCESS_FINE_LOCATION as this implies ACCESS_COARSE_LOCATION: - * - * @param activity The activity where this is methods being used. - * @return boolean true if location permission has been granted. - * @see Requesting User Permissions - * @see Location permissions - * @since 1.0.0 - */ - public static boolean isLocationGranted(Activity activity) { - int permissionCheck = ContextCompat.checkSelfPermission(activity, permission); - return (permissionCheck == PackageManager.PERMISSION_GRANTED); - } - - /** - * Request user permission - * - * @param activity The activity where this is methods being used. - * @since 1.0.0 - */ - public static void startPermissionFlow(Activity activity) { - startPermissionFlow(activity, MESSAGE_RATIONALE); - } - - /** - * Request user permission - * - * @param activity The activity where this is methods being used. - * @param rationale Defaults "MESSAGE_RATIONALE" - * @since 1.0.0 - */ - public static void startPermissionFlow(Activity activity, String rationale) { - // Should we show an explanation? - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { - // Show an explanation to the user *asynchronously* -- don't block - // this thread waiting for the user's response! After the user - // sees the explanation, try again to request the permission. - Toast.makeText(activity, rationale, Toast.LENGTH_LONG).show(); - requestPermissions(activity); - } else { - // No explanation needed, we can request the permission. - requestPermissions(activity); - } - } - - private static void requestPermissions(Activity activity) { - ActivityCompat.requestPermissions(activity, - new String[] {permission}, - LOCATION_PERMISSIONS_REQUEST); - } - - /** - * Use this method to check if the request was successful or not. - * - * @param requestCode The request code passed in requestPermissions(android.app.Activity, String[], int) - * @param permissions The requested permissions. Never null. - * @param grantResults The grant results for the corresponding permissions which is either - * PERMISSION_GRANTED or PERMISSION_DENIED. Never null. - * @return boolean true if permission was granted. - * @since 1.0.0 - */ - public static boolean isRequestSuccessful(int requestCode, String[] permissions, int[] grantResults) { - boolean result = false; - if (requestCode == LOCATION_PERMISSIONS_REQUEST) { - // If request is cancelled, the result arrays are empty - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { - // permission was granted - result = true; - } - } - return result; - } - - /** - * Display to the user when their device GPS has been disabled. - * - * @param activity The activity where the message is being used. - * @since 1.0.0 - */ - public static void explainFallback(Activity activity) { - explainFallback(activity, MESSAGE_FALLBACK); - } - - /** - * Display to the user when their device GPS has been disabled. - * - * @param activity The activity where the message is being used. - * @param message The message, should either be MESSAGE_RATIONALE or MESSAGE_FALLBACK. - * @since 1.0.0 - */ - public static void explainFallback(Activity activity, String message) { - Toast.makeText(activity, message, Toast.LENGTH_LONG).show(); - } - -} diff --git a/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/package-info.java b/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/package-info.java deleted file mode 100644 index b0f909018..000000000 --- a/mapbox/libandroid-services/src/main/java/com/mapbox/services/android/utils/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Android Services classes. - */ -package com.mapbox.services.android.utils; diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java new file mode 100644 index 000000000..df19712b2 --- /dev/null +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java @@ -0,0 +1,14 @@ +package com.mapbox.services.android.telemetry.permissions; + +import java.util.List; + +/** + * Created by antonio on 1/11/17. + */ + +public interface PermissionsListener { + + void onExplanationNeeded(List permissionsToExplain); + void onPermissionResult(boolean granted); + +} diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java new file mode 100644 index 000000000..fa791de13 --- /dev/null +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java @@ -0,0 +1,105 @@ +package com.mapbox.services.android.telemetry.permissions; + +import android.Manifest; +import android.app.Activity; +import android.content.pm.PackageManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; + +import java.util.ArrayList; + +/** + * Helps request permissions at runtime + */ + +public class PermissionsManager { + + private final static String COARSE_LOCATION_PERMISSION = Manifest.permission.ACCESS_COARSE_LOCATION; + private final static String FINE_LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION; + + private final int REQUEST_PERMISSIONS_CODE = 0; + + private Activity activity; + private PermissionsListener listener; + + public PermissionsManager(Activity activity) { + this.activity = activity; + } + + public PermissionsManager(Activity activity, PermissionsListener listener) { + this.activity = activity; + this.listener = listener; + } + + public PermissionsListener getListener() { + return listener; + } + + public void setListener(PermissionsListener listener) { + this.listener = listener; + } + + public boolean isPermissionGranted(String permission) { + return ContextCompat.checkSelfPermission(activity, permission) == + PackageManager.PERMISSION_GRANTED; + } + + public boolean isCoarseLocationPermissionGranted() { + return isPermissionGranted(COARSE_LOCATION_PERMISSION); + } + + public boolean isFineLocationPermissionGranted() { + return isPermissionGranted(FINE_LOCATION_PERMISSION); + } + + public boolean areLocationPermissionsGranted() { + return isCoarseLocationPermissionGranted() && isFineLocationPermissionGranted(); + } + + public void requestLocationPermissions() { + // Request fine location permissions by default + requestLocationPermissions(true); + } + + public void requestLocationPermissions(boolean requestFineLocation) { + String[] permissions = requestFineLocation ? + new String[] {FINE_LOCATION_PERMISSION} : + new String[] {COARSE_LOCATION_PERMISSION}; + requestPermissions(permissions); + } + + public void requestPermissions(String[] permissions) { + ArrayList permissionsToExplain = new ArrayList<>(); + for (String permission : permissions) { + if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { + permissionsToExplain.add(permission); + } + } + + if (listener != null && permissionsToExplain.size() > 0) { + // The developer should show an explanation to the user asynchronously + listener.onExplanationNeeded(permissionsToExplain); + } + + + ActivityCompat.requestPermissions(activity, permissions, REQUEST_PERMISSIONS_CODE); + } + + /** + * You should call this method from your activity onRequestPermissionsResult. + * + * @param requestCode + * @param permissions + * @param grantResults + */ + public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + switch (requestCode) { + case REQUEST_PERMISSIONS_CODE: + if (listener != null) { + boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; + listener.onPermissionResult(granted); + } + } + } + +} From 61baddb46634fb8957ccbb5a5f7c6b7fc9ebcc64 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Wed, 11 Jan 2017 16:46:51 -0500 Subject: [PATCH 03/16] implement changes in testapp --- .../java/com/mapbox/services/android/testapp/MainActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java index d4446ef37..90ac991a1 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java @@ -40,7 +40,7 @@ import java.util.List; /** - * This activity shows how to use PermissionsUtils to request location permissions + * This activity shows how to use PermissionsManager to request location permissions * from the user. It loads all the sample activities using a RecyclerView. */ public class MainActivity extends AppCompatActivity implements PermissionsListener { From 5719f86764aa0d5a45803fd92de3b6bfbb0c209f Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 11:24:20 -0500 Subject: [PATCH 04/16] update to latest lost version --- mapbox/app/build.gradle | 7 ++++--- .../geocoding/GeocodingServiceActivity.java | 3 ++- .../geocoding/GeocodingWidgetActivity.java | 17 ++++++++++++++++- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/mapbox/app/build.gradle b/mapbox/app/build.gradle index 3253d68fa..db6cc5a61 100644 --- a/mapbox/app/build.gradle +++ b/mapbox/app/build.gradle @@ -52,9 +52,10 @@ dependencies { testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' // LOST - compile ('com.mapzen.android:lost:1.1.1') { - exclude group: 'com.google.guava' - } + compile 'com.mapzen.android:lost:2.1.2' + + // Google Play Services + compile 'com.google.android.gms:play-services-location:10.0.1' // Picasso (Static Image) compile 'com.squareup.picasso:picasso:2.5.2' diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingServiceActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingServiceActivity.java index c4303ee41..711cf1093 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingServiceActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingServiceActivity.java @@ -151,7 +151,8 @@ protected void onStart() { private void getLastLocation() { // Gets the best and most recent location currently available, which may be null // in rare cases when a location is not available. - lastLocation = LocationServices.FusedLocationApi.getLastLocation(); + //noinspection MissingPermission + lastLocation = LocationServices.FusedLocationApi.getLastLocation(lostApiClient); if (lastLocation != null) { // Determine whether a Geocoder is available. if (!AndroidGeocoder.isPresent()) { diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingWidgetActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingWidgetActivity.java index 664283a30..1c5899d40 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingWidgetActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/geocoding/GeocodingWidgetActivity.java @@ -22,6 +22,7 @@ import com.mapzen.android.lost.api.LocationListener; import com.mapzen.android.lost.api.LocationRequest; import com.mapzen.android.lost.api.LocationServices; +import com.mapzen.android.lost.api.LostApiClient; public class GeocodingWidgetActivity extends AppCompatActivity { @@ -65,13 +66,27 @@ public void onMapReady(MapboxMap mapboxMapReady) { LocationRequest request = LocationRequest.create() .setInterval(5000) .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); - LocationServices.FusedLocationApi.requestLocationUpdates(request, new LocationListener() { + LostApiClient lostApiClient = new LostApiClient.Builder(this).build(); + + //noinspection MissingPermission + LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, new LocationListener() { + @Override public void onLocationChanged(Location location) { Log.d(LOG_TAG, "New LOST location: " + location.toString()); autocomplete.setProximity(Position.fromCoordinates( location.getLongitude(), location.getLatitude())); } + + @Override + public void onProviderDisabled(String provider) { + Log.d(LOG_TAG, "onProviderDisabled: " + provider); + } + + @Override + public void onProviderEnabled(String provider) { + Log.d(LOG_TAG, "onProviderEnabled: " + provider); + } }); } From f4b53c23498de0063b7deeca78140df3859be7a6 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 11:25:05 -0500 Subject: [PATCH 05/16] abstract location engine that any provider can implement --- .../telemetry/location/LocationEngine.java | 33 +++++++++++++++++++ .../location/LocationEngineListener.java | 15 +++++++++ .../location/LocationEnginePriority.java | 16 +++++++++ 3 files changed, 64 insertions(+) create mode 100644 mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngine.java create mode 100644 mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java create mode 100644 mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngine.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngine.java new file mode 100644 index 000000000..72ee585f8 --- /dev/null +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngine.java @@ -0,0 +1,33 @@ +package com.mapbox.services.android.telemetry.location; + +import android.location.Location; + +/** + * In part inspired by the LocationSource interface + * + * https://developers.google.com/android/reference/com/google/android/gms/maps/LocationSource + */ + +public interface LocationEngine { + + void activate(); + + void deactivate(); + + boolean isConnected(); + + int getPriority(); + + void setPriority(int priority); + + Location getLastLocation(); + + void addLocationEngineListener(LocationEngineListener listener); + + boolean removeLocationEngineListener(LocationEngineListener listener); + + void requestLocationUpdates(); + + void removeLocationUpdates(); + +} diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java new file mode 100644 index 000000000..bb71b96f1 --- /dev/null +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java @@ -0,0 +1,15 @@ +package com.mapbox.services.android.telemetry.location; + +import android.location.Location; + +/** + * Created by antonio on 1/11/17. + */ + +public interface LocationEngineListener { + + void onConnected(); + + void onLocationChanged(Location location); + +} diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java new file mode 100644 index 000000000..8edf11424 --- /dev/null +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java @@ -0,0 +1,16 @@ +package com.mapbox.services.android.telemetry.location; + +/** + * Same priorities GMS and Lost support: + * + * https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest + * https://github.com/mapzen/lost/blob/master/lost/src/main/java/com/mapzen/android/lost/api/LocationRequest.java + */ +public class LocationEnginePriority { + + public static final int PRIORITY_NO_POWER = 0; + public static final int PRIORITY_LOW_POWER = 1; + public static final int PRIORITY_BALANCED_POWER_ACCURACY = 2; + public static final int PRIORITY_HIGH_ACCURACY = 3; + +} From 0a42d232d3f531a2a2f1bdc67f99a4de35089cfd Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 11:25:41 -0500 Subject: [PATCH 06/16] sample location engines implementations --- .../location/GoogleLocationEngine.java | 158 ++++++++++++++++++ .../testapp/location/LostLocationEngine.java | 157 +++++++++++++++++ .../testapp/location/MockLocationEngine.java | 115 +++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java create mode 100644 mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java create mode 100644 mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/MockLocationEngine.java diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java new file mode 100644 index 000000000..11ce55039 --- /dev/null +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java @@ -0,0 +1,158 @@ +package com.mapbox.services.android.testapp.location; + +import android.content.Context; +import android.location.Location; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.location.LocationListener; +import com.google.android.gms.location.LocationRequest; +import com.google.android.gms.location.LocationServices; +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +import com.mapbox.services.android.telemetry.permissions.PermissionsManager; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Sample LocationEngine using Google Play Services + */ +public class GoogleLocationEngine implements LocationEngine, + GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { + + private static final String LOG_TAG = GoogleLocationEngine.class.getSimpleName(); + + private static LocationEngine instance; + + private Context context; + private CopyOnWriteArrayList locationListeners; + private GoogleApiClient googleApiClient; + + private int priority; + + public GoogleLocationEngine(Context context) { + this.context = context; + locationListeners = new CopyOnWriteArrayList<>(); + googleApiClient = new GoogleApiClient.Builder(context) + .addConnectionCallbacks(this) + .addOnConnectionFailedListener(this) + .addApi(LocationServices.API) + .build(); + } + + public static LocationEngine getLocationEngine(Context context) { + if (instance == null) { + instance = new GoogleLocationEngine(context.getApplicationContext()); + } + + return instance; + } + + @Override + public void activate() { + if (googleApiClient != null && !googleApiClient.isConnected()) { + googleApiClient.connect(); + } + } + + @Override + public void deactivate() { + if (googleApiClient != null && googleApiClient.isConnected()) { + googleApiClient.disconnect(); + } + } + + @Override + public boolean isConnected() { + return googleApiClient.isConnected(); + } + + @Override + public void onConnected(@Nullable Bundle bundle) { + for (LocationEngineListener listener : this.locationListeners) { + listener.onConnected(); + } + } + + @Override + public void onConnectionSuspended(int cause) { + Log.d(LOG_TAG, "Connection suspended: " + cause); + } + + @Override + public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { + Log.d(LOG_TAG, "Connection failed:" + connectionResult.getErrorMessage()); + } + + @Override + public int getPriority() { + return priority; + } + + @Override + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public Location getLastLocation() { + if (googleApiClient.isConnected() + && PermissionsManager.isPermissionGranted(context, PermissionsManager.FINE_LOCATION_PERMISSION)) { + //noinspection MissingPermission + return LocationServices.FusedLocationApi.getLastLocation(googleApiClient); + } + + return null; + } + + @Override + public void addLocationEngineListener(LocationEngineListener listener) { + if (!this.locationListeners.contains(listener)) { + this.locationListeners.add(listener); + } + } + + @Override + public boolean removeLocationEngineListener(LocationEngineListener listener) { + return this.locationListeners.remove(listener); + } + + @Override + public void requestLocationUpdates() { + // Common params + LocationRequest request = LocationRequest.create() + .setFastestInterval(1000) + .setSmallestDisplacement(3.0f); + + // Priority matching is straightforward + if (priority == LocationEnginePriority.PRIORITY_NO_POWER) { + request.setPriority(LocationRequest.PRIORITY_NO_POWER); + } else if (priority == LocationEnginePriority.PRIORITY_LOW_POWER) { + request.setPriority(LocationRequest.PRIORITY_LOW_POWER); + } else if (priority == LocationEnginePriority.PRIORITY_BALANCED_POWER_ACCURACY) { + request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); + } else if (priority == LocationEnginePriority.PRIORITY_HIGH_ACCURACY) { + request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + } + + //noinspection MissingPermission + LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, request, this); + } + + @Override + public void removeLocationUpdates() { + LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this); + } + + @Override + public void onLocationChanged(Location location) { + for (LocationEngineListener listener : this.locationListeners) { + listener.onLocationChanged(location); + } + } +} diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java new file mode 100644 index 000000000..b482dcb34 --- /dev/null +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java @@ -0,0 +1,157 @@ +package com.mapbox.services.android.testapp.location; + +import android.content.Context; +import android.location.Location; +import android.util.Log; + +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +import com.mapbox.services.android.telemetry.permissions.PermissionsManager; +import com.mapzen.android.lost.api.LocationListener; +import com.mapzen.android.lost.api.LocationRequest; +import com.mapzen.android.lost.api.LocationServices; +import com.mapzen.android.lost.api.LostApiClient; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Sample LocationEngine using the Open Source Lost library + */ +public class LostLocationEngine implements + LocationEngine, LostApiClient.ConnectionCallbacks, LocationListener { + + private static final String LOG_TAG = LostLocationEngine.class.getSimpleName(); + + private static LocationEngine instance; + + private Context context; + private CopyOnWriteArrayList locationListeners; + private LostApiClient lostApiClient; + + private int priority; + + public LostLocationEngine(Context context) { + this.context = context; + locationListeners = new CopyOnWriteArrayList<>(); + lostApiClient = new LostApiClient.Builder(context) + .addConnectionCallbacks(this) + .build(); + } + + public static LocationEngine getLocationEngine(Context context) { + if (instance == null) { + instance = new LostLocationEngine(context.getApplicationContext()); + } + + return instance; + } + + @Override + public void activate() { + if (lostApiClient != null && !lostApiClient.isConnected()) { + lostApiClient.connect(); + } + } + + @Override + public void deactivate() { + if (lostApiClient != null && lostApiClient.isConnected()) { + lostApiClient.disconnect(); + } + } + + @Override + public boolean isConnected() { + return lostApiClient.isConnected(); + } + + @Override + public void onConnected() { + for (LocationEngineListener listener : this.locationListeners) { + listener.onConnected(); + } + } + + @Override + public void onConnectionSuspended() { + Log.d(LOG_TAG, "Connection suspended."); + } + + @Override + public int getPriority() { + return priority; + } + + @Override + public void setPriority(int priority) { + this.priority = priority; + } + + @Override + public Location getLastLocation() { + if (lostApiClient.isConnected() + && PermissionsManager.isPermissionGranted(context, PermissionsManager.FINE_LOCATION_PERMISSION)) { + //noinspection MissingPermission + return LocationServices.FusedLocationApi.getLastLocation(lostApiClient); + } + + return null; + } + + @Override + public void addLocationEngineListener(LocationEngineListener listener) { + if (!this.locationListeners.contains(listener)) { + this.locationListeners.add(listener); + } + } + + @Override + public boolean removeLocationEngineListener(LocationEngineListener listener) { + return this.locationListeners.remove(listener); + } + + @Override + public void requestLocationUpdates() { + // Common params + LocationRequest request = LocationRequest.create() + .setFastestInterval(1000) + .setSmallestDisplacement(3.0f); + + // Priority matching is straightforward + if (priority == LocationEnginePriority.PRIORITY_NO_POWER) { + request.setPriority(LocationRequest.PRIORITY_NO_POWER); + } else if (priority == LocationEnginePriority.PRIORITY_LOW_POWER) { + request.setPriority(LocationRequest.PRIORITY_LOW_POWER); + } else if (priority == LocationEnginePriority.PRIORITY_BALANCED_POWER_ACCURACY) { + request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); + } else if (priority == LocationEnginePriority.PRIORITY_HIGH_ACCURACY) { + request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); + } + + //noinspection MissingPermission + LocationServices.FusedLocationApi.requestLocationUpdates(lostApiClient, request, this); + } + + @Override + public void removeLocationUpdates() { + LocationServices.FusedLocationApi.removeLocationUpdates(lostApiClient, this); + } + + @Override + public void onLocationChanged(Location location) { + for (LocationEngineListener listener : this.locationListeners) { + listener.onLocationChanged(location); + } + } + + @Override + public void onProviderDisabled(String provider) { + Log.d(LOG_TAG, "Provider disabled: " + provider); + } + + @Override + public void onProviderEnabled(String provider) { + Log.d(LOG_TAG, "Provider enabled: " + provider); + } +} diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/MockLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/MockLocationEngine.java new file mode 100644 index 000000000..b5ae46096 --- /dev/null +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/MockLocationEngine.java @@ -0,0 +1,115 @@ +package com.mapbox.services.android.testapp.location; + +import android.location.Location; +import android.os.Handler; + +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Sample LocationEngine that provides mocked locations simulating GPS updates + */ +public class MockLocationEngine implements LocationEngine { + + // Mocked data + private static final int UPDATE_INTERVAL_MS = 1000; + private static final double[][] locations = new double[][] { + new double[] {38.909620, -77.043410}, + new double[] {38.909621, -77.043411}, + new double[] {38.909622, -77.043412}, + new double[] {38.909623, -77.043413}, + new double[] {38.909624, -77.043414}}; + + private CopyOnWriteArrayList locationListeners; + + private Handler handler; + int currentIndex; + + public MockLocationEngine() { + locationListeners = new CopyOnWriteArrayList<>(); + } + + @Override + public void activate() { + currentIndex = 0; + + // "Connection" is immediate here + for (LocationEngineListener listener : this.locationListeners) { + listener.onConnected(); + } + } + + @Override + public void deactivate() { + handler = null; + } + + @Override + public boolean isConnected() { + return true; // Always connected + } + + @Override + public int getPriority() { + return 0; // No effect + } + + @Override + public void setPriority(int priority) { + // No effect + } + + @Override + public Location getLastLocation() { + return getNextLocation(); + } + + @Override + public void addLocationEngineListener(LocationEngineListener listener) { + if (!this.locationListeners.contains(listener)) { + this.locationListeners.add(listener); + } + } + + @Override + public boolean removeLocationEngineListener(LocationEngineListener listener) { + return this.locationListeners.remove(listener); + } + + @Override + public void requestLocationUpdates() { + // Fake regular updates with a handler + handler = new Handler(); + handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS); + } + + @Override + public void removeLocationUpdates() { + handler.removeCallbacksAndMessages(null); + } + + private Location getNextLocation() { + // Build the next location and rotate the index + Location location = new Location(MockLocationEngine.class.getSimpleName()); + location.setLatitude(locations[currentIndex][0]); + location.setLongitude(locations[currentIndex][1]); + currentIndex = (currentIndex == locations.length - 1 ? 0 : currentIndex + 1); + return location; + } + + private class LocationUpdateRunnable implements Runnable { + @Override + public void run() { + // Notify of an update + Location location = getNextLocation(); + for (LocationEngineListener listener : locationListeners) { + listener.onLocationChanged(location); + } + + // Schedule the next update + handler.postDelayed(new LocationUpdateRunnable(), UPDATE_INTERVAL_MS); + } + } +} From 7c194bf6b166dbadfb9b0aa1b6ffd495654babf1 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 11:26:18 -0500 Subject: [PATCH 07/16] update permissions logic --- .../permissions/PermissionsListener.java | 1 + .../permissions/PermissionsManager.java | 33 +++++++++++-------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java index df19712b2..f90a5bb06 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java @@ -9,6 +9,7 @@ public interface PermissionsListener { void onExplanationNeeded(List permissionsToExplain); + void onPermissionResult(boolean granted); } diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java index fa791de13..b7fc5ca34 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java @@ -2,6 +2,7 @@ import android.Manifest; import android.app.Activity; +import android.content.Context; import android.content.pm.PackageManager; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; @@ -14,8 +15,8 @@ public class PermissionsManager { - private final static String COARSE_LOCATION_PERMISSION = Manifest.permission.ACCESS_COARSE_LOCATION; - private final static String FINE_LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION; + public static final String COARSE_LOCATION_PERMISSION = Manifest.permission.ACCESS_COARSE_LOCATION; + public static final String FINE_LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION; private final int REQUEST_PERMISSIONS_CODE = 0; @@ -39,17 +40,17 @@ public void setListener(PermissionsListener listener) { this.listener = listener; } - public boolean isPermissionGranted(String permission) { - return ContextCompat.checkSelfPermission(activity, permission) == - PackageManager.PERMISSION_GRANTED; + public static boolean isPermissionGranted(Context context, String permission) { + return ContextCompat.checkSelfPermission(context, permission) + == PackageManager.PERMISSION_GRANTED; } public boolean isCoarseLocationPermissionGranted() { - return isPermissionGranted(COARSE_LOCATION_PERMISSION); + return isPermissionGranted(activity, COARSE_LOCATION_PERMISSION); } public boolean isFineLocationPermissionGranted() { - return isPermissionGranted(FINE_LOCATION_PERMISSION); + return isPermissionGranted(activity, FINE_LOCATION_PERMISSION); } public boolean areLocationPermissionsGranted() { @@ -62,9 +63,9 @@ public void requestLocationPermissions() { } public void requestLocationPermissions(boolean requestFineLocation) { - String[] permissions = requestFineLocation ? - new String[] {FINE_LOCATION_PERMISSION} : - new String[] {COARSE_LOCATION_PERMISSION}; + String[] permissions = requestFineLocation + ? new String[] {FINE_LOCATION_PERMISSION} + : new String[] {COARSE_LOCATION_PERMISSION}; requestPermissions(permissions); } @@ -88,17 +89,21 @@ public void requestPermissions(String[] permissions) { /** * You should call this method from your activity onRequestPermissionsResult. * - * @param requestCode - * @param permissions - * @param grantResults + * @param requestCode The request code passed in requestPermissions(android.app.Activity, String[], int) + * @param permissions The requested permissions. Never null. + * @param grantResults The grant results for the corresponding permissions which is either + * PERMISSION_GRANTED or PERMISSION_DENIED. Never null. */ - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQUEST_PERMISSIONS_CODE: if (listener != null) { boolean granted = grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED; listener.onPermissionResult(granted); } + break; + default: + // Ignored } } From cfe40d334f938fd3518732ae5c52a8c1d0581f35 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 11:26:40 -0500 Subject: [PATCH 08/16] sample location engine activity --- mapbox/app/src/main/AndroidManifest.xml | 7 ++ .../android/testapp/MainActivity.java | 5 + .../location/LocationEngineActivity.java | 117 ++++++++++++++++++ .../res/layout/activity_location_engine.xml | 42 +++++++ mapbox/app/src/main/res/values/strings.xml | 9 ++ 5 files changed, 180 insertions(+) create mode 100644 mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java create mode 100644 mapbox/app/src/main/res/layout/activity_location_engine.xml diff --git a/mapbox/app/src/main/AndroidManifest.xml b/mapbox/app/src/main/AndroidManifest.xml index ab012c37b..ec6313e61 100644 --- a/mapbox/app/src/main/AndroidManifest.xml +++ b/mapbox/app/src/main/AndroidManifest.xml @@ -27,6 +27,13 @@ + + + diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java index 90ac991a1..51bc92453 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java @@ -24,6 +24,7 @@ import com.mapbox.services.android.testapp.geocoding.GeocodingWidgetActivity; import com.mapbox.services.android.testapp.icons.DirectionsIconsActivity; import com.mapbox.services.android.testapp.icons.MakiIconsActivity; +import com.mapbox.services.android.testapp.location.LocationEngineActivity; import com.mapbox.services.android.testapp.nav.OffRouteDetectionActivity; import com.mapbox.services.android.testapp.staticimage.StaticImageActivity; import com.mapbox.services.android.testapp.turf.TurfBearingActivity; @@ -56,6 +57,10 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_main); final List samples = new ArrayList<>(Arrays.asList( + new SampleItem( + getString(R.string.title_location), + getString(R.string.description_location), + LocationEngineActivity.class), new SampleItem( getString(R.string.title_distance), getString(R.string.description_distance), diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java new file mode 100644 index 000000000..5fed453a6 --- /dev/null +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java @@ -0,0 +1,117 @@ +package com.mapbox.services.android.testapp.location; + +import android.location.Location; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; + +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; +import com.mapbox.services.android.testapp.R; + +public class LocationEngineActivity extends AppCompatActivity + implements AdapterView.OnItemSelectedListener, LocationEngineListener { + + private static final String LOG_TAG = LocationEngineActivity.class.getSimpleName(); + + private TextView textLocation; + private LocationEngine locationEngine; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_location_engine); + textLocation = (TextView) findViewById(R.id.text_location); + setupSpinner(); + } + + @Override + protected void onResume() { + super.onResume(); + if (locationEngine != null && locationEngine.isConnected()) { + locationEngine.requestLocationUpdates(); + } + } + + @Override + protected void onPause() { + super.onPause(); + if (locationEngine != null) { + locationEngine.removeLocationUpdates(); + } + } + + private void setupSpinner() { + Spinner spinner = (Spinner) findViewById(R.id.spinner_engine); + + ArrayAdapter adapter = ArrayAdapter.createFromResource(this, + R.array.location_engines, android.R.layout.simple_spinner_item); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinner.setAdapter(adapter); + + spinner.setOnItemSelectedListener(this); + } + + @Override + public void onItemSelected(AdapterView parent, View view, int pos, long id) { + String engineName = (String) parent.getItemAtPosition(pos); + Log.d(LOG_TAG, "Engine selected: " + engineName); + setNoEngine(); + + String[] locationEngines = getResources().getStringArray(R.array.location_engines); + if (engineName.equals(locationEngines[1])) { + // Mock + locationEngine = new MockLocationEngine(); + } else if (engineName.equals(locationEngines[2])) { + // Lost + locationEngine = LostLocationEngine.getLocationEngine(this); + } else if (engineName.equals(locationEngines[3])) { + // Google Play Services + locationEngine = GoogleLocationEngine.getLocationEngine(this); + } + + if (!engineName.equals(locationEngines[0]) && locationEngine != null) { + // Not None + locationEngine.setPriority(LocationEnginePriority.PRIORITY_HIGH_ACCURACY); + locationEngine.addLocationEngineListener(this); + locationEngine.activate(); + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) { + Log.d(LOG_TAG, "No engine selected."); + setNoEngine(); + } + + private void setNoEngine() { + if (locationEngine != null) { + locationEngine.removeLocationUpdates(); + locationEngine.removeLocationEngineListener(this); + locationEngine.deactivate(); + } + + textLocation.setText("No location updates, yet."); + locationEngine = null; + } + + @Override + public void onConnected() { + Log.d(LOG_TAG, "Connected to engine, we can now request updates."); + locationEngine.requestLocationUpdates(); + } + + @Override + public void onLocationChanged(Location location) { + if (location != null) { + Log.d(LOG_TAG, "New location received: " + location.toString()); + textLocation.setText(location.toString()); + } + } +} diff --git a/mapbox/app/src/main/res/layout/activity_location_engine.xml b/mapbox/app/src/main/res/layout/activity_location_engine.xml new file mode 100644 index 000000000..b6d28b18d --- /dev/null +++ b/mapbox/app/src/main/res/layout/activity_location_engine.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/mapbox/app/src/main/res/values/strings.xml b/mapbox/app/src/main/res/values/strings.xml index c072740ba..bd62d9272 100644 --- a/mapbox/app/src/main/res/values/strings.xml +++ b/mapbox/app/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ Invalid latitude or longitude used + Location Engine Distance Directions v5 Route Utils v5 @@ -43,6 +44,7 @@ Off route detection + Shows how to obtain location from different engines. Returns all travel times between many points. Show you how to get where you\'re going. Used for testing the RouteUtils class. @@ -82,4 +84,11 @@ meters + + None + Mock + Lost + Google Play Services + + From 4fbe2773a7cc3304ac42c35400632a709f2da2df Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 13:50:16 -0500 Subject: [PATCH 09/16] let's not keep references to any activity or context --- .../android/testapp/MainActivity.java | 6 ++-- .../permissions/PermissionsManager.java | 31 ++++++++----------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java index 51bc92453..256ae3276 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/MainActivity.java @@ -170,10 +170,10 @@ protected void onCreate(Bundle savedInstanceState) { recyclerView.setAdapter(adapter); // Check for location permission - permissionsManager = new PermissionsManager(this, this); - if (!permissionsManager.areLocationPermissionsGranted()) { + permissionsManager = new PermissionsManager(this); + if (!permissionsManager.areLocationPermissionsGranted(this)) { recyclerView.setEnabled(false); - permissionsManager.requestLocationPermissions(); + permissionsManager.requestLocationPermissions(this); } } diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java index b7fc5ca34..20f1efefd 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java @@ -20,15 +20,9 @@ public class PermissionsManager { private final int REQUEST_PERMISSIONS_CODE = 0; - private Activity activity; private PermissionsListener listener; - public PermissionsManager(Activity activity) { - this.activity = activity; - } - - public PermissionsManager(Activity activity, PermissionsListener listener) { - this.activity = activity; + public PermissionsManager(PermissionsListener listener) { this.listener = listener; } @@ -45,31 +39,32 @@ public static boolean isPermissionGranted(Context context, String permission) { == PackageManager.PERMISSION_GRANTED; } - public boolean isCoarseLocationPermissionGranted() { - return isPermissionGranted(activity, COARSE_LOCATION_PERMISSION); + public static boolean isCoarseLocationPermissionGranted(Context context) { + return isPermissionGranted(context, COARSE_LOCATION_PERMISSION); } - public boolean isFineLocationPermissionGranted() { - return isPermissionGranted(activity, FINE_LOCATION_PERMISSION); + public static boolean isFineLocationPermissionGranted(Context context) { + return isPermissionGranted(context, FINE_LOCATION_PERMISSION); } - public boolean areLocationPermissionsGranted() { - return isCoarseLocationPermissionGranted() && isFineLocationPermissionGranted(); + public static boolean areLocationPermissionsGranted(Context context) { + return isCoarseLocationPermissionGranted(context) + && isFineLocationPermissionGranted(context); } - public void requestLocationPermissions() { + public void requestLocationPermissions(Activity activity) { // Request fine location permissions by default - requestLocationPermissions(true); + requestLocationPermissions(activity, true); } - public void requestLocationPermissions(boolean requestFineLocation) { + public void requestLocationPermissions(Activity activity, boolean requestFineLocation) { String[] permissions = requestFineLocation ? new String[] {FINE_LOCATION_PERMISSION} : new String[] {COARSE_LOCATION_PERMISSION}; - requestPermissions(permissions); + requestPermissions(activity, permissions); } - public void requestPermissions(String[] permissions) { + public void requestPermissions(Activity activity, String[] permissions) { ArrayList permissionsToExplain = new ArrayList<>(); for (String permission : permissions) { if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { From c94f63a6c7b4ffe9e0bf26251c9c09020c919fc5 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 13:50:25 -0500 Subject: [PATCH 10/16] typo --- mapbox/app/src/main/res/layout/activity_location_engine.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapbox/app/src/main/res/layout/activity_location_engine.xml b/mapbox/app/src/main/res/layout/activity_location_engine.xml index b6d28b18d..be11c1c54 100644 --- a/mapbox/app/src/main/res/layout/activity_location_engine.xml +++ b/mapbox/app/src/main/res/layout/activity_location_engine.xml @@ -14,7 +14,7 @@ From 882066a88353288e09ec2173f575def59167100a Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 13:58:47 -0500 Subject: [PATCH 11/16] only one is necessary to have location updates --- .../android/telemetry/permissions/PermissionsManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java index 20f1efefd..980d05775 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java @@ -49,7 +49,7 @@ public static boolean isFineLocationPermissionGranted(Context context) { public static boolean areLocationPermissionsGranted(Context context) { return isCoarseLocationPermissionGranted(context) - && isFineLocationPermissionGranted(context); + || isFineLocationPermissionGranted(context); } public void requestLocationPermissions(Activity activity) { From 37fdc75e926f22319f55c2c98b799279fa424b55 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 14:05:16 -0500 Subject: [PATCH 12/16] add synchronized keyworkd --- .../services/android/testapp/location/GoogleLocationEngine.java | 2 +- .../services/android/testapp/location/LostLocationEngine.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java index 11ce55039..7fd876331 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java @@ -45,7 +45,7 @@ public GoogleLocationEngine(Context context) { .build(); } - public static LocationEngine getLocationEngine(Context context) { + public static synchronized LocationEngine getLocationEngine(Context context) { if (instance == null) { instance = new GoogleLocationEngine(context.getApplicationContext()); } diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java index b482dcb34..70a1a8e2e 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java @@ -39,7 +39,7 @@ public LostLocationEngine(Context context) { .build(); } - public static LocationEngine getLocationEngine(Context context) { + public static synchronized LocationEngine getLocationEngine(Context context) { if (instance == null) { instance = new LostLocationEngine(context.getApplicationContext()); } From 42ec10a890f0d80d26c081e8c9521db92db531f1 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 14:05:21 -0500 Subject: [PATCH 13/16] fix javadoc --- .../android/telemetry/location/LocationEngineListener.java | 2 +- .../android/telemetry/permissions/PermissionsListener.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java index bb71b96f1..9cb1ac0ce 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEngineListener.java @@ -3,7 +3,7 @@ import android.location.Location; /** - * Created by antonio on 1/11/17. + * Callback used in LocationEngine */ public interface LocationEngineListener { diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java index f90a5bb06..d39c8a222 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsListener.java @@ -3,7 +3,7 @@ import java.util.List; /** - * Created by antonio on 1/11/17. + * Callback used in PermissionsManager */ public interface PermissionsListener { From 77eedf95a6d747df32f61212680938e074873baa Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 14:19:37 -0500 Subject: [PATCH 14/16] small javadoc fix --- .../android/telemetry/permissions/PermissionsManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java index 980d05775..703b61231 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/permissions/PermissionsManager.java @@ -10,9 +10,8 @@ import java.util.ArrayList; /** - * Helps request permissions at runtime + * Helps request permissions at runtime. */ - public class PermissionsManager { public static final String COARSE_LOCATION_PERMISSION = Manifest.permission.ACCESS_COARSE_LOCATION; From 910e28a704436aed5639467a6829c4277cd5b626 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 14:38:51 -0500 Subject: [PATCH 15/16] minor rename --- .../android/testapp/location/GoogleLocationEngine.java | 8 ++++---- .../android/testapp/location/LocationEngineActivity.java | 2 +- .../android/testapp/location/LostLocationEngine.java | 8 ++++---- .../telemetry/location/LocationEnginePriority.java | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java index 7fd876331..24ba2bdf5 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/GoogleLocationEngine.java @@ -130,13 +130,13 @@ public void requestLocationUpdates() { .setSmallestDisplacement(3.0f); // Priority matching is straightforward - if (priority == LocationEnginePriority.PRIORITY_NO_POWER) { + if (priority == LocationEnginePriority.NO_POWER) { request.setPriority(LocationRequest.PRIORITY_NO_POWER); - } else if (priority == LocationEnginePriority.PRIORITY_LOW_POWER) { + } else if (priority == LocationEnginePriority.LOW_POWER) { request.setPriority(LocationRequest.PRIORITY_LOW_POWER); - } else if (priority == LocationEnginePriority.PRIORITY_BALANCED_POWER_ACCURACY) { + } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) { request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - } else if (priority == LocationEnginePriority.PRIORITY_HIGH_ACCURACY) { + } else if (priority == LocationEnginePriority.HIGH_ACCURACY) { request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java index 5fed453a6..dcc271c74 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LocationEngineActivity.java @@ -78,7 +78,7 @@ public void onItemSelected(AdapterView parent, View view, int pos, long id) { if (!engineName.equals(locationEngines[0]) && locationEngine != null) { // Not None - locationEngine.setPriority(LocationEnginePriority.PRIORITY_HIGH_ACCURACY); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); locationEngine.addLocationEngineListener(this); locationEngine.activate(); } diff --git a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java index 70a1a8e2e..50798027d 100644 --- a/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java +++ b/mapbox/app/src/main/java/com/mapbox/services/android/testapp/location/LostLocationEngine.java @@ -119,13 +119,13 @@ public void requestLocationUpdates() { .setSmallestDisplacement(3.0f); // Priority matching is straightforward - if (priority == LocationEnginePriority.PRIORITY_NO_POWER) { + if (priority == LocationEnginePriority.NO_POWER) { request.setPriority(LocationRequest.PRIORITY_NO_POWER); - } else if (priority == LocationEnginePriority.PRIORITY_LOW_POWER) { + } else if (priority == LocationEnginePriority.LOW_POWER) { request.setPriority(LocationRequest.PRIORITY_LOW_POWER); - } else if (priority == LocationEnginePriority.PRIORITY_BALANCED_POWER_ACCURACY) { + } else if (priority == LocationEnginePriority.BALANCED_POWER_ACCURACY) { request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); - } else if (priority == LocationEnginePriority.PRIORITY_HIGH_ACCURACY) { + } else if (priority == LocationEnginePriority.HIGH_ACCURACY) { request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } diff --git a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java index 8edf11424..b1e25244d 100644 --- a/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java +++ b/mapbox/libandroid-telemetry/src/main/java/com/mapbox/services/android/telemetry/location/LocationEnginePriority.java @@ -8,9 +8,9 @@ */ public class LocationEnginePriority { - public static final int PRIORITY_NO_POWER = 0; - public static final int PRIORITY_LOW_POWER = 1; - public static final int PRIORITY_BALANCED_POWER_ACCURACY = 2; - public static final int PRIORITY_HIGH_ACCURACY = 3; + public static final int NO_POWER = 0; + public static final int LOW_POWER = 1; + public static final int BALANCED_POWER_ACCURACY = 2; + public static final int HIGH_ACCURACY = 3; } From 1993b46f107d9472ffb8d0df916d4f7dd64404d3 Mon Sep 17 00:00:00 2001 From: Antonio Zugaldia Date: Thu, 12 Jan 2017 14:45:13 -0500 Subject: [PATCH 16/16] update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a377872..f2bcca5bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Mapbox welcomes participation and contributions from everyone. +### v2.0.0 + +* `libjava` and `libandroid` now live under the same (Android Studio) project. These projects are now split up into: `libjava-core`, `libjava-geojson`, `libjava-services`, `libjava-services-rx`, `libandroid-telemetry`, `libandroid-services`, `libandroid-ui`, independently published to Maven to reduce method count on Android apps. +* New `LocationEngine` that can be implemented by different location providers (open source or proprietary), decoupled from other components so that it can be used for telemetry, user tracking (map), navigation, or mocking use-cases. Sample implementations for Lost (`LostLocationEngine`), Google Play Services (`GoogleLocationEngine`) and Mock provider (`MockLocationEngine`). +* New `PermissionsManager` and `PermissionsListener` that replaces the old `PermissionsUtils` to be more flexible checking for any kind of permission, not just location, at runtime. + ### v1.3.2 * Geocoding: added poi.landmark type