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 @@ + + + + + + + + + +