diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
index f99516bb0bc..a18b864cfe4 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerViewManager.java
@@ -8,6 +8,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import com.mapbox.mapboxsdk.R;
@@ -376,11 +377,7 @@ public void onClick(final View v) {
}
if (!clickHandled) {
- // InfoWindow offset
- int infoWindowOffsetX = (int) ((adaptedView.getWidth() * marker.getInfoWindowAnchorU()) - marker.getOffsetX());
- int infoWindowOffsetY = (int) ((adaptedView.getHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY());
- marker.setTopOffsetPixels(infoWindowOffsetY);
- marker.setRightOffsetPixels(infoWindowOffsetX);
+ ensureInfoWindowOffset(marker);
select(marker, v, adapter);
}
}
@@ -397,6 +394,44 @@ public void onClick(final View v) {
}
}
+ //TODO: This whole method is a stopgap for: https://github.com/mapbox/mapbox-gl-native/issues/5384
+ public void ensureInfoWindowOffset(MarkerView marker) {
+ View view = null;
+ if (markerViewMap.containsKey(marker)) {
+ view = markerViewMap.get(marker);
+ } else {
+ for (final MapboxMap.MarkerViewAdapter adapter : markerViewAdapters) {
+ if (adapter.getMarkerClass().equals(marker.getClass())) {
+ View convertView = (View) adapter.getViewReusePool().acquire();
+ view = adapter.getView(marker, convertView, mapView);
+ break;
+ }
+ }
+ }
+
+ if (view != null) {
+ //Ensure the marker's view is measured first
+ if (view.getMeasuredWidth() == 0) {
+ view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ }
+
+ // update position on map
+ if (marker.getOffsetX() == -1) {
+ PointF point = mapboxMap.getProjection().toScreenLocation(marker.getPosition());
+ int x = (int) (marker.getAnchorU() * view.getMeasuredWidth());
+ int y = (int) (marker.getAnchorV() * view.getMeasuredHeight());
+ marker.setOffsetX(x);
+ marker.setOffsetY(y);
+ }
+
+ // InfoWindow offset
+ int infoWindowOffsetX = (int) ((view.getMeasuredWidth() * marker.getInfoWindowAnchorU()) - marker.getOffsetX());
+ int infoWindowOffsetY = (int) ((view.getMeasuredHeight() * marker.getInfoWindowAnchorV()) - marker.getOffsetY());
+ marker.setTopOffsetPixels(infoWindowOffsetY);
+ marker.setRightOffsetPixels(infoWindowOffsetX);
+ }
+ }
+
/**
* Default MarkerViewAdapter used for base class of MarkerView to adapt a MarkerView to an ImageView
*/
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index c26b0795262..b0df57fe1d1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -1142,6 +1142,11 @@ public void selectMarker(@NonNull Marker marker) {
}
if (!handledDefaultClick) {
+
+ if (marker instanceof MarkerView) {
+ mMarkerViewManager.ensureInfoWindowOffset((MarkerView) marker);
+ }
+
if (isInfoWindowValidForMarker(marker) || getInfoWindowAdapter() != null) {
mInfoWindows.add(marker.showInfoWindow(this, mMapView));
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
index 5c6265c7255..10ddcd90b71 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
+++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle
@@ -90,13 +90,8 @@ dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta1'
- // Directions SDK
- compile('com.mapbox.mapboxsdk:mapbox-android-directions:1.0.0@aar') {
- transitive = true
- }
-
- // Geocoder SDK
- compile('com.mapbox.mapboxsdk:mapbox-android-geocoder:1.0.0@aar') {
+ // Mapbox Android Services
+ compile('com.mapbox.mapboxsdk:mapbox-android-services:1.1.0@aar') {
transitive = true
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index c3bd231d309..593ba8403c6 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -270,6 +270,14 @@
android:value="@string/category_annotation" />
+
+
+
+
() {
- @Override
- public void onResponse(Response response, Retrofit retrofit) {
- // You can get generic HTTP info about the response
- Log.d(LOG_TAG, "Response code: " + response.code());
-
- // Print some info about the route
- DirectionsRoute currentRoute = response.body().getRoutes().get(0);
- Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
-
- // Draw the route on the map
- drawRoute(currentRoute);
- }
-
- @Override
- public void onFailure(Throwable t) {
- Log.e(LOG_TAG, "Error: " + t.getMessage());
- }
- });
+ try {
+ MapboxDirections md = new MapboxDirections.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setOrigin(origin)
+ .setDestination(destination)
+ .setProfile(DirectionsCriteria.PROFILE_WALKING)
+ .build();
+
+ md.enqueueCall(new Callback() {
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Log.e(LOG_TAG, "Error: " + t.getMessage());
+ }
+
+ @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());
+
+ // Print some info about the route
+ DirectionsRoute currentRoute = response.body().getRoutes().get(0);
+ Log.d(LOG_TAG, "Distance: " + currentRoute.getDistance());
+
+ // Draw the route on the map
+ drawRoute(currentRoute);
+ }
+
+ });
+ } catch (ServicesException e) {
+ Log.e(LOG_TAG, "Error displaying route: " + e.toString());
+ e.printStackTrace();
+ }
}
private void drawRoute(DirectionsRoute route) {
- // Convert List into LatLng[]
- List waypoints = route.getGeometry().getWaypoints();
- LatLng[] point = new LatLng[waypoints.size()];
- for (int i = 0; i < waypoints.size(); i++) {
- point[i] = new LatLng(
- waypoints.get(i).getLatitude(),
- waypoints.get(i).getLongitude());
+
+ PolylineOptions builder = new PolylineOptions();
+ builder.color(Color.parseColor("#3887be"));
+ builder.alpha(0.5f);
+ builder.width(5);
+ builder.width(5);
+ LineString lineString = route.asLineString(Constants.OSRM_PRECISION_V4);
+ for (Position coordinates : lineString.getCoordinates()) {
+ builder.add(new LatLng(coordinates.getLatitude(), coordinates.getLongitude()));
}
// Draw Points on MapView
- mMapboxMap.addPolyline(new PolylineOptions()
- .add(point)
- .color(Color.parseColor("#3887be"))
- .width(5));
+ mMapboxMap.addPolyline(builder);
}
@Override
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
index 99d2f81aadc..7c99e16b2ef 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/geocoding/GeocoderActivity.java
@@ -14,11 +14,6 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
-
-import com.mapbox.geocoder.GeocoderCriteria;
-import com.mapbox.geocoder.MapboxGeocoder;
-import com.mapbox.geocoder.service.models.GeocoderFeature;
-import com.mapbox.geocoder.service.models.GeocoderResponse;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -27,12 +22,19 @@
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
import com.mapbox.mapboxsdk.maps.Projection;
import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.ServicesException;
+import com.mapbox.services.commons.models.Position;
+import com.mapbox.services.geocoding.v5.GeocodingCriteria;
+import com.mapbox.services.geocoding.v5.MapboxGeocoding;
+import com.mapbox.services.geocoding.v5.models.GeocodingFeature;
+import com.mapbox.services.geocoding.v5.models.GeocodingResponse;
import java.util.List;
-import retrofit.Callback;
-import retrofit.Response;
-import retrofit.Retrofit;
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+import retrofit2.Retrofit;
public class GeocoderActivity extends AppCompatActivity {
@@ -123,40 +125,38 @@ protected void onSaveInstanceState(Bundle outState) {
*/
private void geocode(final LatLng point) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- MapboxGeocoder client = new MapboxGeocoder.Builder()
- .setAccessToken(getString(R.string.mapbox_access_token))
- .setCoordinates(point.getLongitude(), point.getLatitude())
- .setType(GeocoderCriteria.TYPE_POI)
- .build();
-
- client.enqueue(new Callback()
-
- {
- @Override
- public void onResponse(Response response, Retrofit retrofit) {
- List results = response.body().getFeatures();
- if (results.size() > 0) {
- String placeName = results.get(0).getPlaceName();
- setSuccess(placeName);
- } else {
- setMessage("No results.");
- }
- }
-
- @Override
- public void onFailure(Throwable t) {
- setError(t.getMessage());
- }
- }
-
- );
- return null;
- }
- }.execute();
+ try {
+ MapboxGeocoding client = new MapboxGeocoding.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setCoordinates(Position.fromCoordinates(point.getLongitude(), point.getLatitude()))
+ .setType(GeocodingCriteria.TYPE_POI)
+ .build();
+
+ client.enqueueCall(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ List results = response.body().getFeatures();
+ if (results.size() > 0) {
+ String placeName = results.get(0).getPlaceName();
+ setSuccess(placeName);
+ } else {
+ setMessage("No results.");
+ }
+
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ setError(t.getMessage());
+ }
+ });
+ } catch (ServicesException e) {
+ Log.e(LOG_TAG, "Error geocoding: " + e.toString());
+ e.printStackTrace();
+ setError(e.getMessage());
+ }
}
/*
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java
new file mode 100644
index 00000000000..7a6e425652b
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/navigation/LocationPickerActivity.java
@@ -0,0 +1,467 @@
+package com.mapbox.mapboxsdk.testapp.activity.navigation;
+
+import android.Manifest;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.PointF;
+import android.location.Location;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.MarkerOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+import com.mapbox.mapboxsdk.annotations.MarkerViewOptions;
+import com.mapbox.mapboxsdk.camera.CameraPosition;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.location.LocationListener;
+import com.mapbox.mapboxsdk.location.LocationServices;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.mapboxsdk.testapp.model.annotations.PulseMarkerView;
+import com.mapbox.mapboxsdk.testapp.model.annotations.PulseMarkerViewOptions;
+import com.mapbox.services.commons.ServicesException;
+import com.mapbox.services.commons.models.Position;
+import com.mapbox.services.geocoding.v5.GeocodingCriteria;
+import com.mapbox.services.geocoding.v5.MapboxGeocoding;
+import com.mapbox.services.geocoding.v5.models.GeocodingFeature;
+import com.mapbox.services.geocoding.v5.models.GeocodingResponse;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.Callback;
+import retrofit2.Response;
+
+/**
+ * Sample Activity to show a typical location picker use case
+ */
+public class LocationPickerActivity extends AppCompatActivity {
+ private static final String TAG = "LocationPickerActivity";
+ private static final int REQUEST_PERMISSIONS = 101;
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ private ImageView dropPinView;
+ private Marker addressPin;
+ private ImageButton clearDisplayViewButton;
+ private MarkerView userMarker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_location_picker);
+
+ setupActionBar();
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+ //Create ui elements
+ createDropPin();
+ createSelectLocationButton();
+ createClearSelectionButton();
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ //Store for later
+ mapboxMap = map;
+
+ //Add user marker
+ mapboxMap.getMarkerViewManager().addMarkerViewAdapter(new PulseMarkerViewAdapter(LocationPickerActivity.this));
+ userMarker = createCustomUserMarker(new LatLng(0, 0));
+
+ //Fix the focal point to the center of the map
+ PointF focalPoint = new PointF((mapView.getX() + mapView.getWidth() / 2), (mapView.getY() + mapView.getHeight() / 2));
+ mapboxMap.getUiSettings().setFocalPoint(focalPoint);
+
+ //Track camera updates to animate the user location views
+ trackUserLocationView(userMarker);
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+
+ //Check permissions
+ if (arePermissionsGranted()) {
+ mapView.getMapAsync(new OnMapReadyCallback() {
+
+ @Override
+ public void onMapReady(final MapboxMap mapboxMap) {
+ //Get the user's location
+ final LocationServices locationServices = LocationServices.getLocationServices(getApplicationContext());
+
+ Location location = locationServices.getLastLocation();
+ if (location != null) {
+ zoomInOn(location);
+ userMarker.setPosition(new LatLng(location));
+ } else {
+ final ProgressDialog loadingDialog = ProgressDialog.show(LocationPickerActivity.this, "Loading", "Getting user location", false);
+ locationServices.addLocationListener(new LocationListener() {
+ @Override
+ public void onLocationChanged(@Nullable Location location) {
+ //Move the camera to the user
+ if (location != null) {
+ zoomInOn(location);
+ userMarker.setPosition(new LatLng(location));
+ locationServices.removeLocationListener(this);
+ loadingDialog.hide();
+ }
+ }
+ });
+ }
+
+ locationServices.toggleGPS(true);
+ }
+ });
+ }
+ }
+
+ private void zoomInOn(Location location) {
+ //Move the camera to the user
+ if (location != null) {
+ mapboxMap.setCameraPosition(new CameraPosition.Builder()
+ .target(new LatLng(location))
+ .zoom(16)
+ .bearing(0)
+ .tilt(0)
+ .build());
+ }
+ }
+
+
+ /**
+ * Tracks the camera to animate the marker when overlapping with the picker.
+ * Makes sure the marker actually points to the user's position by tracking it.
+ */
+ private void trackUserLocationView(final MarkerView markerView) {
+ final float circleDiameterSize = getResources().getDimension(R.dimen.circle_size);
+
+ //Track camera changes to check for overlap
+ mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() {
+
+ private Animation pulseAnimation;
+
+ @Override
+ public void onCameraChange(CameraPosition position) {
+ if (markerView == null) {
+ return;
+ }
+
+ //Make drop pin visible, if it wasn't already
+ showDropPin();
+
+ //Get the distance from the tip of the location picker to the MarkerView
+ double distance = getLocationPickerLocation().distanceTo(markerView.getPosition());
+
+ //If closeby, animate, otherwise, stop animation
+ View view = mapboxMap.getMarkerViewManager().getView(markerView);
+ if (view != null) {
+ View backgroundView = view.findViewById(R.id.background_imageview);
+ if (pulseAnimation == null && distance < 0.5f * circleDiameterSize) {
+ pulseAnimation = AnimationUtils.loadAnimation(LocationPickerActivity.this, R.anim.pulse);
+ pulseAnimation.setRepeatCount(Animation.INFINITE);
+ pulseAnimation.setRepeatMode(Animation.RESTART);
+ backgroundView.startAnimation(pulseAnimation);
+ } else if (pulseAnimation != null && distance >= 0.5f * circleDiameterSize) {
+ backgroundView.clearAnimation();
+ pulseAnimation = null;
+ }
+ }
+ }
+ });
+
+ //Track location updates to move the user marker
+ LocationServices.getLocationServices(getApplicationContext()).addLocationListener(new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ if (location != null && markerView != null) {
+ markerView.setPosition(new LatLng(location));
+ }
+ }
+ });
+ }
+
+ private MarkerView createCustomUserMarker(LatLng markerPosition) {
+ return mapboxMap.addMarker(new PulseMarkerViewOptions()
+ .icon(IconFactory.getInstance(getApplicationContext()).fromResource(R.drawable.ic_my_location_24dp))
+ .position(markerPosition)
+ );
+ }
+
+ private void createClearSelectionButton() {
+ clearDisplayViewButton = (ImageButton) findViewById(R.id.clearDisplayViewButton);
+ clearDisplayViewButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ removeAddressPin();
+ hide(clearDisplayViewButton);
+ showDropPin();
+ }
+ });
+ }
+
+ private void createSelectLocationButton() {
+ Button selectLocationButton = (Button) findViewById(R.id.selectLocationButton);
+ //noinspection ConstantConditions
+ selectLocationButton.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "Location Selected!");
+ if (mapboxMap != null) {
+ //Control button's state
+ clearDisplayViewButton.setVisibility(View.VISIBLE);
+ dropPinView.setVisibility(View.INVISIBLE);
+
+ //Get position for the drop pin
+ LatLng position = getLocationPickerLocation();
+
+ //Show the address pin (result)
+ showAddressPin(position);
+
+ //Get the address for that location and update the marker
+ geocode(position, new GeocodeCallbacks() {
+ @Override
+ public void onResult(String result) {
+ updateAddressPin(result);
+ }
+
+ @Override
+ public void onFailure(Throwable failure) {
+ showFeedbackMessage("Could not retrieve address: " + failure.getMessage());
+ }
+ });
+ }
+ }
+ }
+ );
+ }
+
+ private void createDropPin() {
+ float density = getResources().getDisplayMetrics().density;
+
+ dropPinView = new ImageView(this);
+ dropPinView.setImageResource(R.drawable.ic_droppin_24dp);
+ FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER);
+ params.bottomMargin = (int) (12 * density);
+ dropPinView.setLayoutParams(params);
+
+ mapView.addView(dropPinView);
+ }
+
+ private void showDropPin() {
+ if (dropPinView != null && dropPinView.getVisibility() != View.VISIBLE) {
+ dropPinView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void hide(View view) {
+ if (view != null) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ /**
+ * Get address for the given location
+ */
+ private void geocode(LatLng point, final GeocodeCallbacks callbacks) {
+ try {
+ //Create Geocoding client
+ MapboxGeocoding client = new MapboxGeocoding.Builder()
+ .setAccessToken(getString(R.string.mapbox_access_token))
+ .setCoordinates(Position.fromCoordinates(point.getLongitude(), point.getLatitude()))
+ .setType(GeocodingCriteria.TYPE_ADDRESS)
+ .build();
+
+ //Place the request
+ client.enqueueCall(new Callback() {
+ @Override
+ public void onResponse(Call call, Response response) {
+
+ List results = response.body().getFeatures();
+ String address = null;
+ if (results.size() > 0) {
+ GeocodingFeature feature = results.get(0);
+ address = feature.getAddress() + " " + feature.getText();
+ Log.i(TAG, "address " + address);
+ } else {
+ showFeedbackMessage("No results for search.");
+ }
+
+ callbacks.onResult(address);
+ }
+
+ @Override
+ public void onFailure(Call call, Throwable t) {
+ Log.e(TAG, "Geocoding Failure: " + t.getMessage());
+ callbacks.onFailure(t);
+ }
+ });
+ } catch (ServicesException e) {
+ Log.e(TAG, "Error geocoding: " + e.toString());
+ callbacks.onFailure(e);
+ }
+ }
+
+ private LatLng getLocationPickerLocation() {
+ return mapboxMap.getProjection().fromScreenLocation(
+ new PointF(dropPinView.getLeft() + (dropPinView.getWidth() / 2), dropPinView.getBottom())
+ );
+ }
+
+ private Marker showAddressPin(LatLng position) {
+ if (addressPin != null) {
+ //Remove previous pin
+ removeAddressPin();
+ }
+
+ //Create new one
+ addressPin = mapboxMap.addMarker(new MarkerViewOptions().title("Loading address...").position(position));
+ mapboxMap.selectMarker(addressPin);
+ return addressPin;
+ }
+
+ private void removeAddressPin() {
+ if (mapboxMap != null && addressPin != null) {
+ mapboxMap.removeMarker(addressPin);
+ }
+ }
+
+ private void updateAddressPin(@Nullable String address) {
+ if (addressPin != null) {
+ addressPin.setTitle(address == null ? "No address found" : address);
+ }
+ }
+
+ private void showFeedbackMessage(String message) {
+ Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+ }
+
+ private boolean arePermissionsGranted() {
+ if (Build.VERSION.SDK_INT >= 23 &&
+ checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
+ checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ Log.i(TAG, "Requesting permissions");
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_PERMISSIONS);
+ return false;
+ }
+ Log.i(TAG, "Permissions already granted");
+ return true;
+ }
+
+ @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();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ /**
+ * Custom MarkerViewAdapter for the pulsing marker
+ */
+ private static class PulseMarkerViewAdapter extends MapboxMap.MarkerViewAdapter {
+
+ private LayoutInflater inflater;
+
+ public PulseMarkerViewAdapter(@NonNull Context context) {
+ super(context);
+ this.inflater = LayoutInflater.from(context);
+ }
+
+ @Nullable
+ @Override
+ public View getView(@NonNull PulseMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) {
+ ViewHolder viewHolder;
+ if (convertView == null) {
+ viewHolder = new ViewHolder();
+ convertView = inflater.inflate(R.layout.view_pulse_marker, parent, false);
+ viewHolder.foregroundImageView = (ImageView) convertView.findViewById(R.id.foreground_imageView);
+ viewHolder.backgroundImageView = (ImageView) convertView.findViewById(R.id.background_imageview);
+ convertView.setTag(viewHolder);
+ }
+ return convertView;
+ }
+
+ private static class ViewHolder {
+ ImageView foregroundImageView;
+ ImageView backgroundImageView;
+ }
+ }
+
+ private interface GeocodeCallbacks {
+ void onResult(String result);
+
+ void onFailure(Throwable failure);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java
new file mode 100644
index 00000000000..48867ea5eb3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerView.java
@@ -0,0 +1,11 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.MarkerView;
+
+public class PulseMarkerView extends MarkerView {
+
+ public PulseMarkerView(BaseMarkerViewOptions baseMarkerViewOptions) {
+ super(baseMarkerViewOptions);
+ }
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
new file mode 100644
index 00000000000..70ff6a22e2d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/PulseMarkerViewOptions.java
@@ -0,0 +1,79 @@
+package com.mapbox.mapboxsdk.testapp.model.annotations;
+
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions;
+import com.mapbox.mapboxsdk.annotations.Icon;
+import com.mapbox.mapboxsdk.annotations.IconFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+
+public class PulseMarkerViewOptions extends BaseMarkerViewOptions {
+
+ public PulseMarkerViewOptions() {
+ }
+
+ protected PulseMarkerViewOptions(Parcel in) {
+ position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
+ snippet(in.readString());
+ title(in.readString());
+ flat(in.readByte() != 0);
+ anchor(in.readFloat(), in.readFloat());
+ selected = in.readByte() != 0;
+ rotation(in.readFloat());
+ if (in.readByte() != 0) {
+ // this means we have an icon
+ String iconId = in.readString();
+ Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
+ Icon icon = IconFactory.recreate(iconId, iconBitmap);
+ icon(icon);
+ }
+ }
+
+ @Override
+ public PulseMarkerViewOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeParcelable(getPosition(), flags);
+ out.writeString(getSnippet());
+ out.writeString(getTitle());
+ out.writeByte((byte) (isFlat() ? 1 : 0));
+ out.writeFloat(getAnchorU());
+ out.writeFloat(getAnchorV());
+ out.writeFloat(getInfoWindowAnchorU());
+ out.writeFloat(getInfoWindowAnchorV());
+ out.writeByte((byte) (selected ? 1 : 0));
+ out.writeFloat(getRotation());
+ Icon icon = getIcon();
+ out.writeByte((byte) (icon != null ? 1 : 0));
+ if (icon != null) {
+ out.writeString(getIcon().getId());
+ out.writeParcelable(getIcon().getBitmap(), flags);
+ }
+ }
+
+ @Override
+ public PulseMarkerView getMarker() {
+ return new PulseMarkerView(this);
+ }
+
+ public static final Parcelable.Creator CREATOR
+ = new Parcelable.Creator() {
+ public CountryMarkerViewOptions createFromParcel(Parcel in) {
+ return new CountryMarkerViewOptions(in);
+ }
+
+ public CountryMarkerViewOptions[] newArray(int size) {
+ return new CountryMarkerViewOptions[size];
+ }
+ };
+}
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml
new file mode 100644
index 00000000000..40bc57ab684
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/anim/pulse.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 00000000000..006b2ce5a88
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml
new file mode 100644
index 00000000000..1e2d044bee2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_clear_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml
new file mode 100644
index 00000000000..f9fe77bfec2
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_picker.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml
new file mode 100644
index 00000000000..e1ac50b4407
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_pulse_marker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
index 98ec90c6fd4..c9bc0b85cfd 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml
@@ -1,6 +1,8 @@
+ 24dp
16dp
+ 8dp
10dp
32dp
96dp
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 2ec1b0d25d5..fba86a72137 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -35,6 +35,9 @@
Directions
Geocoder
+
+ Location Picker
+
Double Map Activity
Back to map activity
@@ -82,7 +85,7 @@
Restart map view after temporarily leaving to another activity
Use an Android SDK View as marker
Learn how to create a dynamic custom InfoWindow
-
+ Use a fixed Marker to select your location
Concurrent Open InfoWindows
Deselect Markers On Tap
Dismiss location tracking on gesture
@@ -101,6 +104,7 @@
Map Layout
Offline
User Location
+ Navigation
Center map around 2 markers
Remove polylines
@@ -162,4 +166,6 @@
Arsenal
Emirates Stadium
+ Select Location!
+
diff --git a/platform/android/build.gradle b/platform/android/build.gradle
index 23aef9d1e62..c99fc67a619 100644
--- a/platform/android/build.gradle
+++ b/platform/android/build.gradle
@@ -7,7 +7,7 @@ buildscript {
maven { url 'https://jitpack.io' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.1.0'
+ classpath 'com.android.tools.build:gradle:2.1.2'
classpath 'com.github.JakeWharton:sdk-manager-plugin:220bf7a88a7072df3ed16dc8466fb144f2817070'
// NOTE: Do not place your application dependencies here; they belong