diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity')
79 files changed, 13430 insertions, 0 deletions
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/FeatureOverviewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/FeatureOverviewActivity.java new file mode 100644 index 0000000000..74cb904dd9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/FeatureOverviewActivity.java @@ -0,0 +1,168 @@ +package com.mapbox.mapboxsdk.maps.activity; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.StringRes; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.adapter.FeatureAdapter; +import com.mapbox.mapboxsdk.testapp.adapter.FeatureSectionAdapter; +import com.mapbox.mapboxsdk.testapp.model.activity.Feature; +import com.mapbox.mapboxsdk.maps.utils.ItemClickSupport; +import timber.log.Timber; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Activity shown when application is started + * <p> + * This activity will generate data for RecyclerView based on the AndroidManifest entries. + * It uses tags as category and description to order the different entries. + * </p> + */ +public class FeatureOverviewActivity extends AppCompatActivity { + + private static final String KEY_STATE_FEATURES = "featureList"; + + private RecyclerView recyclerView; + private FeatureSectionAdapter sectionAdapter; + private List<Feature> features; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_feature_overview); + + recyclerView = (RecyclerView) findViewById(R.id.recyclerView); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener()); + recyclerView.setHasFixedSize(true); + + ItemClickSupport.addTo(recyclerView).setOnItemClickListener((recyclerView, position, view) -> { + if (!sectionAdapter.isSectionHeaderPosition(position)) { + int itemPosition = sectionAdapter.getConvertedPosition(position); + Feature feature = features.get(itemPosition); + startFeature(feature); + } + }); + + if (savedInstanceState == null) { + loadFeatures(); + } else { + features = savedInstanceState.getParcelableArrayList(KEY_STATE_FEATURES); + onFeaturesLoaded(features); + } + } + + private void loadFeatures() { + try { + new LoadFeatureTask().execute( + getPackageManager().getPackageInfo(getPackageName(), + PackageManager.GET_ACTIVITIES | PackageManager.GET_META_DATA)); + } catch (PackageManager.NameNotFoundException exception) { + Timber.e(exception, "Could not resolve package info"); + } + } + + private void onFeaturesLoaded(List<Feature> featuresList) { + features = featuresList; + if (featuresList == null || featuresList.isEmpty()) { + return; + } + + List<FeatureSectionAdapter.Section> sections = new ArrayList<>(); + String currentCat = ""; + for (int i = 0; i < features.size(); i++) { + String category = features.get(i).getCategory(); + if (!currentCat.equals(category)) { + sections.add(new FeatureSectionAdapter.Section(i, category)); + currentCat = category; + } + } + + FeatureSectionAdapter.Section[] dummy = new FeatureSectionAdapter.Section[sections.size()]; + sectionAdapter = new FeatureSectionAdapter( + this, R.layout.section_main_layout, R.id.section_text, new FeatureAdapter(features)); + sectionAdapter.setSections(sections.toArray(dummy)); + recyclerView.setAdapter(sectionAdapter); + } + + private void startFeature(Feature feature) { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(getPackageName(), feature.getName())); + startActivity(intent); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putParcelableArrayList(KEY_STATE_FEATURES, (ArrayList<Feature>) features); + } + + private class LoadFeatureTask extends AsyncTask<PackageInfo, Void, List<Feature>> { + + @Override + protected List<Feature> doInBackground(PackageInfo... params) { + List<Feature> features = new ArrayList<>(); + PackageInfo app = params[0]; + + String packageName = getApplicationContext().getPackageName(); + String metaDataKey = getString(R.string.category); + for (ActivityInfo info : app.activities) { + if (info.labelRes != 0 && info.name.startsWith(packageName) + && !info.name.equals(FeatureOverviewActivity.class.getName())) { + String label = getString(info.labelRes); + String description = resolveString(info.descriptionRes); + String category = resolveMetaData(info.metaData, metaDataKey); + features.add(new Feature(info.name, label, description, category)); + } + } + + if (!features.isEmpty()) { + Comparator<Feature> comparator = (lhs, rhs) -> { + int result = lhs.getCategory().compareToIgnoreCase(rhs.getCategory()); + if (result == 0) { + result = lhs.getLabel().compareToIgnoreCase(rhs.getLabel()); + } + return result; + }; + Collections.sort(features, comparator); + } + + return features; + } + + private String resolveMetaData(Bundle bundle, String key) { + String category = null; + if (bundle != null) { + category = bundle.getString(key); + } + return category; + } + + private String resolveString(@StringRes int stringRes) { + try { + return getString(stringRes); + } catch (Resources.NotFoundException exception) { + return "-"; + } + } + + @Override + protected void onPostExecute(List<Feature> features) { + super.onPostExecute(features); + onFeaturesLoaded(features); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/AnimatedSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/AnimatedSymbolLayerActivity.java new file mode 100644 index 0000000000..c293c0cd24 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/AnimatedSymbolLayerActivity.java @@ -0,0 +1,438 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.graphics.drawable.BitmapDrawable; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.LinearInterpolator; + +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.turf.TurfMeasurement; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate; + +/** + * Test activity showcasing animating MarkerViews. + */ +public class AnimatedSymbolLayerActivity extends AppCompatActivity { + + private static final String PASSENGER = "passenger"; + private static final String PASSENGER_LAYER = "passenger-layer"; + private static final String PASSENGER_SOURCE = "passenger-source"; + private static final String TAXI = "taxi"; + private static final String TAXI_LAYER = "taxi-layer"; + private static final String TAXI_SOURCE = "taxi-source"; + private static final String RANDOM_CAR_LAYER = "random-car-layer"; + private static final String RANDOM_CAR_SOURCE = "random-car-source"; + private static final String RANDOM_CAR_IMAGE_ID = "random-car"; + private static final String PROPERTY_BEARING = "bearing"; + private static final String WATERWAY_LAYER_ID = "waterway-label"; + private static final int DURATION_RANDOM_MAX = 1500; + private static final int DURATION_BASE = 3000; + + private final Random random = new Random(); + + private MapView mapView; + private MapboxMap mapboxMap; + + private List<Car> randomCars = new ArrayList<>(); + private GeoJsonSource randomCarSource; + private Car taxi; + private GeoJsonSource taxiSource; + private LatLng passenger; + + private List<Animator> animators = new ArrayList<>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_animated_marker); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + AnimatedSymbolLayerActivity.this.mapboxMap = mapboxMap; + setupCars(); + animateRandomRoutes(); + animateTaxi(); + }); + } + + private void setupCars() { + addRandomCars(); + addPassenger(); + addMainCar(); + } + + private void animateRandomRoutes() { + final Car longestDrive = getLongestDrive(); + final Random random = new Random(); + for (final Car car : randomCars) { + final boolean isLongestDrive = longestDrive.equals(car); + ValueAnimator valueAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), car.current, car.next); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + private LatLng latLng; + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + latLng = (LatLng) animation.getAnimatedValue(); + car.current = latLng; + if (isLongestDrive) { + updateRandomCarSource(); + } + } + }); + + if (isLongestDrive) { + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + updateRandomDestinations(); + animateRandomRoutes(); + } + }); + } + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + car.feature.properties().addProperty("bearing", Car.getBearing(car.current, car.next)); + } + }); + + int offset = random.nextInt(2) == 0 ? 0 : random.nextInt(1000) + 250; + valueAnimator.setStartDelay(offset); + valueAnimator.setDuration(car.duration - offset); + valueAnimator.setInterpolator(new LinearInterpolator()); + valueAnimator.start(); + + animators.add(valueAnimator); + } + } + + private void animateTaxi() { + ValueAnimator valueAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), taxi.current, taxi.next); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + private LatLng latLng; + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + latLng = (LatLng) animation.getAnimatedValue(); + taxi.current = latLng; + updateTaxiSource(); + } + }); + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + updatePassenger(); + animateTaxi(); + } + }); + + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + taxi.feature.properties().addProperty("bearing", Car.getBearing(taxi.current, taxi.next)); + } + }); + + valueAnimator.setDuration((long) (7 * taxi.current.distanceTo(taxi.next))); + valueAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + valueAnimator.start(); + + animators.add(valueAnimator); + } + + private void updatePassenger() { + passenger = getLatLngInBounds(); + updatePassengerSource(); + taxi.setNext(passenger); + } + + private void updatePassengerSource() { + GeoJsonSource source = mapboxMap.getSourceAs(PASSENGER_SOURCE); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry( + Point.fromLngLat( + passenger.getLongitude(), + passenger.getLatitude() + ) + ) + }); + source.setGeoJson(featureCollection); + } + + private void updateTaxiSource() { + taxi.updateFeature(); + taxiSource.setGeoJson(taxi.feature); + } + + private void updateRandomDestinations() { + for (Car randomCar : randomCars) { + randomCar.setNext(getLatLngInBounds()); + } + } + + private Car getLongestDrive() { + Car longestDrive = null; + for (Car randomCar : randomCars) { + if (longestDrive == null) { + longestDrive = randomCar; + } else if (longestDrive.duration < randomCar.duration) { + longestDrive = randomCar; + } + } + return longestDrive; + } + + private void updateRandomCarSource() { + for (Car randomCarsRoute : randomCars) { + randomCarsRoute.updateFeature(); + } + randomCarSource.setGeoJson(featuresFromRoutes()); + } + + private FeatureCollection featuresFromRoutes() { + List<Feature> features = new ArrayList<>(); + for (Car randomCarsRoute : randomCars) { + features.add(randomCarsRoute.feature); + } + return FeatureCollection.fromFeatures(features); + } + + private long getDuration() { + return random.nextInt(DURATION_RANDOM_MAX) + DURATION_BASE; + } + + private void addRandomCars() { + LatLng latLng; + LatLng next; + for (int i = 0; i < 10; i++) { + latLng = getLatLngInBounds(); + next = getLatLngInBounds(); + + JsonObject properties = new JsonObject(); + properties.addProperty(PROPERTY_BEARING, Car.getBearing(latLng, next)); + + Feature feature = Feature.fromGeometry( + Point.fromLngLat( + latLng.getLongitude(), + latLng.getLatitude() + ), properties); + + randomCars.add( + new Car(feature, next, getDuration()) + ); + } + + randomCarSource = new GeoJsonSource(RANDOM_CAR_SOURCE, featuresFromRoutes()); + mapboxMap.addSource(randomCarSource); + mapboxMap.addImage(RANDOM_CAR_IMAGE_ID, + ((BitmapDrawable) getResources().getDrawable(R.drawable.ic_car_top)).getBitmap()); + + SymbolLayer symbolLayer = new SymbolLayer(RANDOM_CAR_LAYER, RANDOM_CAR_SOURCE); + symbolLayer.withProperties( + iconImage(RANDOM_CAR_IMAGE_ID), + iconAllowOverlap(true), + iconRotate(get(PROPERTY_BEARING)), + iconIgnorePlacement(true) + ); + + mapboxMap.addLayerBelow(symbolLayer, WATERWAY_LAYER_ID); + } + + private void addPassenger() { + passenger = getLatLngInBounds(); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry( + Point.fromLngLat( + passenger.getLongitude(), + passenger.getLatitude() + ) + ) + }); + + mapboxMap.addImage(PASSENGER, + ((BitmapDrawable) getResources().getDrawable(R.drawable.icon_burned)).getBitmap()); + + GeoJsonSource geoJsonSource = new GeoJsonSource(PASSENGER_SOURCE, featureCollection); + mapboxMap.addSource(geoJsonSource); + + SymbolLayer symbolLayer = new SymbolLayer(PASSENGER_LAYER, PASSENGER_SOURCE); + symbolLayer.withProperties( + iconImage(PASSENGER), + iconIgnorePlacement(true), + iconAllowOverlap(true) + ); + mapboxMap.addLayerBelow(symbolLayer, RANDOM_CAR_LAYER); + } + + private void addMainCar() { + LatLng latLng = getLatLngInBounds(); + JsonObject properties = new JsonObject(); + properties.addProperty(PROPERTY_BEARING, Car.getBearing(latLng, passenger)); + Feature feature = Feature.fromGeometry( + Point.fromLngLat( + latLng.getLongitude(), + latLng.getLatitude()), properties); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] {feature}); + + taxi = new Car(feature, passenger, getDuration()); + mapboxMap.addImage(TAXI, + ((BitmapDrawable) getResources().getDrawable(R.drawable.ic_taxi_top)).getBitmap()); + taxiSource = new GeoJsonSource(TAXI_SOURCE, featureCollection); + mapboxMap.addSource(taxiSource); + + SymbolLayer symbolLayer = new SymbolLayer(TAXI_LAYER, TAXI_SOURCE); + symbolLayer.withProperties( + iconImage(TAXI), + iconRotate(get(PROPERTY_BEARING)), + iconAllowOverlap(true), + iconIgnorePlacement(true) + + ); + mapboxMap.addLayer(symbolLayer); + } + + private LatLng getLatLngInBounds() { + LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; + Random generator = new Random(); + double randomLat = bounds.getLatSouth() + generator.nextDouble() + * (bounds.getLatNorth() - bounds.getLatSouth()); + double randomLon = bounds.getLonWest() + generator.nextDouble() + * (bounds.getLonEast() - bounds.getLonWest()); + return new LatLng(randomLat, randomLon); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + for (Animator animator : animators) { + if (animator != null) { + animator.removeAllListeners(); + animator.cancel(); + } + } + + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + /** + * Evaluator for LatLng pairs + */ + private static class LatLngEvaluator implements TypeEvaluator<LatLng> { + + private LatLng latLng = new LatLng(); + + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + latLng.setLatitude(startValue.getLatitude() + + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); + latLng.setLongitude(startValue.getLongitude() + + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); + return latLng; + } + } + + + private static class Car { + private Feature feature; + private LatLng next; + private LatLng current; + private long duration; + + Car(Feature feature, LatLng next, long duration) { + this.feature = feature; + Point point = ((Point) feature.geometry()); + this.current = new LatLng(point.latitude(), point.longitude()); + this.duration = duration; + this.next = next; + } + + void setNext(LatLng next) { + this.next = next; + } + + void updateFeature() { + feature = Feature.fromGeometry(Point.fromLngLat( + current.getLongitude(), + current.getLatitude()) + ); + feature.properties().addProperty("bearing", getBearing(current, next)); + } + + private static float getBearing(LatLng from, LatLng to) { + return (float) TurfMeasurement.bearing( + Point.fromLngLat(from.getLongitude(), from.getLatitude()), + Point.fromLngLat(to.getLongitude(), to.getLatitude()) + ); + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/BulkMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/BulkMarkerActivity.java new file mode 100644 index 0000000000..1623ddf9d3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/BulkMarkerActivity.java @@ -0,0 +1,285 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.app.ProgressDialog; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; +import android.widget.TextView; +import android.widget.Toast; +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.GeoParseUtil; +import com.mapbox.mapboxsdk.maps.utils.IconUtils; +import timber.log.Timber; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Random; + +/** + * Test activity showcasing adding a large amount of Markers or MarkerViews. + */ +public class BulkMarkerActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { + + private MapboxMap mapboxMap; + private MapView mapView; + private boolean customMarkerView; + private List<LatLng> locations; + private ProgressDialog progressDialog; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_marker_bulk); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> BulkMarkerActivity.this.mapboxMap = mapboxMap); + + final View fab = findViewById(R.id.fab); + if (fab != null) { + fab.setOnClickListener(new FabClickListener()); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + ArrayAdapter<CharSequence> spinnerAdapter = ArrayAdapter.createFromResource( + this, R.array.bulk_marker_list, android.R.layout.simple_spinner_item); + spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + getMenuInflater().inflate(R.menu.menu_bulk_marker, menu); + MenuItem item = menu.findItem(R.id.spinner); + Spinner spinner = (Spinner) MenuItemCompat.getActionView(item); + spinner.setAdapter(spinnerAdapter); + spinner.setOnItemSelectedListener(BulkMarkerActivity.this); + return true; + } + + @Override + public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { + int amount = Integer.valueOf(getResources().getStringArray(R.array.bulk_marker_list)[position]); + if (locations == null) { + progressDialog = ProgressDialog.show(this, "Loading", "Fetching markers", false); + new LoadLocationTask(this, amount).execute(); + } else { + showMarkers(amount); + } + } + + private void onLatLngListLoaded(List<LatLng> latLngs, int amount) { + progressDialog.hide(); + locations = latLngs; + showMarkers(amount); + } + + private void showMarkers(int amount) { + if (mapboxMap == null || locations == null) { + return; + } + + mapboxMap.clear(); + + if (locations.size() < amount) { + amount = locations.size(); + } + + if (customMarkerView) { + showViewMarkers(amount); + } else { + showGlMarkers(amount); + } + } + + private void showViewMarkers(int amount) { + DecimalFormat formatter = new DecimalFormat("#.#####"); + Random random = new Random(); + int randomIndex; + + int color = ResourcesCompat.getColor(getResources(), R.color.redAccent, getTheme()); + Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_droppin, color); + + List<MarkerViewOptions> markerOptionsList = new ArrayList<>(); + for (int i = 0; i < amount; i++) { + randomIndex = random.nextInt(locations.size()); + LatLng latLng = locations.get(randomIndex); + MarkerViewOptions markerOptions = new MarkerViewOptions() + .position(latLng) + .icon(icon) + .title(String.valueOf(i)) + .snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude())); + markerOptionsList.add(markerOptions); + } + mapboxMap.addMarkerViews(markerOptionsList); + } + + private void showGlMarkers(int amount) { + List<MarkerOptions> markerOptionsList = new ArrayList<>(); + DecimalFormat formatter = new DecimalFormat("#.#####"); + Random random = new Random(); + int randomIndex; + + for (int i = 0; i < amount; i++) { + randomIndex = random.nextInt(locations.size()); + LatLng latLng = locations.get(randomIndex); + markerOptionsList.add(new MarkerOptions() + .position(latLng) + .title(String.valueOf(i)) + .snippet(formatter.format(latLng.getLatitude()) + ", " + formatter.format(latLng.getLongitude()))); + } + + mapboxMap.addMarkers(markerOptionsList); + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // nothing selected, nothing to do! + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + + private class FabClickListener implements View.OnClickListener { + + private TextView viewCountView; + + @Override + public void onClick(final View view) { + if (mapboxMap != null) { + customMarkerView = true; + + // remove fab + view.animate().alpha(0).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + view.setVisibility(View.GONE); + } + }).start(); + + // reload markers + Spinner spinner = (Spinner) findViewById(R.id.spinner); + if (spinner != null) { + int amount = Integer.valueOf( + getResources().getStringArray(R.array.bulk_marker_list)[spinner.getSelectedItemPosition()]); + showMarkers(amount); + } + + viewCountView = (TextView) findViewById(R.id.countView); + + mapView.addOnMapChangedListener(change -> { + if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) { + if (!mapboxMap.getMarkerViewManager().getMarkerViewAdapters().isEmpty()) { + viewCountView.setText(String.format(Locale.getDefault(), "ViewCache size %d", + mapboxMap.getMarkerViewManager().getMarkerViewContainer().getChildCount())); + } + } + }); + + mapboxMap.getMarkerViewManager().setOnMarkerViewClickListener( + (marker, view1, adapter) -> { + Toast.makeText( + BulkMarkerActivity.this, + "Hello " + marker.getId(), + Toast.LENGTH_SHORT).show(); + return false; + }); + } + } + } + + private static class LoadLocationTask extends AsyncTask<Void, Integer, List<LatLng>> { + + private WeakReference<BulkMarkerActivity> activity; + private int amount; + + private LoadLocationTask(BulkMarkerActivity activity, int amount) { + this.amount = amount; + this.activity = new WeakReference<>(activity); + } + + @Override + protected List<LatLng> doInBackground(Void... params) { + BulkMarkerActivity activity = this.activity.get(); + if (activity != null) { + String json = null; + try { + json = GeoParseUtil.loadStringFromAssets(activity.getApplicationContext(), "points.geojson"); + } catch (IOException exception) { + Timber.e(exception, "Could not add markers"); + } + + if (json != null) { + return GeoParseUtil.parseGeoJsonCoordinates(json); + } + } + return null; + } + + @Override + protected void onPostExecute(List<LatLng> locations) { + super.onPostExecute(locations); + BulkMarkerActivity activity = this.activity.get(); + if (activity != null) { + activity.onLatLngListLoaded(locations, amount); + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/DynamicMarkerChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/DynamicMarkerChangeActivity.java new file mode 100644 index 0000000000..bb9531c0d3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/DynamicMarkerChangeActivity.java @@ -0,0 +1,117 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.IconUtils; + +/** + * Test activity showcasing updating a Marker position, title, icon and snippet. + */ +public class DynamicMarkerChangeActivity extends AppCompatActivity { + + private static final LatLng LAT_LNG_CHELSEA = new LatLng(51.481670, -0.190849); + private static final LatLng LAT_LNG_ARSENAL = new LatLng(51.555062, -0.108417); + + private MapView mapView; + private MapboxMap mapboxMap; + private Marker marker; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_dynamic_marker); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.setTag(false); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + DynamicMarkerChangeActivity.this.mapboxMap = mapboxMap; + // Create marker + MarkerOptions markerOptions = new MarkerOptions() + .position(LAT_LNG_CHELSEA) + .icon(IconUtils.drawableToIcon(DynamicMarkerChangeActivity.this, R.drawable.ic_stars, + ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme()))) + .title(getString(R.string.dynamic_marker_chelsea_title)) + .snippet(getString(R.string.dynamic_marker_chelsea_snippet)); + marker = mapboxMap.addMarker(markerOptions); + }); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setColorFilter(ContextCompat.getColor(this, R.color.primary)); + fab.setOnClickListener(view -> { + if (mapboxMap != null) { + updateMarker(); + } + }); + } + + private void updateMarker() { + // update model + boolean first = (boolean) mapView.getTag(); + mapView.setTag(!first); + + // update marker + marker.setPosition(first ? LAT_LNG_CHELSEA : LAT_LNG_ARSENAL); + marker.setIcon(IconUtils.drawableToIcon(this, R.drawable.ic_stars, first + ? ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme()) : + ResourcesCompat.getColor(getResources(), R.color.redAccent, getTheme()) + )); + + marker.setTitle(first + ? getString(R.string.dynamic_marker_chelsea_title) : getString(R.string.dynamic_marker_arsenal_title)); + marker.setSnippet(first + ? getString(R.string.dynamic_marker_chelsea_snippet) : getString(R.string.dynamic_marker_arsenal_snippet)); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewActivity.java new file mode 100644 index 0000000000..910538a803 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewActivity.java @@ -0,0 +1,483 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.animation.Animator; +import android.animation.AnimatorInflater; +import android.animation.AnimatorListenerAdapter; +import android.animation.FloatEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +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.MarkerViewManager; +import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerView; +import com.mapbox.mapboxsdk.testapp.model.annotations.CountryMarkerViewOptions; +import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerView; +import com.mapbox.mapboxsdk.testapp.model.annotations.TextMarkerViewOptions; + +import java.util.Locale; +import java.util.Random; + +/** + * Test activity showcasing multiple MarkerViews above Washington D.C. + * <p> + * Shows a couple of open InfoWindows out of current Viewport. + * Updates the rotation and location of a couple of MarkerViews. + * </p> + */ +public class MarkerViewActivity extends AppCompatActivity { + + private static final LatLng[] LAT_LNGS = new LatLng[] { + new LatLng(38.897424, -77.036508), + new LatLng(38.909698, -77.029642), + new LatLng(38.907227, -77.036530), + new LatLng(38.905607, -77.031916), + new LatLng(38.889441, -77.050134), + new LatLng(38.888000, -77.050000) // Slight overlap to show re-ordering on selection + }; + + private MapboxMap mapboxMap; + private MapView mapView; + + // MarkerView location updates + private MarkerView movingMarkerOne; + private MarkerView movingMarkerTwo; + private Random randomAnimator = new Random(); + private Handler locationUpdateHandler = new Handler(); + private Runnable moveMarkerRunnable = new MoveMarkerRunnable(); + + // MarkerView rotate updates + private MarkerView rotateMarker; + private Handler rotateUpdateHandler = new Handler(); + private Runnable rotateMarkerRunnable = new RotateMarkerRunnable(); + private int rotation = 360; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_marker_view); + + final TextView viewCountView = (TextView) findViewById(R.id.countView); + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + MarkerViewActivity.this.mapboxMap = mapboxMap; + + final MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager(); + + Icon usFlag = IconFactory.getInstance(MarkerViewActivity.this) + .fromResource(R.drawable.ic_us); + + // add default ViewMarker markers + for (int i = 0; i < LAT_LNGS.length; i++) { + MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions() + .position(LAT_LNGS[i]) + .title(String.valueOf(i)) + .alpha(0.5f) + .icon(usFlag) + ); + } + + // add custom ViewMarker + CountryMarkerViewOptions options = new CountryMarkerViewOptions(); + options.flagRes(R.drawable.icon_burned); + options.abbrevName("Mapbox"); + options.title("Hello"); + options.position(new LatLng(38.899774, -77.023237)); + options.flat(true); + MarkerView markerView = mapboxMap.addMarker(options); + + // Use object animator to rotate MarkerView + ValueAnimator markerAnimator = ObjectAnimator.ofObject(markerView, "rotation", new FloatEvaluator(), -90, 90); + markerAnimator.setDuration(5000); + markerAnimator.start(); + + MarkerViewActivity.this.mapboxMap.addMarker(new MarkerOptions() + .title("United States") + .position(new LatLng(38.902580, -77.050102)) + ); + + rotateMarker = MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions() + .text("A") + .rotation(rotation = 270) + .position(new LatLng(38.889876, -77.008849)) + ); + loopMarkerRotate(); + + + MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions() + .text("B") + .position(new LatLng(38.907327, -77.041293)) + ); + + MarkerViewActivity.this.mapboxMap.addMarker(new TextMarkerViewOptions() + .text("C") + .position(new LatLng(38.897642, -77.041980)) + ); + + // if you want to customise a ViewMarker you need to extend ViewMarker and provide an adapter implementation + // set adapters for child classes of ViewMarker + markerViewManager.addMarkerViewAdapter(new CountryAdapter(MarkerViewActivity.this, mapboxMap)); + markerViewManager.addMarkerViewAdapter(new TextAdapter(MarkerViewActivity.this, mapboxMap)); + + final ViewGroup markerViewContainer = markerViewManager.getMarkerViewContainer(); + + // add a change listener to validate the size of amount of child views + mapView.addOnMapChangedListener(change -> { + if (change == MapView.REGION_IS_CHANGING || change == MapView.REGION_DID_CHANGE) { + if (!markerViewManager.getMarkerViewAdapters().isEmpty() && viewCountView != null) { + viewCountView.setText(String.format( + Locale.getDefault(), + getString(R.string.viewcache_size), + markerViewContainer.getChildCount()) + ); + } + } + }); + + // add a OnMarkerView click listener + MarkerViewActivity.this.mapboxMap.getMarkerViewManager().setOnMarkerViewClickListener( + (marker, view, adapter) -> { + Toast.makeText(MarkerViewActivity.this, "Hello " + marker.getId(), Toast.LENGTH_SHORT).show(); + return false; + }); + + movingMarkerOne = MarkerViewActivity.this.mapboxMap.addMarker(new MarkerViewOptions() + .position(CarLocation.CAR_0_LNGS[0]) + .icon(IconFactory.getInstance(mapView.getContext()) + .fromResource(R.drawable.ic_android)) + ); + + movingMarkerTwo = mapboxMap.addMarker(new MarkerViewOptions() + .position(CarLocation.CAR_1_LNGS[0]) + .icon(IconFactory.getInstance(mapView.getContext()) + .fromResource(R.drawable.ic_android_2)) + ); + + // allow more open infowindows at the same time + mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(true); + + // add offscreen markers + Marker markerRightOffScreen = mapboxMap.addMarker(new MarkerOptions() + .setPosition(new LatLng(38.892846, -76.909399)) + .title("InfoWindow") + .snippet("Offscreen, to the right of the Map.")); + + Marker markerRightBottomOffScreen = mapboxMap.addMarker(new MarkerOptions() + .setPosition(new LatLng(38.791645, -77.039006)) + .title("InfoWindow") + .snippet("Offscreen, to the bottom of the Map")); + + // open infowindow offscreen markers + mapboxMap.selectMarker(markerRightOffScreen); + mapboxMap.selectMarker(markerRightBottomOffScreen); + }); + } + + private void loopMarkerRotate() { + rotateUpdateHandler.postDelayed(rotateMarkerRunnable, 800); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + loopMarkerMove(); + } + + private void loopMarkerMove() { + locationUpdateHandler.postDelayed(moveMarkerRunnable, randomAnimator.nextInt(3000) + 1000); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + locationUpdateHandler.removeCallbacks(moveMarkerRunnable); + rotateUpdateHandler.removeCallbacks(rotateMarkerRunnable); + } + + /** + * Updates the position of a Marker + */ + private class MoveMarkerRunnable implements Runnable { + @Override + public void run() { + int randomInteger = randomAnimator.nextInt(9); + if (randomAnimator.nextInt() % 2 == 0) { + movingMarkerOne.setPosition(CarLocation.CAR_0_LNGS[randomInteger]); + } else { + movingMarkerTwo.setPosition(CarLocation.CAR_1_LNGS[randomInteger]); + } + loopMarkerMove(); + } + } + + /** + * Updates the rotation of a Marker + */ + private class RotateMarkerRunnable implements Runnable { + + private static final int ROTATION_INCREASE_VALUE = 9; + + @Override + public void run() { + rotation -= ROTATION_INCREASE_VALUE; + if (rotation >= 0) { + rotation += 360; + } + rotateMarker.setRotation(rotation); + loopMarkerRotate(); + } + } + + /** + * Adapts a MarkerView to display an abbreviated name in a TextView and a flag in an ImageView. + */ + private static class CountryAdapter extends MapboxMap.MarkerViewAdapter<CountryMarkerView> { + + private LayoutInflater inflater; + private MapboxMap mapboxMap; + + CountryAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) { + super(context, CountryMarkerView.class); + this.inflater = LayoutInflater.from(context); + this.mapboxMap = mapboxMap; + } + + @Nullable + @Override + public View getView(@NonNull CountryMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + convertView = inflater.inflate(R.layout.view_custom_marker, parent, false); + viewHolder.flag = (ImageView) convertView.findViewById(R.id.imageView); + viewHolder.abbrev = (TextView) convertView.findViewById(R.id.textView); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + viewHolder.flag.setImageResource(marker.getFlagRes()); + viewHolder.abbrev.setText(marker.getAbbrevName()); + return convertView; + } + + @Override + public boolean onSelect( + @NonNull final CountryMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 0, 360); + rotateAnimator.setDuration(reselectionForViewReuse ? 0 : 350); + rotateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + mapboxMap.selectMarker(marker); + } + }); + rotateAnimator.start(); + + // false indicates that we are calling selectMarker after our animation ourselves + // true will let the system call it for you, which will result in showing an InfoWindow instantly + return false; + } + + @Override + public void onDeselect(@NonNull CountryMarkerView marker, @NonNull final View convertView) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(convertView, View.ROTATION, 360, 0); + rotateAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + rotateAnimator.start(); + } + + private static class ViewHolder { + ImageView flag; + TextView abbrev; + } + } + + /** + * Adapts a MarkerView to display text in a TextView. + */ + public static class TextAdapter extends MapboxMap.MarkerViewAdapter<TextMarkerView> { + + private LayoutInflater inflater; + private MapboxMap mapboxMap; + + public TextAdapter(@NonNull Context context, @NonNull MapboxMap mapboxMap) { + super(context, TextMarkerView.class); + this.inflater = LayoutInflater.from(context); + this.mapboxMap = mapboxMap; + } + + @Nullable + @Override + public View getView(@NonNull TextMarkerView marker, @Nullable View convertView, @NonNull ViewGroup parent) { + ViewHolder viewHolder; + if (convertView == null) { + viewHolder = new ViewHolder(); + convertView = inflater.inflate(R.layout.view_text_marker, parent, false); + viewHolder.textView = (TextView) convertView.findViewById(R.id.textView); + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + viewHolder.textView.setText(marker.getText()); + return convertView; + } + + @Override + public boolean onSelect( + @NonNull final TextMarkerView marker, @NonNull final View convertView, boolean reselectionForViewReuse) { + animateGrow(marker, convertView, 0); + + // false indicates that we are calling selectMarker after our animation ourselves + // true will let the system call it for you, which will result in showing an InfoWindow instantly + return false; + } + + @Override + public void onDeselect(@NonNull TextMarkerView marker, @NonNull final View convertView) { + animateShrink(convertView, 350); + } + + @Override + public boolean prepareViewForReuse(@NonNull MarkerView marker, @NonNull View convertView) { + // this method is called before a view will be reused, we need to restore view state + // as we have scaled the view in onSelect. If not correctly applied other MarkerView will + // become large since these have been recycled + + // cancel ongoing animation + convertView.animate().cancel(); + + if (marker.isSelected()) { + // shrink view to be able to be reused + animateShrink(convertView, 0); + } + + // true if you want reuse to occur automatically, false if you want to manage this yourself + return true; + } + + private void animateGrow(@NonNull final MarkerView marker, @NonNull final View convertView, int duration) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_up); + animator.setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + mapboxMap.selectMarker(marker); + } + }); + animator.setTarget(convertView); + animator.start(); + } + + private void animateShrink(@NonNull final View convertView, int duration) { + convertView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + Animator animator = AnimatorInflater.loadAnimator(convertView.getContext(), R.animator.scale_down); + animator.setDuration(duration); + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + convertView.setLayerType(View.LAYER_TYPE_NONE, null); + } + }); + animator.setTarget(convertView); + animator.start(); + } + + private static class ViewHolder { + TextView textView; + } + } + + + @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(); + } + + private static class CarLocation { + + static LatLng[] CAR_0_LNGS = new LatLng[] { + new LatLng(38.92334425495122, -77.0533673443786), + new LatLng(38.9234737236897, -77.05389484528261), + new LatLng(38.9257094658146, -76.98819752280579), + new LatLng(38.8324328369647, -77.00690648325929), + new LatLng(38.87540698725855, -77.0093148713099), + new LatLng(38.96499498141065, -77.07707916040054), + new LatLng(38.90794910679896, -76.99695304153806), + new LatLng(38.86234025281626, -76.9950528034839), + new LatLng(38.862930274733635, -76.99647808241964) + }; + + static LatLng[] CAR_1_LNGS = new LatLng[] { + new LatLng(38.94237975070426, -76.98324549005675), + new LatLng(38.941520236084486, -76.98234257804742), + new LatLng(38.85972219720714, -76.98955808483929), + new LatLng(38.944289166113776, -76.98584257252891), + new LatLng(38.94375860578053, -76.98470344318412), + new LatLng(38.943167431929645, -76.98373163938666), + new LatLng(38.882834728904605, -77.02862535635137), + new LatLng(38.882869724926245, -77.02992539231113), + new LatLng(38.9371988177896, -76.97786740676564) + }; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewsInRectangleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewsInRectangleActivity.java new file mode 100644 index 0000000000..4ac2a25520 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/MarkerViewsInRectangleActivity.java @@ -0,0 +1,110 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.graphics.RectF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; + +import java.util.List; + +import timber.log.Timber; + +/** + * Test activity showcasing counting MarkerViews in a rectangle. + */ +public class MarkerViewsInRectangleActivity extends AppCompatActivity implements OnMapReadyCallback, + View.OnClickListener { + + public MapView mapView; + private MapboxMap mapboxMap; + private View selectionBox; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_marker_view_in_rect); + + selectionBox = findViewById(R.id.selection_box); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + MarkerViewsInRectangleActivity.this.mapboxMap = mapboxMap; + selectionBox.setOnClickListener(this); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 16)); + mapboxMap.addMarker(new MarkerViewOptions().position(new LatLng(52.0907, 5.1214))); + } + + @Override + public void onClick(View view) { + // Query + int top = selectionBox.getTop() - mapView.getTop(); + int left = selectionBox.getLeft() - mapView.getLeft(); + RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); + Timber.i("Querying box %s", box); + List<MarkerView> markers = mapboxMap.getMarkerViewsInRect(box); + + // Show count + Toast.makeText( + MarkerViewsInRectangleActivity.this, + String.format("%s markers inside box", markers.size()), + Toast.LENGTH_SHORT).show(); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolygonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolygonActivity.java new file mode 100644 index 0000000000..fe830e819a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolygonActivity.java @@ -0,0 +1,218 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.annotations.Polygon; +import com.mapbox.mapboxsdk.annotations.PolygonOptions; +import com.mapbox.mapboxsdk.camera.CameraPosition; +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.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.BLUE_COLOR; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.BROKEN_SHAPE_POINTS; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.FULL_ALPHA; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.NO_ALPHA; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.PARTIAL_ALPHA; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.RED_COLOR; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.STAR_SHAPE_HOLES; +import static com.mapbox.mapboxsdk.maps.activity.annotation.PolygonActivity.Config.STAR_SHAPE_POINTS; + +/** + * Test activity to showcase the Polygon annotation API & programmatically creating a MapView. + * <p> + * Shows how to change Polygon features as visibility, alpha, color and points. + * </p> + */ +public class PolygonActivity extends AppCompatActivity implements OnMapReadyCallback { + + private MapView mapView; + private MapboxMap mapboxMap; + + private Polygon polygon; + private boolean fullAlpha = true; + private boolean visible = true; + private boolean color = true; + private boolean allPoints = true; + private boolean holes = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // configure inital map state + MapboxMapOptions options = new MapboxMapOptions() + .attributionTintColor(RED_COLOR) + .compassFadesWhenFacingNorth(false) + .styleUrl(Style.MAPBOX_STREETS) + .camera(new CameraPosition.Builder() + .target(new LatLng(45.520486, -122.673541)) + .zoom(12) + .tilt(40) + .build()); + + // create map + mapView = new MapView(this, options); + mapView.setId(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + + setContentView(mapView); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + + map.setOnPolygonClickListener(polygon -> Toast.makeText( + PolygonActivity.this, + "You clicked on polygon with id = " + polygon.getId(), + Toast.LENGTH_SHORT + ).show()); + + polygon = mapboxMap.addPolygon(new PolygonOptions() + .addAll(STAR_SHAPE_POINTS) + .fillColor(BLUE_COLOR)); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 R.id.action_id_alpha: + fullAlpha = !fullAlpha; + polygon.setAlpha(fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA); + return true; + case R.id.action_id_visible: + visible = !visible; + polygon.setAlpha(visible ? (fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA) : NO_ALPHA); + return true; + case R.id.action_id_points: + allPoints = !allPoints; + polygon.setPoints(allPoints ? STAR_SHAPE_POINTS : BROKEN_SHAPE_POINTS); + return true; + case R.id.action_id_color: + color = !color; + polygon.setFillColor(color ? BLUE_COLOR : RED_COLOR); + return true; + case R.id.action_id_holes: + holes = !holes; + polygon.setHoles(holes ? STAR_SHAPE_HOLES : Collections.<List<LatLng>>emptyList()); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_polygon, menu); + return true; + } + + static final class Config { + static final int BLUE_COLOR = Color.parseColor("#3bb2d0"); + static final int RED_COLOR = Color.parseColor("#AF0000"); + + static final float FULL_ALPHA = 1.0f; + static final float PARTIAL_ALPHA = 0.5f; + static final float NO_ALPHA = 0.0f; + + static final List<LatLng> STAR_SHAPE_POINTS = new ArrayList<LatLng>() { + { + add(new LatLng(45.522585, -122.685699)); + add(new LatLng(45.534611, -122.708873)); + add(new LatLng(45.530883, -122.678833)); + add(new LatLng(45.547115, -122.667503)); + add(new LatLng(45.530643, -122.660121)); + add(new LatLng(45.533529, -122.636260)); + add(new LatLng(45.521743, -122.659091)); + add(new LatLng(45.510677, -122.648792)); + add(new LatLng(45.515008, -122.664070)); + add(new LatLng(45.502496, -122.669048)); + add(new LatLng(45.515369, -122.678489)); + add(new LatLng(45.506346, -122.702007)); + add(new LatLng(45.522585, -122.685699)); + } + }; + + static final List<LatLng> BROKEN_SHAPE_POINTS = + STAR_SHAPE_POINTS.subList(0, STAR_SHAPE_POINTS.size() - 3); + + static final List<? extends List<LatLng>> STAR_SHAPE_HOLES = new ArrayList<List<LatLng>>() { + { + add(new ArrayList<>(new ArrayList<LatLng>() { + { + add(new LatLng(45.521743, -122.669091)); + add(new LatLng(45.530483, -122.676833)); + add(new LatLng(45.520483, -122.676833)); + add(new LatLng(45.521743, -122.669091)); + } + })); + add(new ArrayList<>(new ArrayList<LatLng>() { + { + add(new LatLng(45.529743, -122.662791)); + add(new LatLng(45.525543, -122.662791)); + add(new LatLng(45.525543, -122.660)); + add(new LatLng(45.527743, -122.660)); + add(new LatLng(45.529743, -122.662791)); + } + })); + } + }; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolylineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolylineActivity.java new file mode 100644 index 0000000000..30adf3604d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PolylineActivity.java @@ -0,0 +1,223 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.annotations.Polyline; +import com.mapbox.mapboxsdk.annotations.PolylineOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Test activity showcasing the Polyline annotations API. + * <p> + * Shows how to add and remove polylines. + * </p> + */ +public class PolylineActivity extends AppCompatActivity { + + private static final String STATE_POLYLINE_OPTIONS = "polylineOptions"; + + private static final LatLng ANDORRA = new LatLng(42.505777, 1.52529); + private static final LatLng LUXEMBOURG = new LatLng(49.815273, 6.129583); + private static final LatLng MONACO = new LatLng(43.738418, 7.424616); + private static final LatLng VATICAN_CITY = new LatLng(41.902916, 12.453389); + private static final LatLng SAN_MARINO = new LatLng(43.942360, 12.457777); + private static final LatLng LIECHTENSTEIN = new LatLng(47.166000, 9.555373); + + private static final float FULL_ALPHA = 1.0f; + private static final float PARTIAL_ALPHA = 0.5f; + private static final float NO_ALPHA = 0.0f; + + private List<Polyline> polylines; + private ArrayList<PolylineOptions> polylineOptions = new ArrayList<>(); + private MapView mapView; + private MapboxMap mapboxMap; + + private boolean fullAlpha = true; + private boolean visible = true; + private boolean width = true; + private boolean color = true; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_polyline); + + if (savedInstanceState != null) { + polylineOptions = savedInstanceState.getParcelableArrayList(STATE_POLYLINE_OPTIONS); + } else { + polylineOptions.addAll(getAllPolylines()); + } + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + PolylineActivity.this.mapboxMap = mapboxMap; + + mapboxMap.setOnPolylineClickListener(polyline -> Toast.makeText( + PolylineActivity.this, + "You clicked on polygon with id = " + polyline.getId(), + Toast.LENGTH_SHORT + ).show()); + + polylines = mapboxMap.addPolylines(polylineOptions); + }); + + View fab = findViewById(R.id.fab); + if (fab != null) { + fab.setOnClickListener(view -> { + if (mapboxMap != null) { + if (polylines != null && polylines.size() > 0) { + if (polylines.size() == 1) { + // test for removing annotation + mapboxMap.removeAnnotation(polylines.get(0)); + } else { + // test for removing annotations + mapboxMap.removeAnnotations(polylines); + } + } + polylineOptions.clear(); + polylineOptions.addAll(getRandomLine()); + polylines = mapboxMap.addPolylines(polylineOptions); + + } + }); + } + } + + private List<PolylineOptions> getAllPolylines() { + List<PolylineOptions> options = new ArrayList<>(); + options.add(generatePolyline(ANDORRA, LUXEMBOURG, "#F44336")); + options.add(generatePolyline(ANDORRA, MONACO, "#FF5722")); + options.add(generatePolyline(MONACO, VATICAN_CITY, "#673AB7")); + options.add(generatePolyline(VATICAN_CITY, SAN_MARINO, "#009688")); + options.add(generatePolyline(SAN_MARINO, LIECHTENSTEIN, "#795548")); + options.add(generatePolyline(LIECHTENSTEIN, LUXEMBOURG, "#3F51B5")); + return options; + } + + private PolylineOptions generatePolyline(LatLng start, LatLng end, String color) { + PolylineOptions line = new PolylineOptions(); + line.add(start); + line.add(end); + line.color(Color.parseColor(color)); + return line; + } + + public List<PolylineOptions> getRandomLine() { + final List<PolylineOptions> randomLines = getAllPolylines(); + Collections.shuffle(randomLines); + return new ArrayList<PolylineOptions>() { + { + add(randomLines.get(0)); + } + }; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + outState.putParcelableArrayList(STATE_POLYLINE_OPTIONS, polylineOptions); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_polyline, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (polylines.size() <= 0) { + Toast.makeText(PolylineActivity.this, "No polylines on map", Toast.LENGTH_LONG).show(); + return super.onOptionsItemSelected(item); + } + switch (item.getItemId()) { + case R.id.action_id_remove: + // test to remove all annotations + polylineOptions.clear(); + mapboxMap.clear(); + polylines.clear(); + return true; + + case R.id.action_id_alpha: + fullAlpha = !fullAlpha; + for (Polyline p : polylines) { + p.setAlpha(fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA); + } + return true; + + case R.id.action_id_color: + color = !color; + for (Polyline p : polylines) { + p.setColor(color ? Color.RED : Color.BLUE); + } + return true; + + case R.id.action_id_width: + width = !width; + for (Polyline p : polylines) { + p.setWidth(width ? 3.0f : 5.0f); + } + return true; + + case R.id.action_id_visible: + visible = !visible; + for (Polyline p : polylines) { + p.setAlpha(visible ? (fullAlpha ? FULL_ALPHA : PARTIAL_ALPHA) : NO_ALPHA); + } + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PressForMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PressForMarkerActivity.java new file mode 100644 index 0000000000..cb5ee9e65b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/annotation/PressForMarkerActivity.java @@ -0,0 +1,140 @@ +package com.mapbox.mapboxsdk.maps.activity.annotation; + +import android.graphics.PointF; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +import java.text.DecimalFormat; +import java.util.ArrayList; + +/** + * Test activity showcasing to add a Marker on click. + * <p> + * Shows how to use a OnMapClickListener and a OnMapLongClickListener + * </p> + */ +public class PressForMarkerActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + private ArrayList<MarkerOptions> markerList = new ArrayList<>(); + + private static final DecimalFormat LAT_LON_FORMATTER = new DecimalFormat("#.#####"); + + private static String STATE_MARKER_LIST = "markerList"; + + @Override + protected void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_press_for_marker); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + resetMap(); + + mapboxMap.setOnMapLongClickListener(point -> addMarker(point)); + + mapboxMap.setOnMapClickListener(point -> addMarker(point)); + + if (savedInstanceState != null) { + markerList = savedInstanceState.getParcelableArrayList(STATE_MARKER_LIST); + mapboxMap.addMarkers(markerList); + } + }); + } + + private void addMarker(LatLng point) { + final PointF pixel = mapboxMap.getProjection().toScreenLocation(point); + + String title = LAT_LON_FORMATTER.format(point.getLatitude()) + ", " + + LAT_LON_FORMATTER.format(point.getLongitude()); + String snippet = "X = " + (int) pixel.x + ", Y = " + (int) pixel.y; + + MarkerOptions marker = new MarkerOptions() + .position(point) + .title(title) + .snippet(snippet); + + markerList.add(marker); + mapboxMap.addMarker(marker); + } + + private void resetMap() { + if (mapboxMap == null) { + return; + } + mapboxMap.removeAnnotations(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_press_for_marker, menu); + return true; + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + mapView.onSaveInstanceState(outState); + outState.putParcelableArrayList(STATE_MARKER_LIST, markerList); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 R.id.menuItemReset: + resetMap(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimationTypeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimationTypeActivity.java new file mode 100644 index 0000000000..e462315021 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimationTypeActivity.java @@ -0,0 +1,182 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; + +import timber.log.Timber; + +/** + * Test activity showcasing the Camera API and listen to camera updates by animating the camera above London. + * <p> + * Shows how to use animate, ease and move camera update factory methods. + * </p> + */ +public class CameraAnimationTypeActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final LatLng LAT_LNG_LONDON_EYE = new LatLng(51.50325, -0.11968); + private static final LatLng LAT_LNG_TOWER_BRIDGE = new LatLng(51.50550, -0.07520); + + private MapboxMap mapboxMap; + private MapView mapView; + private boolean cameraState; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera_animation_types); + + mapView = (MapView) findViewById(R.id.mapView); + if (mapView != null) { + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.getUiSettings().setAttributionEnabled(false); + mapboxMap.getUiSettings().setLogoEnabled(false); + mapboxMap.setOnCameraChangeListener(position -> Timber.w(position.toString())); + + // handle move button clicks + View moveButton = findViewById(R.id.cameraMoveButton); + if (moveButton != null) { + moveButton.setOnClickListener(view -> { + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(getNextLatLng()) + .zoom(14) + .tilt(30) + .tilt(0) + .build(); + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + }); + } + + // handle ease button clicks + View easeButton = findViewById(R.id.cameraEaseButton); + if (easeButton != null) { + easeButton.setOnClickListener(view -> { + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(getNextLatLng()) + .zoom(15) + .bearing(180) + .tilt(30) + .build(); + + MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + Timber.i("Duration onCancel Callback called."); + Toast.makeText( + CameraAnimationTypeActivity.this, + "Ease onCancel Callback called.", + Toast.LENGTH_LONG).show(); + } + + @Override + public void onFinish() { + Timber.i("Duration onFinish Callback called."); + Toast.makeText( + CameraAnimationTypeActivity.this, + "Ease onFinish Callback called.", + Toast.LENGTH_LONG).show(); + } + }; + + mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback); + }); + } + + // handle animate button clicks + View animateButton = findViewById(R.id.cameraAnimateButton); + if (animateButton != null) { + animateButton.setOnClickListener(view -> { + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(getNextLatLng()) + .bearing(270) + .tilt(20) + .build(); + + MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + Timber.i("Duration onCancel Callback called."); + Toast.makeText( + CameraAnimationTypeActivity.this, + "Duration onCancel Callback called.", + Toast.LENGTH_LONG).show(); + } + + @Override + public void onFinish() { + Timber.i("Duration onFinish Callback called."); + Toast.makeText( + CameraAnimationTypeActivity.this, + "Duration onFinish Callback called.", + Toast.LENGTH_LONG).show(); + } + }; + + mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 7500, callback); + }); + } + } + + private LatLng getNextLatLng() { + cameraState = !cameraState; + return cameraState ? LAT_LNG_TOWER_BRIDGE : LAT_LNG_LONDON_EYE; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimatorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimatorActivity.java new file mode 100644 index 0000000000..d6a6593e72 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraAnimatorActivity.java @@ -0,0 +1,278 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.TypeEvaluator; +import android.animation.ValueAnimator; +import android.os.Bundle; +import android.support.v4.util.LongSparseArray; +import android.support.v4.view.animation.FastOutLinearInInterpolator; +import android.support.v4.view.animation.FastOutSlowInInterpolator; +import android.support.v4.view.animation.PathInterpolatorCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.animation.AnticipateOvershootInterpolator; +import android.view.animation.BounceInterpolator; +import android.view.animation.Interpolator; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using Android SDK animators to animate camera position changes. + */ +public class CameraAnimatorActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final double ANIMATION_DELAY_FACTOR = 1.5; + private static final LatLng START_LAT_LNG = new LatLng(37.787947, -122.407432); + + private final LongSparseArray<AnimatorBuilder> animators = new LongSparseArray<AnimatorBuilder>() { + { + put(R.id.menu_action_accelerate_decelerate_interpolator, () -> { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + createLatLngAnimator(START_LAT_LNG, new LatLng(37.826715, -122.422795)), + obtainExampleInterpolator(new FastOutSlowInInterpolator(), 2500) + ); + return animatorSet; + }); + + put(R.id.menu_action_bounce_interpolator, () -> { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + createLatLngAnimator(START_LAT_LNG, new LatLng(37.787947, -122.407432)), + obtainExampleInterpolator(new BounceInterpolator(), 3750) + ); + return animatorSet; + }); + + put(R.id.menu_action_anticipate_overshoot_interpolator, () -> + obtainExampleInterpolator(new AnticipateOvershootInterpolator(), 2500) + ); + + put(R.id.menu_action_path_interpolator, () -> obtainExampleInterpolator( + PathInterpolatorCompat.create(.22f, .68f, 0, 1.71f), 2500)); + } + }; + + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera_animator); + mapView = (MapView) findViewById(R.id.mapView); + if (mapView != null) { + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + } + + @Override + public void onMapReady(final MapboxMap map) { + mapboxMap = map; + initFab(); + } + + private void initFab() { + findViewById(R.id.fab).setOnClickListener(view -> { + view.setVisibility(View.GONE); + + CameraPosition animatedPosition = new CameraPosition.Builder() + .target(new LatLng(37.789992, -122.402214)) + .tilt(60) + .zoom(14.5f) + .bearing(135) + .build(); + + createExampleAnimator(mapboxMap.getCameraPosition(), animatedPosition).start(); + }); + } + + // + // Animator API used for the animation on the FAB + // + + private Animator createExampleAnimator(CameraPosition currentPosition, CameraPosition targetPosition) { + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.play(createLatLngAnimator(currentPosition.target, targetPosition.target)); + animatorSet.play(createZoomAnimator(currentPosition.zoom, targetPosition.zoom)); + animatorSet.play(createBearingAnimator(currentPosition.bearing, targetPosition.bearing)); + animatorSet.play(createTiltAnimator(currentPosition.tilt, targetPosition.tilt)); + return animatorSet; + } + + private Animator createLatLngAnimator(LatLng currentPosition, LatLng targetPosition) { + ValueAnimator latLngAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), currentPosition, targetPosition); + latLngAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + latLngAnimator.setInterpolator(new FastOutSlowInInterpolator()); + latLngAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.newLatLng((LatLng) animation.getAnimatedValue())) + ); + return latLngAnimator; + } + + private Animator createZoomAnimator(double currentZoom, double targetZoom) { + ValueAnimator zoomAnimator = ValueAnimator.ofFloat((float) currentZoom, (float) targetZoom); + zoomAnimator.setDuration((long) (2200 * ANIMATION_DELAY_FACTOR)); + zoomAnimator.setStartDelay((long) (600 * ANIMATION_DELAY_FACTOR)); + zoomAnimator.setInterpolator(new AnticipateOvershootInterpolator()); + zoomAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.zoomTo((Float) animation.getAnimatedValue())) + ); + return zoomAnimator; + } + + private Animator createBearingAnimator(double currentBearing, double targetBearing) { + ValueAnimator bearingAnimator = ValueAnimator.ofFloat((float) currentBearing, (float) targetBearing); + bearingAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + bearingAnimator.setStartDelay((long) (1000 * ANIMATION_DELAY_FACTOR)); + bearingAnimator.setInterpolator(new FastOutLinearInInterpolator()); + bearingAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.bearingTo((Float) animation.getAnimatedValue())) + ); + return bearingAnimator; + } + + private Animator createTiltAnimator(double currentTilt, double targetTilt) { + ValueAnimator tiltAnimator = ValueAnimator.ofFloat((float) currentTilt, (float) targetTilt); + tiltAnimator.setDuration((long) (1000 * ANIMATION_DELAY_FACTOR)); + tiltAnimator.setStartDelay((long) (1500 * ANIMATION_DELAY_FACTOR)); + tiltAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.tiltTo((Float) animation.getAnimatedValue())) + ); + return tiltAnimator; + } + + // + // Interpolator examples + // + + private Animator obtainExampleInterpolator(int menuItemId) { + return animators.get(menuItemId).build(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_animator, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mapboxMap == null) { + return false; + } + + if (item.getItemId() != android.R.id.home) { + findViewById(R.id.fab).setVisibility(View.GONE); + resetCameraPosition(); + playAnimation(item.getItemId()); + } + return super.onOptionsItemSelected(item); + } + + private void resetCameraPosition() { + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(START_LAT_LNG) + .zoom(11) + .bearing(0) + .tilt(0) + .build() + )); + } + + private void playAnimation(int itemId) { + Animator animator = obtainExampleInterpolator(itemId); + if (animator != null) { + animator.start(); + } + } + + private Animator obtainExampleInterpolator(Interpolator interpolator, long duration) { + ValueAnimator zoomAnimator = ValueAnimator.ofFloat(11.0f, 16.0f); + zoomAnimator.setDuration((long) (duration * ANIMATION_DELAY_FACTOR)); + zoomAnimator.setInterpolator(interpolator); + zoomAnimator.addUpdateListener(animation -> mapboxMap.moveCamera( + CameraUpdateFactory.zoomTo((Float) animation.getAnimatedValue())) + ); + return zoomAnimator; + } + + // + // MapView lifecycle + // + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + + /** + * Helper class to evaluate LatLng objects with a ValueAnimator + */ + private static class LatLngEvaluator implements TypeEvaluator<LatLng> { + + private final LatLng latLng = new LatLng(); + + @Override + public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) { + latLng.setLatitude(startValue.getLatitude() + + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); + latLng.setLongitude(startValue.getLongitude() + + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); + return latLng; + } + } + + interface AnimatorBuilder { + Animator build(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraPositionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraPositionActivity.java new file mode 100644 index 0000000000..1c4b022416 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/CameraPositionActivity.java @@ -0,0 +1,254 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.content.Context; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.SeekBar; +import android.widget.TextView; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; +import timber.log.Timber; + +/** + * Test activity showcasing how to listen to camera change events. + */ +public class CameraPositionActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener, + MapboxMap.OnMapLongClickListener { + + private MapView mapView; + private MapboxMap mapboxMap; + private FloatingActionButton fab; + private boolean logCameraChanges; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera_position); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + toggleLogCameraChanges(); + + // add a listener to FAB + fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, R.color.primary)); + fab.setOnClickListener(this); + + // listen to long click events to toggle logging camera changes + mapboxMap.setOnMapLongClickListener(this); + } + + @Override + public void onMapLongClick(@NonNull LatLng point) { + toggleLogCameraChanges(); + } + + @Override + public void onClick(View view) { + Context context = view.getContext(); + final View dialogContent = LayoutInflater.from(context).inflate(R.layout.dialog_camera_position, null); + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.dialog_camera_position); + builder.setView(onInflateDialogContent(dialogContent)); + builder.setPositiveButton("Animate", new DialogClickListener(mapboxMap, dialogContent)); + builder.setNegativeButton("Cancel", null); + builder.setCancelable(false); + builder.show(); + } + + private void toggleLogCameraChanges() { + logCameraChanges = !logCameraChanges; + if (logCameraChanges) { + mapboxMap.addOnCameraIdleListener(idleListener); + mapboxMap.addOnCameraMoveCancelListener(moveCanceledListener); + mapboxMap.addOnCameraMoveListener(moveListener); + mapboxMap.addOnCameraMoveStartedListener(moveStartedListener); + } else { + mapboxMap.removeOnCameraIdleListener(idleListener); + mapboxMap.removeOnCameraMoveCancelListener(moveCanceledListener); + mapboxMap.removeOnCameraMoveListener(moveListener); + mapboxMap.removeOnCameraMoveStartedListener(moveStartedListener); + } + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + private View onInflateDialogContent(View view) { + linkTextView(view, R.id.value_lat, R.id.seekbar_lat, new LatLngChangeListener(), 180 + 38); + linkTextView(view, R.id.value_lon, R.id.seekbar_lon, new LatLngChangeListener(), 180 - 77); + linkTextView(view, R.id.value_zoom, R.id.seekbar_zoom, new ValueChangeListener(), 6); + linkTextView(view, R.id.value_bearing, R.id.seekbar_bearing, new ValueChangeListener(), 90); + linkTextView(view, R.id.value_tilt, R.id.seekbar_tilt, new ValueChangeListener(), 40); + return view; + } + + private void linkTextView( + View view, @IdRes int textViewRes, @IdRes int seekBarRes, ValueChangeListener listener, int defaultValue) { + final TextView value = (TextView) view.findViewById(textViewRes); + SeekBar seekBar = (SeekBar) view.findViewById(seekBarRes); + listener.setLinkedValueView(value); + seekBar.setOnSeekBarChangeListener(listener); + seekBar.setProgress(defaultValue); + } + + private MapboxMap.OnCameraIdleListener idleListener = new MapboxMap.OnCameraIdleListener() { + @Override + public void onCameraIdle() { + Timber.e("OnCameraIdle"); + fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_green_dark)); + } + }; + + private MapboxMap.OnCameraMoveListener moveListener = new MapboxMap.OnCameraMoveListener() { + @Override + public void onCameraMove() { + Timber.e("OnCameraMove"); + fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_orange_dark)); + } + }; + + private MapboxMap.OnCameraMoveCanceledListener moveCanceledListener = () -> Timber.e("OnCameraMoveCanceled"); + + private MapboxMap.OnCameraMoveStartedListener moveStartedListener = new MapboxMap.OnCameraMoveStartedListener() { + + private final String[] REASONS = {"REASON_API_GESTURE", "REASON_DEVELOPER_ANIMATION", "REASON_API_ANIMATION"}; + + @Override + public void onCameraMoveStarted(int reason) { + // reason ranges from 1 <-> 3 + fab.setColorFilter(ContextCompat.getColor(CameraPositionActivity.this, android.R.color.holo_red_dark)); + Timber.e("OnCameraMoveStarted: %s", REASONS[reason - 1]); + } + }; + + private class ValueChangeListener implements SeekBar.OnSeekBarChangeListener { + + protected TextView textView; + + public void setLinkedValueView(TextView textView) { + this.textView = textView; + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + textView.setText(String.valueOf(progress)); + } + } + + private class LatLngChangeListener extends ValueChangeListener { + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + super.onProgressChanged(seekBar, progress - 180, fromUser); + } + } + + private static class DialogClickListener implements DialogInterface.OnClickListener { + + private MapboxMap mapboxMap; + private View dialogContent; + + public DialogClickListener(MapboxMap mapboxMap, View view) { + this.mapboxMap = mapboxMap; + this.dialogContent = view; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + double latitude = Double.parseDouble( + ((TextView) dialogContent.findViewById(R.id.value_lat)).getText().toString()); + double longitude = Double.parseDouble( + ((TextView) dialogContent.findViewById(R.id.value_lon)).getText().toString()); + double zoom = Double.parseDouble( + ((TextView) dialogContent.findViewById(R.id.value_zoom)).getText().toString()); + double bearing = Double.parseDouble( + ((TextView) dialogContent.findViewById(R.id.value_bearing)).getText().toString()); + double tilt = Double.parseDouble( + ((TextView) dialogContent.findViewById(R.id.value_tilt)).getText().toString()); + + CameraPosition cameraPosition = new CameraPosition.Builder() + .target(new LatLng(latitude, longitude)) + .zoom(zoom) + .bearing(bearing) + .tilt(tilt) + .build(); + + mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 5000, + new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + Timber.v("OnCancel called"); + } + + @Override + public void onFinish() { + Timber.v("OnFinish called"); + } + }); + Timber.v(cameraPosition.toString()); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/GestureDetectorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/GestureDetectorActivity.java new file mode 100644 index 0000000000..89270880d0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/GestureDetectorActivity.java @@ -0,0 +1,422 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.graphics.Typeface; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.ColorInt; +import android.support.annotation.IntDef; +import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.FontCache; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.lang.annotation.Retention; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.annotation.RetentionPolicy.SOURCE; + +/** + * Test activity showcasing APIs around gestures implementation. + */ +public class GestureDetectorActivity extends AppCompatActivity { + + private static final int MAX_NUMBER_OF_ALERTS = 30; + + private MapView mapView; + private MapboxMap mapboxMap; + private RecyclerView recyclerView; + private GestureAlertsAdapter gestureAlertsAdapter; + + private AndroidGesturesManager gesturesManager; + + @Nullable + private Marker marker; + @Nullable + private LatLng focalPointLatLng; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_gesture_detector); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(MapboxMap mapboxMap) { + GestureDetectorActivity.this.mapboxMap = mapboxMap; + initializeMap(); + } + }); + + recyclerView = (RecyclerView) findViewById(R.id.alerts_recycler); + recyclerView.setLayoutManager(new LinearLayoutManager(this)); + + gestureAlertsAdapter = new GestureAlertsAdapter(); + recyclerView.setAdapter(gestureAlertsAdapter); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + gestureAlertsAdapter.cancelUpdates(); + mapView.onPause(); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + private void initializeMap() { + gesturesManager = mapboxMap.getGesturesManager(); + + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams(); + layoutParams.height = (int) (mapView.getHeight() / 1.75); + layoutParams.width = (mapView.getWidth() / 3); + recyclerView.setLayoutParams(layoutParams); + + attachListeners(); + } + + public void attachListeners() { + mapboxMap.addOnMoveListener(new MapboxMap.OnMoveListener() { + @Override + public void onMoveBegin(MoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "MOVE START")); + } + + @Override + public void onMove(MoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "MOVE PROGRESS")); + } + + @Override + public void onMoveEnd(MoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "MOVE END")); + } + }); + + mapboxMap.addOnRotateListener(new MapboxMap.OnRotateListener() { + @Override + public void onRotateBegin(RotateGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "ROTATE START")); + } + + @Override + public void onRotate(RotateGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "ROTATE PROGRESS")); + recalculateFocalPoint(); + } + + @Override + public void onRotateEnd(RotateGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "ROTATE END")); + } + }); + + mapboxMap.addOnScaleListener(new MapboxMap.OnScaleListener() { + @Override + public void onScaleBegin(StandardScaleGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SCALE START")); + if (focalPointLatLng != null) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "INCREASING MOVE THRESHOLD")); + gesturesManager.getMoveGestureDetector().setMoveThreshold( + ResourceUtils.convertDpToPx(GestureDetectorActivity.this, 175)); + + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "MANUALLY INTERRUPTING MOVE")); + gesturesManager.getMoveGestureDetector().interrupt(); + } + recalculateFocalPoint(); + } + + @Override + public void onScale(StandardScaleGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SCALE PROGRESS")); + } + + @Override + public void onScaleEnd(StandardScaleGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SCALE END")); + + if (focalPointLatLng != null) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_OTHER, "REVERTING MOVE THRESHOLD")); + gesturesManager.getMoveGestureDetector().setMoveThreshold(0f); + } + } + }); + + mapboxMap.addOnShoveListener(new MapboxMap.OnShoveListener() { + @Override + public void onShoveBegin(ShoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_START, "SHOVE START")); + } + + @Override + public void onShove(ShoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_PROGRESS, "SHOVE PROGRESS")); + } + + @Override + public void onShoveEnd(ShoveGestureDetector detector) { + gestureAlertsAdapter.addAlert(new GestureAlert(GestureAlert.TYPE_END, "SHOVE END")); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_gestures, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + resetModes(); + switch (item.getItemId()) { + case R.id.menu_gesture_none: + return true; + case R.id.menu_gesture_focus_point: + focalPointLatLng = new LatLng(51.50325, -0.12968); + marker = mapboxMap.addMarker(new MarkerOptions().position(focalPointLatLng)); + mapboxMap.easeCamera(CameraUpdateFactory.newLatLngZoom(focalPointLatLng, 16)); + mapboxMap.getUiSettings().setFocalPoint(mapboxMap.getProjection().toScreenLocation(focalPointLatLng)); + return true; + case R.id.menu_gesture_animation: + mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(false); + } + return super.onOptionsItemSelected(item); + } + + private void resetModes() { + focalPointLatLng = null; + mapboxMap.getUiSettings().setFocalPoint(null); + gesturesManager.getMoveGestureDetector().setMoveThreshold(0f); + mapboxMap.getUiSettings().setAllVelocityAnimationsEnabled(true); + + if (marker != null) { + mapboxMap.removeMarker(marker); + marker = null; + } + } + + private void recalculateFocalPoint() { + if (focalPointLatLng != null) { + mapboxMap.getUiSettings().setFocalPoint( + mapboxMap.getProjection().toScreenLocation(focalPointLatLng) + ); + } + } + + private static class GestureAlertsAdapter extends RecyclerView.Adapter<GestureAlertsAdapter.ViewHolder> { + + private boolean isUpdating; + private final Handler updateHandler = new Handler(); + private final List<GestureAlert> alerts = new ArrayList<>(); + + public static class ViewHolder extends RecyclerView.ViewHolder { + + TextView alertMessageTv; + + @ColorInt + public int textColor; + + ViewHolder(View view) { + super(view); + Typeface typeface = FontCache.get("Roboto-Regular.ttf", view.getContext()); + alertMessageTv = (TextView) view.findViewById(R.id.alert_message); + alertMessageTv.setTypeface(typeface); + } + } + + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_gesture_alert, parent, false); + return new ViewHolder(view); + } + + @Override + public void onBindViewHolder(ViewHolder holder, int position) { + GestureAlert alert = alerts.get(position); + holder.alertMessageTv.setText(alert.getMessage()); + holder.alertMessageTv.setTextColor( + ContextCompat.getColor(holder.alertMessageTv.getContext(), alert.getColor())); + } + + @Override + public int getItemCount() { + return alerts.size(); + } + + void addAlert(GestureAlert alert) { + for (GestureAlert gestureAlert : alerts) { + if (gestureAlert.getAlertType() != GestureAlert.TYPE_PROGRESS) { + break; + } + + if (alert.getAlertType() == GestureAlert.TYPE_PROGRESS && gestureAlert.equals(alert)) { + return; + } + } + + if (getItemCount() >= MAX_NUMBER_OF_ALERTS) { + alerts.remove(getItemCount() - 1); + } + + alerts.add(0, alert); + if (!isUpdating) { + isUpdating = true; + updateHandler.postDelayed(updateRunnable, 250); + } + } + + private Runnable updateRunnable = new Runnable() { + @Override + public void run() { + notifyDataSetChanged(); + isUpdating = false; + } + }; + + void cancelUpdates() { + updateHandler.removeCallbacksAndMessages(null); + } + } + + private static class GestureAlert { + @Retention(SOURCE) + @IntDef( {TYPE_NONE, TYPE_START, TYPE_PROGRESS, TYPE_END, TYPE_OTHER}) + @interface Type { + } + + static final int TYPE_NONE = 0; + static final int TYPE_START = 1; + static final int TYPE_END = 2; + static final int TYPE_PROGRESS = 3; + static final int TYPE_OTHER = 4; + + @Type + private int alertType; + + private String message; + + @ColorInt + private int color; + + GestureAlert(@Type int alertType, String message) { + this.alertType = alertType; + this.message = message; + + switch (alertType) { + case TYPE_NONE: + color = android.R.color.black; + break; + case TYPE_END: + color = android.R.color.holo_red_dark; + break; + case TYPE_OTHER: + color = android.R.color.holo_purple; + break; + case TYPE_PROGRESS: + color = android.R.color.holo_orange_dark; + break; + case TYPE_START: + color = android.R.color.holo_green_dark; + break; + } + } + + int getAlertType() { + return alertType; + } + + String getMessage() { + return message; + } + + int getColor() { + return color; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + GestureAlert that = (GestureAlert) o; + + if (alertType != that.alertType) { + return false; + } + return message != null ? message.equals(that.message) : that.message == null; + } + + @Override + public int hashCode() { + int result = alertType; + result = 31 * result + (message != null ? message.hashCode() : 0); + return result; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/LatLngBoundsActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/LatLngBoundsActivity.java new file mode 100644 index 0000000000..fd8f9afa01 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/LatLngBoundsActivity.java @@ -0,0 +1,162 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomSheetBehavior; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.http.HttpRequestUtil; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.view.LockableBottomSheetBehavior; + +import java.util.ArrayList; +import java.util.List; + +/** + * Test activity showcasing using the LatLngBounds camera API. + */ +public class LatLngBoundsActivity extends AppCompatActivity implements View.OnClickListener { + + private static final List<LatLng> LOCATIONS = new ArrayList<LatLng>() { + { + add(new LatLng(37.806866, -122.422502)); + add(new LatLng(37.812905, -122.477605)); + add(new LatLng(37.826944, -122.423188)); + add(new LatLng(37.752676, -122.447736)); + add(new LatLng(37.769305, -122.479322)); + add(new LatLng(37.749834, -122.417867)); + add(new LatLng(37.756149, -122.405679)); + add(new LatLng(37.751403, -122.387397)); + add(new LatLng(37.793064, -122.391517)); + add(new LatLng(37.769122, -122.427394)); + } + }; + private static final LatLngBounds BOUNDS = new LatLngBounds.Builder().includes(LOCATIONS).build(); + private static final int ANIMATION_DURATION_LONG = 450; + private static final int ANIMATION_DURATION_SHORT = 250; + private static final int BOUNDS_PADDING_DIVIDER_SMALL = 3; + private static final int BOUNDS_PADDING_DIVIDER_LARGE = 9; + + private MapView mapView; + private MapboxMap mapboxMap; + private View bottomSheet; + private LockableBottomSheetBehavior bottomSheetBehavior; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + HttpRequestUtil.setLogEnabled(false); + setContentView(R.layout.activity_latlngbounds); + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + initMap(); + }); + } + + private void initMap() { + addMarkers(); + initFab(); + initBottomSheet(); + moveToBounds(bottomSheet.getMeasuredHeight(), BOUNDS_PADDING_DIVIDER_SMALL, 0); + } + + private void addMarkers() { + for (LatLng location : LOCATIONS) { + mapboxMap.addMarker(new MarkerOptions().position(location)); + } + } + + private void initFab() { + findViewById(R.id.fab).setOnClickListener(this); + } + + @Override + public void onClick(View v) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + v.animate().alpha(0.0f).setDuration(ANIMATION_DURATION_SHORT); + } + + private void initBottomSheet() { + bottomSheet = findViewById(R.id.bottom_sheet); + bottomSheetBehavior = (LockableBottomSheetBehavior) BottomSheetBehavior.from(bottomSheet); + bottomSheetBehavior.setLocked(true); + bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull View bottomSheet, int newState) { + if (newState == BottomSheetBehavior.STATE_SETTLING) { + moveToBounds(0, BOUNDS_PADDING_DIVIDER_LARGE, ANIMATION_DURATION_LONG); + } + } + + @Override + public void onSlide(@NonNull View bottomSheet, float slideOffset) { + + } + }); + } + + private void moveToBounds(int verticalOffset, int boundsPaddingDivider, int duration) { + int paddingHorizontal = mapView.getMeasuredWidth() / boundsPaddingDivider; + int paddingVertical = (mapView.getMeasuredHeight() - verticalOffset) / boundsPaddingDivider; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds( + BOUNDS, + paddingHorizontal, + paddingVertical, + paddingHorizontal, + paddingVertical + verticalOffset), + duration + ); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + HttpRequestUtil.setLogEnabled(true); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ManualZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ManualZoomActivity.java new file mode 100644 index 0000000000..7288850636 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ManualZoomActivity.java @@ -0,0 +1,121 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.graphics.Point; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.UiSettings; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing the zoom Camera API. + * <p> + * This includes zoomIn, zoomOut, zoomTo, zoomBy (center and custom focal point). + * </p> + */ +public class ManualZoomActivity extends AppCompatActivity { + + private MapboxMap mapboxMap; + private MapView mapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_manual_zoom); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.setStyleUrl(Style.SATELLITE); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + ManualZoomActivity.this.mapboxMap = mapboxMap; + + UiSettings uiSettings = ManualZoomActivity.this.mapboxMap.getUiSettings(); + uiSettings.setAllGesturesEnabled(false); + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_zoom, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.action_zoom_in: + mapboxMap.animateCamera(CameraUpdateFactory.zoomIn()); + return true; + + case R.id.action_zoom_out: + mapboxMap.animateCamera(CameraUpdateFactory.zoomOut()); + return true; + + case R.id.action_zoom_by: + mapboxMap.animateCamera(CameraUpdateFactory.zoomBy(2)); + return true; + case R.id.action_zoom_to: + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(2)); + return true; + + case R.id.action_zoom_to_point: + View view = getWindow().getDecorView(); + mapboxMap.animateCamera( + CameraUpdateFactory.zoomBy(1, new Point(view.getMeasuredWidth() / 4, view.getMeasuredHeight() / 4))); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/MaxMinZoomActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/MaxMinZoomActivity.java new file mode 100644 index 0000000000..26daed7f15 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/MaxMinZoomActivity.java @@ -0,0 +1,82 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.constants.Style; +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 timber.log.Timber; + +/** + * Test activity showcasing using maximum and minimum zoom levels to restrict camera movement. + */ +public class MaxMinZoomActivity extends AppCompatActivity implements OnMapReadyCallback { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_maxmin_zoom); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(final MapboxMap map) { + mapboxMap = map; + mapboxMap.setMinZoomPreference(3); + mapboxMap.setMaxZoomPreference(5); + mapboxMap.setOnMapClickListener(point -> map.setStyle(Style.OUTDOORS, style -> Timber.d("Style Loaded %s", style))); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ScrollByActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ScrollByActivity.java new file mode 100644 index 0000000000..5a85ff0f44 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/camera/ScrollByActivity.java @@ -0,0 +1,157 @@ +package com.mapbox.mapboxsdk.maps.activity.camera; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.MenuItem; +import android.widget.SeekBar; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.maps.UiSettings; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the scrollBy Camera API by moving x,y pixels above Grenada, Spain. + */ +public class ScrollByActivity extends AppCompatActivity implements OnMapReadyCallback { + + public static final int MULTIPLIER_PER_PIXEL = 50; + + private MapView mapView; + private MapboxMap mapboxMap; + private SeekBar seekBarX; + private SeekBar seekBarY; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_scroll_by); + + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayShowHomeEnabled(true); + } + + seekBarX = (SeekBar) findViewById(R.id.seekbar_move_x); + TextView textViewX = (TextView) findViewById(R.id.textview_x); + seekBarX.setOnSeekBarChangeListener(new PixelBarChangeListener(textViewX, R.string.scrollby_x_value)); + + seekBarY = (SeekBar) findViewById(R.id.seekbar_move_y); + TextView textViewY = (TextView) findViewById(R.id.textview_y); + seekBarY.setOnSeekBarChangeListener(new PixelBarChangeListener(textViewY, R.string.scrollby_y_value)); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.setTag(true); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + UiSettings uiSettings = mapboxMap.getUiSettings(); + uiSettings.setLogoEnabled(false); + uiSettings.setAttributionEnabled(false); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setColorFilter(ContextCompat.getColor(ScrollByActivity.this, R.color.primary)); + fab.setOnClickListener(view -> mapboxMap.easeCamera(CameraUpdateFactory.scrollBy( + seekBarX.getProgress() * MULTIPLIER_PER_PIXEL, + seekBarY.getProgress() * MULTIPLIER_PER_PIXEL) + )); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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); + } + } + + private static class PixelBarChangeListener implements SeekBar.OnSeekBarChangeListener { + + @StringRes + private int prefixTextResource; + private TextView valueView; + + public PixelBarChangeListener(@NonNull TextView textView, @StringRes int textRes) { + valueView = textView; + prefixTextResource = textRes; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + int value = progress * ScrollByActivity.MULTIPLIER_PER_PIXEL; + valueView.setText(String.format(seekBar.getResources().getString(prefixTextResource), value)); + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/customlayer/CustomLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/customlayer/CustomLayerActivity.java new file mode 100644 index 0000000000..d10d4b28b4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/customlayer/CustomLayerActivity.java @@ -0,0 +1,140 @@ +package com.mapbox.mapboxsdk.maps.activity.customlayer; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.CustomLayer; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.model.customlayer.ExampleCustomLayer; + +/** + * Test activity showcasing the Custom Layer API + * <p> + * Note: experimental API, do not use. + * </p> + */ +public class CustomLayerActivity extends AppCompatActivity { + + private MapboxMap mapboxMap; + private MapView mapView; + private CustomLayer customLayer; + + private FloatingActionButton fab; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_custom_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(39.91448, -243.60947), 10)); + + }); + + fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setColorFilter(ContextCompat.getColor(this, R.color.primary)); + fab.setOnClickListener(view -> { + if (mapboxMap != null) { + swapCustomLayer(); + } + }); + } + + private void swapCustomLayer() { + if (customLayer != null) { + mapboxMap.removeLayer(customLayer); + customLayer = null; + fab.setImageResource(R.drawable.ic_layers); + } else { + customLayer = new CustomLayer("custom", + ExampleCustomLayer.createContext()); + mapboxMap.addLayerBelow(customLayer, "building"); + fab.setImageResource(R.drawable.ic_layers_clear); + } + } + + private void updateLayer() { + if (customLayer != null) { + customLayer.update(); + } + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_custom_layer, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_update_layer: + updateLayer(); + return true; + case R.id.action_set_color_red: + ExampleCustomLayer.setColor(1, 0, 0, 1); + return true; + case R.id.action_set_color_green: + ExampleCustomLayer.setColor(0, 1, 0, 1); + return true; + case R.id.action_set_color_blue: + ExampleCustomLayer.setColor(0, 0, 1, 1); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/DeviceIndependentTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/DeviceIndependentTestActivity.java new file mode 100644 index 0000000000..83a9c8c891 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/DeviceIndependentTestActivity.java @@ -0,0 +1,76 @@ +package com.mapbox.mapboxsdk.maps.activity.espresso; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +public class DeviceIndependentTestActivity extends AppCompatActivity implements OnMapReadyCallback { + + public MapView mapView; + protected MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera_test); + + // Initialize map as normal + mapView = findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/EspressoTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/EspressoTestActivity.java new file mode 100644 index 0000000000..92d4327802 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/espresso/EspressoTestActivity.java @@ -0,0 +1,80 @@ +package com.mapbox.mapboxsdk.maps.activity.espresso; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Base activity for instrumentation testing. + */ +public class EspressoTestActivity extends AppCompatActivity implements OnMapReadyCallback { + + public MapView mapView; + protected MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_espresso_test); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxCountActivity.java new file mode 100644 index 0000000000..75c5acec74 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxCountActivity.java @@ -0,0 +1,125 @@ +package com.mapbox.mapboxsdk.maps.activity.feature; + +import android.graphics.RectF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.List; +import java.util.Map; + +import timber.log.Timber; + +/** + * Test activity showcasing using the query rendered features API to count features in a rectangle. + */ +public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_query_features_box); + + final View selectionBox = findViewById(R.id.selection_box); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + QueryRenderedFeaturesBoxCountActivity.this.mapboxMap = mapboxMap; + selectionBox.setOnClickListener(view -> { + // Query + int top = selectionBox.getTop() - mapView.getTop(); + int left = selectionBox.getLeft() - mapView.getLeft(); + RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); + Timber.i("Querying box %s", box); + List<Feature> features = mapboxMap.queryRenderedFeatures(box); + + // Show count + Toast.makeText( + QueryRenderedFeaturesBoxCountActivity.this, + String.format("%s features in box", features.size()), + Toast.LENGTH_SHORT).show(); + + // Debug output + debugOutput(features); + }); + }); + } + + private void debugOutput(List<Feature> features) { + Timber.i("Got %s features", features.size()); + for (Feature feature : features) { + if (feature != null) { + Timber.i("Got feature %s with %s properties and Geometry %s", + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "<null>", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "<null>" + ); + if (feature.properties() != null) { + for (Map.Entry<String, JsonElement> entry : feature.properties().entrySet()) { + Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); + } + } + } else { + Timber.i("Got 0 features"); + } + } + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java new file mode 100644 index 0000000000..2fb119bd02 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java @@ -0,0 +1,125 @@ +package com.mapbox.mapboxsdk.maps.activity.feature; + +import android.graphics.Color; +import android.graphics.RectF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.FillLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.lt; +import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor; + +/** + * Demo's query rendered features + */ +public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_query_features_box); + + final View selectionBox = findViewById(R.id.selection_box); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap; + + // Add layer / source + final GeoJsonSource source = new GeoJsonSource("highlighted-shapes-source"); + mapboxMap.addSource(source); + mapboxMap.addLayer( + new FillLayer("highlighted-shapes-layer", "highlighted-shapes-source") + .withProperties(fillColor(Color.RED)) + ); + + selectionBox.setOnClickListener(view -> { + // Query + int top = selectionBox.getTop() - mapView.getTop(); + int left = selectionBox.getLeft() - mapView.getLeft(); + RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); + Timber.i("Querying box %s for buildings", box); + + Expression filter = lt(toNumber(get("height")), literal(10)); + List<Feature> features = mapboxMap.queryRenderedFeatures(box, filter, "building"); + + // Show count + Toast.makeText( + QueryRenderedFeaturesBoxHighlightActivity.this, + String.format("%s features in box", features.size()), + Toast.LENGTH_SHORT).show(); + + // Update source data + source.setGeoJson(FeatureCollection.fromFeatures(features)); + }); + }); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java new file mode 100644 index 0000000000..efef5cc39e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java @@ -0,0 +1,129 @@ +package com.mapbox.mapboxsdk.maps.activity.feature; + +import android.graphics.BitmapFactory; +import android.graphics.RectF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Toast; + +import com.mapbox.geojson.Feature; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.IOException; +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; + +/** + * Test activity showcasing using the query rendered features API to count Symbols in a rectangle. + */ +public class QueryRenderedFeaturesBoxSymbolCountActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + private Toast toast; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_query_features_box); + + final View selectionBox = findViewById(R.id.selection_box); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + QueryRenderedFeaturesBoxSymbolCountActivity.this.mapboxMap = mapboxMap; + + // Add a symbol layer (also works with annotations) + try { + mapboxMap.addSource(new GeoJsonSource("symbols-source", ResourceUtils.readRawResource( + QueryRenderedFeaturesBoxSymbolCountActivity.this, R.raw.test_points_utrecht))); + } catch (IOException ioException) { + Timber.e(ioException, "Could not load geojson"); + return; + } + mapboxMap.addImage( + "test-icon", + BitmapFactory.decodeResource(getResources(), + R.drawable.mapbox_marker_icon_default) + ); + mapboxMap.addLayer(new SymbolLayer("symbols-layer", "symbols-source").withProperties(iconImage("test-icon"))); + + selectionBox.setOnClickListener(view -> { + // Query + int top = selectionBox.getTop() - mapView.getTop(); + int left = selectionBox.getLeft() - mapView.getLeft(); + RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight()); + Timber.i("Querying box %s", box); + List<Feature> features = mapboxMap.queryRenderedFeatures(box, "symbols-layer"); + + // Show count + if (toast != null) { + toast.cancel(); + } + toast = Toast.makeText( + QueryRenderedFeaturesBoxSymbolCountActivity.this, + String.format("%s features in box", features.size()), + Toast.LENGTH_SHORT); + toast.show(); + }); + }); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesPropertiesActivity.java new file mode 100644 index 0000000000..1d5e00c55a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QueryRenderedFeaturesPropertiesActivity.java @@ -0,0 +1,234 @@ +package com.mapbox.mapboxsdk.maps.activity.feature; + +import android.graphics.Color; +import android.graphics.PointF; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.google.gson.JsonElement; +import com.mapbox.geojson.Feature; +import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + + +import java.util.List; +import java.util.Map; + +import timber.log.Timber; + +/** + * Test activity showcasing using the query rendered features API to query feature properties on Map click. + */ +public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + private Marker marker; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_query_features_point); + + final float density = getResources().getDisplayMetrics().density; + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + QueryRenderedFeaturesPropertiesActivity.this.mapboxMap = mapboxMap; + + // Add custom window adapter + addCustomInfoWindowAdapter(mapboxMap); + + // Add a click listener + mapboxMap.setOnMapClickListener(point -> { + // Query + final PointF pixel = mapboxMap.getProjection().toScreenLocation(point); + Timber.i( + "Requesting features for %sx%s (%sx%s adjusted for density)", + pixel.x, pixel.y, pixel.x / density, pixel.y / density + ); + List<Feature> features = mapboxMap.queryRenderedFeatures(pixel); + + // Debug output + debugOutput(features); + + // Remove any previous markers + if (marker != null) { + mapboxMap.removeMarker(marker); + } + + // Add a marker on the clicked point + marker = mapboxMap.addMarker(new CustomMarkerOptions().position(point).features(features)); + mapboxMap.selectMarker(marker); + }); + }); + + } + + private void debugOutput(List<Feature> features) { + Timber.i("Got %s features", features.size()); + for (Feature feature : features) { + if (feature != null) { + Timber.i("Got feature %s with %s properties and Geometry %s", + feature.id(), + feature.properties() != null ? feature.properties().entrySet().size() : "<null>", + feature.geometry() != null ? feature.geometry().getClass().getSimpleName() : "<null>" + ); + if (feature.properties() != null) { + for (Map.Entry<String, JsonElement> entry : feature.properties().entrySet()) { + Timber.i("Prop %s - %s", entry.getKey(), entry.getValue()); + } + } + } else { + Timber.i("Got NULL feature"); + } + } + } + + private void addCustomInfoWindowAdapter(MapboxMap mapboxMap) { + mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() { + + private TextView row(String text) { + TextView view = new TextView(QueryRenderedFeaturesPropertiesActivity.this); + view.setText(text); + return view; + } + + @Override + public View getInfoWindow(@NonNull Marker marker) { + CustomMarker customMarker = (CustomMarker) marker; + LinearLayout view = new LinearLayout(QueryRenderedFeaturesPropertiesActivity.this); + view.setOrientation(LinearLayout.VERTICAL); + view.setBackgroundColor(Color.WHITE); + + if (customMarker.features.size() > 0) { + view.addView(row(String.format("Found %s features", customMarker.features.size()))); + Feature feature = customMarker.features.get(0); + for (Map.Entry<String, JsonElement> prop : feature.properties().entrySet()) { + view.addView(row(String.format("%s: %s", prop.getKey(), prop.getValue()))); + } + } else { + view.addView(row("No features here")); + } + + return view; + } + }); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + + private static class CustomMarker extends Marker { + + private final List<Feature> features; + + CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) { + super(baseMarkerOptions); + this.features = features; + } + } + + private static class CustomMarkerOptions extends BaseMarkerOptions<CustomMarker, CustomMarkerOptions> { + + + private List<Feature> features; + + public CustomMarkerOptions features(List<Feature> features) { + this.features = features; + return this; + } + + CustomMarkerOptions() { + } + + private CustomMarkerOptions(Parcel in) { + // Should implement this + } + + @Override + public CustomMarkerOptions getThis() { + return this; + } + + @Override + public CustomMarker getMarker() { + return new CustomMarker(this, features); + } + + public static final Parcelable.Creator<CustomMarkerOptions> CREATOR = + new Parcelable.Creator<CustomMarkerOptions>() { + public CustomMarkerOptions createFromParcel(Parcel in) { + return new CustomMarkerOptions(in); + } + + public CustomMarkerOptions[] newArray(int size) { + return new CustomMarkerOptions[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + // Should implement this + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QuerySourceFeaturesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QuerySourceFeaturesActivity.java new file mode 100644 index 0000000000..f1f63b6b59 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/feature/QuerySourceFeaturesActivity.java @@ -0,0 +1,109 @@ +package com.mapbox.mapboxsdk.maps.activity.feature; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.widget.Toast; + +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.List; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.neq; + +/** + * Test activity showcasing using the query source features API to query feature counts + */ +public class QuerySourceFeaturesActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_query_source_features); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + QuerySourceFeaturesActivity.this.mapboxMap = mapboxMap; + + JsonObject properties = new JsonObject(); + properties.addProperty("key1", "value1"); + final GeoJsonSource source = new GeoJsonSource("test-source", + FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry(Point.fromLngLat(0, 0), properties) + })); + mapboxMap.addSource(source); + + mapboxMap.addLayer(new CircleLayer("test-layer", source.getId()).withFilter(neq(get("key1"), literal("value1")))); + + // Add a click listener + mapboxMap.setOnMapClickListener(point -> { + // Query + List<Feature> features = source.querySourceFeatures(eq(get("key1"), literal("value1"))); + Toast.makeText(QuerySourceFeaturesActivity.this, String.format("Found %s features", + features.size()), Toast.LENGTH_SHORT).show(); + }); + }); + + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MapFragmentActivity.java new file mode 100644 index 0000000000..25a5c649fb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MapFragmentActivity.java @@ -0,0 +1,89 @@ +package com.mapbox.mapboxsdk.maps.activity.fragment; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +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.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the MapFragment API using SDK Fragments. + * <p> + * Uses MapboxMapOptions to initialise the Fragment. + * </p> + */ +public class MapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { + + private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_fragment); + if (savedInstanceState == null) { + MapFragment mapFragment = MapFragment.newInstance(createFragmentOptions()); + getFragmentManager() + .beginTransaction() + .add(R.id.fragment_container,mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } + + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.OUTDOORS); + + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); + + LatLng dc = new LatLng(38.90252, -77.02291); + + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } + + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } + + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MultiMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MultiMapActivity.java new file mode 100644 index 0000000000..3de1adfd7d --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/MultiMapActivity.java @@ -0,0 +1,18 @@ +package com.mapbox.mapboxsdk.maps.activity.fragment; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test Activity showcasing using multiple static map fragments in one layout. + */ +public class MultiMapActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_multi_map); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/SupportMapFragmentActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/SupportMapFragmentActivity.java new file mode 100644 index 0000000000..6d294eac61 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/SupportMapFragmentActivity.java @@ -0,0 +1,90 @@ +package com.mapbox.mapboxsdk.maps.activity.fragment; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +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.MapFragment; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.maps.SupportMapFragment; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the MapFragment API using Support Library Fragments. + * <p> + * Uses MapboxMapOptions to initialise the Fragment. + * </p> + */ +public class SupportMapFragmentActivity extends AppCompatActivity implements MapFragment.OnMapViewReadyCallback, + OnMapReadyCallback, MapView.OnMapChangedListener { + + private MapboxMap mapboxMap; + private MapView mapView; + private boolean initialCameraAnimation = true; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_fragment); + if (savedInstanceState == null) { + SupportMapFragment mapFragment = SupportMapFragment.newInstance(createFragmentOptions()); + getSupportFragmentManager() + .beginTransaction() + .add(R.id.fragment_container, mapFragment, "com.mapbox.map") + .commit(); + mapFragment.getMapAsync(this); + } + } + + private MapboxMapOptions createFragmentOptions() { + MapboxMapOptions options = new MapboxMapOptions(); + options.styleUrl(Style.MAPBOX_STREETS); + + options.scrollGesturesEnabled(false); + options.zoomGesturesEnabled(false); + options.tiltGesturesEnabled(false); + options.rotateGesturesEnabled(false); + options.debugActive(false); + + LatLng dc = new LatLng(38.90252, -77.02291); + + options.minZoomPreference(9); + options.maxZoomPreference(11); + options.camera(new CameraPosition.Builder() + .target(dc) + .zoom(11) + .build()); + return options; + } + + @Override + public void onMapViewReady(MapView map) { + mapView = map; + mapView.addOnMapChangedListener(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + } + + @Override + public void onMapChanged(int change) { + if (initialCameraAnimation && change == MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED) { + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition(new CameraPosition.Builder().tilt(45.0).build()), 5000); + initialCameraAnimation = false; + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.removeOnMapChangedListener(this); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/ViewPagerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/ViewPagerActivity.java new file mode 100644 index 0000000000..d1e4af97d4 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/fragment/ViewPagerActivity.java @@ -0,0 +1,79 @@ +package com.mapbox.mapboxsdk.maps.activity.fragment; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.SupportMapFragment; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the Android SDK ViewPager API to show MapFragments. + */ +public class ViewPagerActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_viewpager); + + ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager); + if (viewPager != null) { + MapFragmentAdapter adapter = new MapFragmentAdapter(getSupportFragmentManager()); + viewPager.setAdapter(adapter); + } + } + + static class MapFragmentAdapter extends FragmentPagerAdapter { + + private static int NUM_ITEMS = 3; + + MapFragmentAdapter(FragmentManager fragmentManager) { + super(fragmentManager); + } + + @Override + public int getCount() { + return NUM_ITEMS; + } + + @Override + public Fragment getItem(int position) { + SupportMapFragment fragment = null; + MapboxMapOptions options = new MapboxMapOptions(); + options.textureMode(true); + + switch (position) { + case 0: + options.styleUrl(Style.MAPBOX_STREETS); + options.camera(new CameraPosition.Builder().target(new LatLng(34.920526, 102.634774)).zoom(3).build()); + fragment = SupportMapFragment.newInstance(options); + break; + case 1: + options.styleUrl(Style.DARK); + options.camera(new CameraPosition.Builder().target(new LatLng(62.326440, 92.764913)).zoom(3).build()); + fragment = SupportMapFragment.newInstance(options); + break; + case 2: + options.styleUrl(Style.SATELLITE); + options.camera(new CameraPosition.Builder().target(new LatLng(-25.007786, 133.623852)).zoom(3).build()); + fragment = SupportMapFragment.newInstance(options); + break; + } + return fragment; + } + + @Override + public CharSequence getPageTitle(int position) { + return "Page " + position; + } + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/PrintActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/PrintActivity.java new file mode 100644 index 0000000000..4c4e4540e7 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/PrintActivity.java @@ -0,0 +1,88 @@ +package com.mapbox.mapboxsdk.maps.activity.imagegenerator; + +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.v4.print.PrintHelper; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the Snaphot API to print a Map. + */ +public class PrintActivity extends AppCompatActivity implements MapboxMap.SnapshotReadyCallback { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_print); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> PrintActivity.this.mapboxMap = mapboxMap); + + final View fab = findViewById(R.id.fab); + if (fab != null) { + fab.setOnClickListener(view -> { + if (mapboxMap != null) { + mapboxMap.snapshot(PrintActivity.this); + } + }); + } + } + + @Override + public void onSnapshotReady(Bitmap snapshot) { + PrintHelper photoPrinter = new PrintHelper(this); + photoPrinter.setScaleMode(PrintHelper.SCALE_MODE_FIT); + photoPrinter.printBitmap("map.jpg - mapbox print job", snapshot); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/SnapshotActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/SnapshotActivity.java new file mode 100644 index 0000000000..dedc47c691 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/imagegenerator/SnapshotActivity.java @@ -0,0 +1,113 @@ +package com.mapbox.mapboxsdk.maps.activity.imagegenerator; + +import android.graphics.Bitmap; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.ImageView; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.constants.Style; +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 java.util.Locale; + +import timber.log.Timber; + +/** + * Test activity showcasing the Snapshot API to create and display a bitmap of the current shown Map. + */ +public class SnapshotActivity extends AppCompatActivity implements OnMapReadyCallback, View.OnClickListener { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_snapshot); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.setStyleUrl(Style.OUTDOORS); + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + if (fab != null) { + fab.setColorFilter(ContextCompat.getColor(SnapshotActivity.this, R.color.primary)); + fab.setOnClickListener(this); + } + } + + @Override + public void onClick(View view) { + final long startTime = System.nanoTime(); + mapboxMap.snapshot(snapshot -> { + long endTime = System.nanoTime(); + long duration = (long) ((endTime - startTime) / 1e6); + ImageView snapshotView = (ImageView) findViewById(R.id.imageView); + snapshotView.setImageBitmap(snapshot); + Toast.makeText( + SnapshotActivity.this, + String.format(Locale.getDefault(), "Snapshot taken in %d ms", duration), + Toast.LENGTH_LONG).show(); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapboxMap.snapshot(new MapboxMap.SnapshotReadyCallback() { + @Override + public void onSnapshotReady(Bitmap snapshot) { + Timber.e("Regression test for https://github.com/mapbox/mapbox-gl-native/pull/11358"); + } + }); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/DynamicInfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/DynamicInfoWindowAdapterActivity.java new file mode 100644 index 0000000000..a19d626028 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/DynamicInfoWindowAdapterActivity.java @@ -0,0 +1,142 @@ +package com.mapbox.mapboxsdk.maps.activity.infowindow; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v7.app.AppCompatActivity; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.annotations.InfoWindow; +import com.mapbox.mapboxsdk.annotations.MarkerView; +import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.IconUtils; + +import java.util.Locale; + +/** + * Test activity showcasing how to dynamically update InfoWindow when Using an MapboxMap.InfoWindowAdapter. + */ +public class DynamicInfoWindowAdapterActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final LatLng PARIS = new LatLng(48.864716, 2.349014); + + private MapboxMap mapboxMap; + private MapView mapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_infowindow_adapter); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + + // Add info window adapter + addCustomInfoWindowAdapter(mapboxMap); + + // Keep info windows open on click + mapboxMap.getUiSettings().setDeselectMarkersOnTap(false); + + // Add a marker + final MarkerView marker = addMarker(mapboxMap); + mapboxMap.selectMarker(marker); + + // On map click, change the info window contents + mapboxMap.setOnMapClickListener(point -> { + // Distance from click to marker + double distanceKm = marker.getPosition().distanceTo(point) / 1000; + + // Get the info window + final InfoWindow infoWindow = marker.getInfoWindow(); + + // Get the view from the info window + if (infoWindow != null && infoWindow.getView() != null) { + // Set the new text on the text view in the info window + TextView textView = (TextView) infoWindow.getView(); + textView.setText(String.format(Locale.getDefault(), "%.2fkm", distanceKm)); + textView.post(() -> { + // Update the info window position (as the text length changes) + infoWindow.update(); + }); + } + }); + + // Focus on Paris + mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(PARIS)); + } + + private MarkerView addMarker(MapboxMap mapboxMap) { + return mapboxMap.addMarker( + new MarkerViewOptions() + .position(PARIS) + .icon(IconUtils.drawableToIcon(this, R.drawable.ic_location_city, + ResourcesCompat.getColor(getResources(), R.color.mapbox_blue, getTheme())) + )); + } + + private void addCustomInfoWindowAdapter(final MapboxMap mapboxMap) { + final int padding = (int) getResources().getDimension(R.dimen.attr_margin); + mapboxMap.setInfoWindowAdapter(marker -> { + TextView textView = new TextView(DynamicInfoWindowAdapterActivity.this); + textView.setText(marker.getTitle()); + textView.setBackgroundColor(Color.WHITE); + textView.setText(R.string.action_calculate_distance); + textView.setPadding(padding, padding, padding, padding); + return textView; + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowActivity.java new file mode 100644 index 0000000000..1a391598cc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowActivity.java @@ -0,0 +1,190 @@ +package com.mapbox.mapboxsdk.maps.activity.infowindow; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +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.mapboxsdk.testapp.R; + +import java.text.DecimalFormat; + +/** + * Test activity showcasing using the InfoWindow API above Washington D.C. + * <p> + * Allows to test mulitple concurrently open InfoWindows. + * </p> + */ +public class InfoWindowActivity extends AppCompatActivity + implements OnMapReadyCallback, MapboxMap.OnInfoWindowCloseListener, MapboxMap.OnMapLongClickListener, + MapboxMap.OnInfoWindowClickListener, MapboxMap.OnInfoWindowLongClickListener { + + private MapboxMap mapboxMap; + private MapView mapView; + private Marker customMarker; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_infowindow); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + addMarkers(); + addInfoWindowListeners(); + } + + private void addMarkers() { + mapboxMap.addMarker(new MarkerOptions() + .title("Intersection") + .snippet("H St NW with 15th St NW") + .position(new LatLng(38.9002073, -77.03364419))); + + mapboxMap.addMarker(new MarkerOptions().title("Intersection") + .snippet("E St NW with 17th St NW") + .position(new LatLng(38.8954236, -77.0394623))); + + mapboxMap.addMarker(new MarkerOptions().title("The Ellipse").position(new LatLng(38.89393, -77.03654))); + + mapboxMap.addMarker(new MarkerOptions().position(new LatLng(38.89596, -77.03434))); + + mapboxMap.addMarker(new MarkerOptions().snippet("Lafayette Square").position(new LatLng(38.89949, -77.03656))); + + Marker marker = mapboxMap.addMarker(new MarkerOptions() + .title("White House") + .snippet("The official residence and principal workplace of the President of the United States, " + + "located at 1600 Pennsylvania Avenue NW in Washington, D.C. It has been the residence of every" + + "U.S. president since John Adams in 1800.") + .position(new LatLng(38.897705003219784, -77.03655168667463))); + + // open InfoWindow at startup + mapboxMap.selectMarker(marker); + } + + private void addInfoWindowListeners() { + mapboxMap.setOnInfoWindowCloseListener(this); + mapboxMap.setOnMapLongClickListener(this); + mapboxMap.setOnInfoWindowClickListener(this); + mapboxMap.setOnInfoWindowLongClickListener(this); + } + + private void toggleConcurrentInfoWindow(boolean allowConcurrentInfoWindow) { + mapboxMap.deselectMarkers(); + mapboxMap.setAllowConcurrentMultipleOpenInfoWindows(allowConcurrentInfoWindow); + } + + private void toggleDeselectMarkersOnTap(boolean deselectMarkersOnTap) { + mapboxMap.getUiSettings().setDeselectMarkersOnTap(deselectMarkersOnTap); + } + + @Override + public boolean onInfoWindowClick(@NonNull Marker marker) { + Toast.makeText(getApplicationContext(), "OnClick: " + marker.getTitle(), Toast.LENGTH_LONG).show(); + // returning true will leave the info window open + return false; + } + + @Override + public void onInfoWindowClose(Marker marker) { + Toast.makeText(getApplicationContext(), "OnClose: " + marker.getTitle(), Toast.LENGTH_LONG).show(); + } + + @Override + public void onInfoWindowLongClick(Marker marker) { + Toast.makeText(getApplicationContext(), "OnLongClick: " + marker.getTitle(), Toast.LENGTH_LONG).show(); + } + + @Override + public void onMapLongClick(@NonNull LatLng point) { + if (customMarker != null) { + // Remove previous added marker + mapboxMap.removeAnnotation(customMarker); + customMarker = null; + } + + // Add marker on long click location with default marker image + customMarker = mapboxMap.addMarker(new MarkerOptions() + .title("Custom Marker") + .snippet(new DecimalFormat("#.#####").format(point.getLatitude()) + ", " + + new DecimalFormat("#.#####").format(point.getLongitude())) + .position(point)); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_infowindow, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_toggle_concurrent_infowindow: + toggleConcurrentInfoWindow(!item.isChecked()); + item.setChecked(!item.isChecked()); + return true; + case R.id.action_toggle_deselect_markers_on_tap: + toggleDeselectMarkersOnTap(!item.isChecked()); + item.setChecked(!item.isChecked()); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowAdapterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowAdapterActivity.java new file mode 100644 index 0000000000..f5e866ada9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/infowindow/InfoWindowAdapterActivity.java @@ -0,0 +1,125 @@ +package com.mapbox.mapboxsdk.maps.activity.infowindow; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.annotations.Icon; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarker; +import com.mapbox.mapboxsdk.testapp.model.annotations.CityStateMarkerOptions; +import com.mapbox.mapboxsdk.maps.utils.IconUtils; + +/** + * Test activity showcasing using an InfoWindowAdapter to provide a custom InfoWindow content. + */ +public class InfoWindowAdapterActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_infowindow_adapter); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + addMarkers(); + addCustomInfoWindowAdapter(); + }); + } + + private void addMarkers() { + mapboxMap.addMarker(generateCityStateMarker("Andorra", 42.505777, 1.52529, "#F44336")); + mapboxMap.addMarker(generateCityStateMarker("Luxembourg", 49.815273, 6.129583, "#3F51B5")); + mapboxMap.addMarker(generateCityStateMarker("Monaco", 43.738418, 7.424616, "#673AB7")); + mapboxMap.addMarker(generateCityStateMarker("Vatican City", 41.902916, 12.453389, "#009688")); + mapboxMap.addMarker(generateCityStateMarker("San Marino", 43.942360, 12.457777, "#795548")); + mapboxMap.addMarker(generateCityStateMarker("Liechtenstein", 47.166000, 9.555373, "#FF5722")); + } + + private CityStateMarkerOptions generateCityStateMarker(String title, double lat, double lng, String color) { + CityStateMarkerOptions marker = new CityStateMarkerOptions(); + marker.title(title); + marker.position(new LatLng(lat, lng)); + marker.infoWindowBackground(color); + + Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_location_city, Color.parseColor(color)); + marker.icon(icon); + return marker; + } + + private void addCustomInfoWindowAdapter() { + mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() { + + private int tenDp = (int) getResources().getDimension(R.dimen.attr_margin); + + @Override + public View getInfoWindow(@NonNull Marker marker) { + TextView textView = new TextView(InfoWindowAdapterActivity.this); + textView.setText(marker.getTitle()); + textView.setTextColor(Color.WHITE); + + if (marker instanceof CityStateMarker) { + CityStateMarker cityStateMarker = (CityStateMarker) marker; + textView.setBackgroundColor(Color.parseColor(cityStateMarker.getInfoWindowBackgroundColor())); + } + + textView.setPadding(tenDp, tenDp, tenDp, tenDp); + return textView; + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/BottomSheetActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/BottomSheetActivity.java new file mode 100644 index 0000000000..f076197c08 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/BottomSheetActivity.java @@ -0,0 +1,273 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.BottomSheetBehavior; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +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.MapboxMapOptions; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.utils.MapFragmentUtils; + +/** + * Test activity showcasing using a bottomView with a MapView and stacking map fragments below. + */ +public class BottomSheetActivity extends AppCompatActivity { + + private static final String TAG_MAIN_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.main"; + private static final String TAG_BOTTOM_FRAGMENT = "com.mapbox.mapboxsdk.fragment.tag.bottom"; + + private boolean bottomSheetFragmentAdded; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_bottom_sheet); + + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + + findViewById(R.id.fabFragment).setOnClickListener(v -> addMapFragment()); + + findViewById(R.id.fabBottomSheet).setOnClickListener(v -> toggleBottomSheetMapFragment()); + + BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.bottom_sheet)); + bottomSheetBehavior.setPeekHeight((int) (64 * getResources().getDisplayMetrics().density)); + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + toggleBottomSheetMapFragment(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + onBackPressed(); + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onBackPressed() { + FragmentManager fragmentManager = getSupportFragmentManager(); + + if (fragmentManager.getBackStackEntryCount() > 0) { + fragmentManager.popBackStack(); + } else { + super.onBackPressed(); + } + } + + private void addMapFragment() { + FragmentManager fragmentManager = getSupportFragmentManager(); + int fragmentCount = fragmentManager.getBackStackEntryCount(); + + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + MainMapFragment mainMapFragment = MainMapFragment.newInstance(fragmentCount); + if (fragmentCount == 0) { + fragmentTransaction.add(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT); + } else { + fragmentTransaction.replace(R.id.fragment_container, mainMapFragment, TAG_MAIN_FRAGMENT); + } + fragmentTransaction.addToBackStack(String.valueOf(mainMapFragment.hashCode())); + fragmentTransaction.commit(); + Toast.makeText(this, "Amount of main map fragments: " + (fragmentCount + 1), Toast.LENGTH_SHORT).show(); + } + + private void toggleBottomSheetMapFragment() { + if (!bottomSheetFragmentAdded) { + addBottomSheetMapFragment(); + } else { + removeBottomSheetFragment(); + } + bottomSheetFragmentAdded = !bottomSheetFragmentAdded; + } + + private void addBottomSheetMapFragment() { + FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); + fragmentTransaction.add(R.id.fragment_container_bottom, BottomSheetFragment.newInstance(), TAG_BOTTOM_FRAGMENT); + fragmentTransaction.commit(); + } + + private void removeBottomSheetFragment() { + Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_BOTTOM_FRAGMENT); + if (fragment != null) { + getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + } + } + + public static class MainMapFragment extends Fragment implements OnMapReadyCallback { + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.SATELLITE_STREETS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE, + Style.OUTDOORS + }; + + private MapView map; + + public static MainMapFragment newInstance(int mapCounter) { + MainMapFragment mapFragment = new MainMapFragment(); + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.styleUrl(STYLES[Math.min(Math.max(mapCounter, 0), STYLES.length - 1)]); + mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions)); + return mapFragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + Context context = inflater.getContext(); + return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments())); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + map.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.760545, -122.436055), 15)); + } + + @Override + public void onStart() { + super.onStart(); + map.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + } + + public static class BottomSheetFragment extends Fragment implements OnMapReadyCallback { + + private MapView map; + + public static BottomSheetFragment newInstance() { + BottomSheetFragment mapFragment = new BottomSheetFragment(); + MapboxMapOptions mapboxMapOptions = new MapboxMapOptions(); + mapboxMapOptions.renderSurfaceOnTop(true); + mapboxMapOptions.styleUrl(Style.LIGHT); + mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions)); + return mapFragment; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + Context context = inflater.getContext(); + return map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments())); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + map.onCreate(savedInstanceState); + map.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.760545, -122.436055), 15)); + } + + @Override + public void onStart() { + super.onStart(); + map.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + map.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + map.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + map.onStop(); + } + + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + map.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + map.onLowMemory(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + map.onDestroy(); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DebugModeActivity.java new file mode 100644 index 0000000000..ca29c3dd81 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DebugModeActivity.java @@ -0,0 +1,271 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.http.HttpRequestUtil; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.List; +import java.util.Locale; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +/** + * Test activity showcasing the different debug modes and allows to cycle between the default map styles. + */ +public class DebugModeActivity extends AppCompatActivity implements OnMapReadyCallback { + + private MapView mapView; + private MapboxMap mapboxMap; + private ActionBarDrawerToggle actionBarDrawerToggle; + private int currentStyleIndex = 0; + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.OUTDOORS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE, + Style.SATELLITE_STREETS, + Style.TRAFFIC_DAY, + Style.TRAFFIC_NIGHT + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + HttpRequestUtil.setPrintRequestUrlOnFailure(true); + setContentView(R.layout.activity_debug_mode); + setupToolbar(); + setupMapView(savedInstanceState); + setupDebugChangeView(); + setupStyleChangeView(); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + actionBarDrawerToggle = new ActionBarDrawerToggle(this, + drawerLayout, + R.string.navigation_drawer_open, + R.string.navigation_drawer_close + ); + actionBarDrawerToggle.setDrawerIndicatorEnabled(true); + actionBarDrawerToggle.syncState(); + } + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.addOnMapChangedListener(change -> { + if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) { + Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson()); + setupNavigationView(mapboxMap.getLayers()); + } + }); + + mapView.setTag(true); + mapView.setStyleUrl(STYLES[currentStyleIndex]); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.getUiSettings().setZoomControlsEnabled(true); + + setupNavigationView(mapboxMap.getLayers()); + setupZoomView(); + setFpsView(); + } + + private void setFpsView() { + final TextView fpsView = (TextView) findViewById(R.id.fpsView); + mapboxMap.setOnFpsChangedListener(fps -> + fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps)) + ); + } + + private void setupNavigationView(List<Layer> layerList) { + final LayerListAdapter adapter = new LayerListAdapter(this, layerList); + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter); + listView.setOnItemClickListener((parent, view, position, id) -> { + Layer clickedLayer = adapter.getItem(position); + toggleLayerVisibility(clickedLayer); + closeNavigationView(); + }); + } + + private void toggleLayerVisibility(Layer layer) { + boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE); + layer.setProperties( + visibility( + isVisible ? Property.NONE : Property.VISIBLE + ) + ); + } + + private void closeNavigationView() { + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + drawerLayout.closeDrawers(); + } + + private void setupZoomView() { + final TextView textView = (TextView) findViewById(R.id.textZoom); + mapboxMap.setOnCameraChangeListener(position -> + textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)) + ); + } + + private void setupDebugChangeView() { + FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug); + fabDebug.setOnClickListener(view -> { + if (mapboxMap != null) { + Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive()); + mapboxMap.cycleDebugOptions(); + } + }); + } + + private void setupStyleChangeView() { + FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles); + fabStyles.setOnClickListener(view -> { + if (mapboxMap != null) { + currentStyleIndex++; + if (currentStyleIndex == STYLES.length) { + currentStyleIndex = 0; + } + mapboxMap.setStyleUrl(STYLES[currentStyleIndex], style -> Timber.d("Style loaded %s", style)); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + HttpRequestUtil.setPrintRequestUrlOnFailure(false); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + private static class LayerListAdapter extends BaseAdapter { + + private LayoutInflater layoutInflater; + private List<Layer> layers; + + LayerListAdapter(Context context, List<Layer> layers) { + this.layoutInflater = LayoutInflater.from(context); + this.layers = layers; + } + + @Override + public int getCount() { + return layers.size(); + } + + @Override + public Layer getItem(int position) { + return layers.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Layer layer = layers.get(position); + View view = convertView; + if (view == null) { + view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false); + ViewHolder holder = new ViewHolder( + (TextView) view.findViewById(android.R.id.text1), + (TextView) view.findViewById(android.R.id.text2) + ); + view.setTag(holder); + } + ViewHolder holder = (ViewHolder) view.getTag(); + holder.text.setText(layer.getClass().getSimpleName()); + holder.subText.setText(layer.getId()); + return view; + } + + private static class ViewHolder { + final TextView text; + final TextView subText; + + ViewHolder(TextView text, TextView subText) { + this.text = text; + this.subText = subText; + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DoubleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DoubleMapActivity.java new file mode 100644 index 0000000000..c4879c08cb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/DoubleMapActivity.java @@ -0,0 +1,155 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.UiSettings; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing having 2 maps on top of each other. + * <p> + * The small map is using the `mapbox_enableZMediaOverlay="true"` configuration + * </p> + */ +public class DoubleMapActivity extends AppCompatActivity { + + private static final String TAG_FRAGMENT = "map"; + + // used for ui tests + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_fragment); + + if (savedInstanceState == null) { + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + transaction.add(R.id.fragment_container, new DoubleMapFragment(), TAG_FRAGMENT); + transaction.commit(); + } + } + + public void setMapboxMap(MapboxMap map) { + // we need to set mapboxmap on the parent activity, + // for auto-generated ui tests + mapboxMap = map; + mapboxMap.setStyleUrl(Style.DARK); + mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(18)); + } + + /** + * Custom fragment containing 2 MapViews. + */ + public static class DoubleMapFragment extends Fragment { + + private DoubleMapActivity activity; + private MapView mapView; + private MapView mapViewMini; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + activity = (DoubleMapActivity) context; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_double_map, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + // MapView large + mapView = (MapView) view.findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + if (activity != null) { + activity.setMapboxMap(mapboxMap); + } + }); + + // MapView mini + mapViewMini = (MapView) view.findViewById(R.id.mini_map); + mapViewMini.onCreate(savedInstanceState); + mapViewMini.getMapAsync(mapboxMap -> { + mapboxMap.setStyleUrl(Style.LIGHT); + mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(4)); + + UiSettings uiSettings = mapboxMap.getUiSettings(); + uiSettings.setAllGesturesEnabled(false); + uiSettings.setCompassEnabled(false); + uiSettings.setAttributionEnabled(false); + uiSettings.setLogoEnabled(false); + + mapboxMap.setOnMapClickListener(point -> { + // test if we can open 2 activities after each other + startActivity(new Intent(mapViewMini.getContext(), DoubleMapActivity.class)); + }); + }); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + mapViewMini.onResume(); + } + + @Override + public void onStart() { + super.onStart(); + mapView.onStart(); + mapViewMini.onStart(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + mapViewMini.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mapView.onStop(); + mapViewMini.onStop(); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + mapView.onDestroy(); + mapViewMini.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + mapViewMini.onLowMemory(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + mapViewMini.onSaveInstanceState(outState); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LatLngBoundsForCameraActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LatLngBoundsForCameraActivity.java new file mode 100644 index 0000000000..0afa67deef --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LatLngBoundsForCameraActivity.java @@ -0,0 +1,109 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.View; +import android.widget.FrameLayout; + +import com.mapbox.mapboxsdk.annotations.PolygonOptions; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing restricting user gestures to a bounds around Iceland. + */ +public class LatLngBoundsForCameraActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final LatLngBounds ICELAND_BOUNDS = new LatLngBounds.Builder() + .include(new LatLng(66.852863, -25.985652)) + .include(new LatLng(62.985661, -12.626277)) + .build(); + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_restricted_bounds); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + mapboxMap.setLatLngBoundsForCameraTarget(ICELAND_BOUNDS); + mapboxMap.setMinZoomPreference(2); + showBoundsArea(); + showCrosshair(); + } + + private void showBoundsArea() { + PolygonOptions boundsArea = new PolygonOptions() + .add(ICELAND_BOUNDS.getNorthWest()) + .add(ICELAND_BOUNDS.getNorthEast()) + .add(ICELAND_BOUNDS.getSouthEast()) + .add(ICELAND_BOUNDS.getSouthWest()); + boundsArea.alpha(0.25f); + boundsArea.fillColor(Color.RED); + mapboxMap.addPolygon(boundsArea); + } + + private void showCrosshair() { + View crosshair = new View(this); + crosshair.setLayoutParams(new FrameLayout.LayoutParams(10, 10, Gravity.CENTER)); + crosshair.setBackgroundColor(Color.BLUE); + mapView.addView(crosshair); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LocalGlyphActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LocalGlyphActivity.java new file mode 100644 index 0000000000..85d08fcd8b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/LocalGlyphActivity.java @@ -0,0 +1,85 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +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.mapboxsdk.testapp.R; + +/** + * Test activity that displays the city of Suzhou with a mixture of server-generated + * latin glyphs and CJK glyphs generated locally using "Droid Sans" as a font family. + */ +public class LocalGlyphActivity extends AppCompatActivity { + private MapView mapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_local_glyph); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(new OnMapReadyCallback() { + @Override + public void onMapReady(@NonNull MapboxMap mapboxMap) { + // Set initial position to Suzhou + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(new LatLng(31.3003, 120.7457)) + .zoom(11) + .bearing(0) + .tilt(0) + .build())); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapChangeActivity.java new file mode 100644 index 0000000000..4c957e6bfe --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapChangeActivity.java @@ -0,0 +1,102 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.os.Bundle; +import android.support.v4.util.LongSparseArray; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +import timber.log.Timber; + +/** + * Test activity showcasing how to listen to map change events. + */ +public class MapChangeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_simple); + + final LongSparseArray<String> mapChangeMap = buildMapChangeStringValueSparseArray(); + mapView = (MapView) findViewById(R.id.mapView); + mapView.addOnMapChangedListener(change -> Timber.e("OnMapChange: %s, %s", change, mapChangeMap.get(change))); + + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(55.754020, 37.620948), 12), 9000); + }); + } + + private LongSparseArray<String> buildMapChangeStringValueSparseArray() { + LongSparseArray<String> mapChangeArray = new LongSparseArray<>(); + mapChangeArray.put(MapView.REGION_WILL_CHANGE, "Region will change"); + mapChangeArray.put(MapView.REGION_WILL_CHANGE_ANIMATED, "Region will change animated"); + mapChangeArray.put(MapView.REGION_IS_CHANGING, "Region is changing"); + mapChangeArray.put(MapView.REGION_DID_CHANGE, "Region did change"); + mapChangeArray.put(MapView.REGION_DID_CHANGE_ANIMATED, "Region did change animated"); + mapChangeArray.put(MapView.WILL_START_LOADING_MAP, "Will start loading map"); + mapChangeArray.put(MapView.DID_FINISH_LOADING_MAP, "Did finish loading map"); + mapChangeArray.put(MapView.DID_FAIL_LOADING_MAP, "Did fail loading map"); + mapChangeArray.put(MapView.WILL_START_RENDERING_FRAME, "Will start rendering frame"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME, "Did finish rendering frame"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_FRAME_FULLY_RENDERED, "Did finish rendering frame fully rendered"); + mapChangeArray.put(MapView.WILL_START_RENDERING_MAP, "Will start rendering map"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP, "Did finish rendering map"); + mapChangeArray.put(MapView.DID_FINISH_RENDERING_MAP_FULLY_RENDERED, "Did finish rendering map fully rendered"); + mapChangeArray.put(MapView.DID_FINISH_LOADING_STYLE, "Did finish loading style"); + mapChangeArray.put(MapView.SOURCE_DID_CHANGE, "Source did change"); + return mapChangeArray; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapInDialogActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapInDialogActivity.java new file mode 100644 index 0000000000..4fa33dc60c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapInDialogActivity.java @@ -0,0 +1,119 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.app.Dialog; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentManager; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing showing a Map inside of a DialogFragment. + * <p> + * Uses the deprecated TextureView API to workaround the issue of seeing a grey background before the gl surface. + * </p> + */ +public class MapInDialogActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_in_dialog); + + Button button = (Button) findViewById(R.id.button_open_dialog); + button.setOnClickListener(view -> { + FragmentManager fm = getSupportFragmentManager(); + MapDialogFragment editNameDialogFragment = MapDialogFragment.newInstance("Map Dialog"); + editNameDialogFragment.show(fm, "fragment_dialog_map"); + }); + } + + public static class MapDialogFragment extends DialogFragment { + + private MapView mapView; + + public MapDialogFragment() { + } + + public static MapDialogFragment newInstance(String title) { + MapDialogFragment frag = new MapDialogFragment(); + Bundle args = new Bundle(); + args.putString("title", title); + frag.setArguments(args); + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_dialog_map, container); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mapView = (MapView) view.findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + } + + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new Dialog(getActivity(), getTheme()) { + boolean destroyed = false; + @Override + public void dismiss() { + if (mapView != null && !destroyed) { + mapView.onDestroy(); + destroyed = true; + } + super.dismiss(); + } + }; + } + + @Override + public void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + public void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapPaddingActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapPaddingActivity.java new file mode 100644 index 0000000000..09632f55ec --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/MapPaddingActivity.java @@ -0,0 +1,123 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.mapbox.mapboxsdk.annotations.MarkerOptions; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using the map padding API. + */ +public class MapPaddingActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_padding); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.setTag(true); + mapView.onCreate(savedInstanceState); + + mapView.getMapAsync(mapboxMap -> { + MapPaddingActivity.this.mapboxMap = mapboxMap; + + int paddingLeft = (int) getResources().getDimension(R.dimen.map_padding_left); + int paddingBottom = (int) getResources().getDimension(R.dimen.map_padding_bottom); + int paddingRight = (int) getResources().getDimension(R.dimen.map_padding_right); + int paddingTop = (int) getResources().getDimension(R.dimen.map_padding_top); + mapboxMap.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); + + moveToBangalore(); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_padding, menu); + return true; + } + + private void moveToBangalore() { + LatLng bangalore = new LatLng(12.9810816, 77.6368034); + CameraPosition cameraPosition = new CameraPosition.Builder() + .zoom(16) + .target(bangalore) + .bearing(40) + .tilt(45) + .build(); + + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); + mapboxMap.addMarker(new MarkerOptions().title("Center map").position(bangalore)); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + + case R.id.action_bangalore: + if (mapboxMap != null) { + moveToBangalore(); + } + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/SimpleMapActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/SimpleMapActivity.java new file mode 100644 index 0000000000..24cb56925c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/SimpleMapActivity.java @@ -0,0 +1,66 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing a simple MapView without any MapboxMap interaction. + */ +public class SimpleMapActivity extends AppCompatActivity { + + private MapView mapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_simple); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/VisibilityChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/VisibilityChangeActivity.java new file mode 100644 index 0000000000..35239a96ea --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/maplayout/VisibilityChangeActivity.java @@ -0,0 +1,142 @@ +package com.mapbox.mapboxsdk.maps.activity.maplayout; + +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing visibility changes to the mapview. + */ +public class VisibilityChangeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + private Handler handler = new Handler(); + private Runnable runnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_visibility); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(55.754020, 37.620948), 12), 9000); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + handler.post(runnable = new VisibilityRunner(mapView, findViewById(R.id.viewParent), handler)); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + private static class VisibilityRunner implements Runnable { + + private MapView mapView; + private View viewParent; + private Handler handler; + private int currentStep; + + VisibilityRunner(MapView mapView, View viewParent, Handler handler) { + this.mapView = mapView; + this.viewParent = viewParent; + this.handler = handler; + } + + @Override + public void run() { + if (isViewHiearchyReady()) { + if (isEvenStep()) { + viewParent.setVisibility(View.VISIBLE); + mapView.setVisibility(View.VISIBLE); + } else if (isFirstOrThirdStep()) { + mapView.setVisibility(getVisibilityForStep()); + } else if (isFifthOrSeventhStep()) { + viewParent.setVisibility(getVisibilityForStep()); + } + updateStep(); + } + handler.postDelayed(this, 1500); + } + + private void updateStep() { + if (currentStep == 7) { + currentStep = 0; + } else { + currentStep++; + } + } + + private int getVisibilityForStep() { + return (currentStep == 1 || currentStep == 5) ? View.GONE : View.INVISIBLE; + } + + private boolean isFifthOrSeventhStep() { + return currentStep == 5 || currentStep == 7; + } + + private boolean isFirstOrThirdStep() { + return currentStep == 1 || currentStep == 3; + } + + private boolean isEvenStep() { + return currentStep == 0 || currentStep % 2 == 0; + } + + private boolean isViewHiearchyReady() { + return mapView != null && viewParent != null; + } + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + if (runnable != null) { + handler.removeCallbacks(runnable); + runnable = null; + } + mapView.onStop(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/DeleteRegionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/DeleteRegionActivity.java new file mode 100644 index 0000000000..389a483ce2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/DeleteRegionActivity.java @@ -0,0 +1,157 @@ +package com.mapbox.mapboxsdk.maps.activity.offline; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.offline.OfflineManager; +import com.mapbox.mapboxsdk.offline.OfflineRegion; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.OfflineUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Test activity showing integration of deleting an OfflineRegion. + */ +public class DeleteRegionActivity extends AppCompatActivity implements AdapterView.OnItemClickListener { + + private OfflineRegionAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_offline_region_delete); + + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter = new OfflineRegionAdapter(this)); + listView.setEmptyView(findViewById(android.R.id.empty)); + listView.setOnItemClickListener(this); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final OfflineRegion region = adapter.getItem(position); + String metadata = OfflineUtils.convertRegionName(region.getMetadata()); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Delete region"); + + final TextView input = new TextView(this); + input.setText(metadata); + builder.setView(input); + + builder.setPositiveButton("OK", (dialog, which) -> delete(region)); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + + builder.show(); + } + + private void delete(OfflineRegion region) { + region.delete(new OfflineRegion.OfflineRegionDeleteCallback() { + @Override + public void onDelete() { + Toast.makeText( + DeleteRegionActivity.this, + "Region deleted", + Toast.LENGTH_SHORT + ).show(); + loadOfflineRegions(); + } + + @Override + public void onError(String error) { + Toast.makeText( + DeleteRegionActivity.this, + "Region deletion failed with " + error, + Toast.LENGTH_LONG + ).show(); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + loadOfflineRegions(); + } + + private void loadOfflineRegions() { + OfflineManager.getInstance(this).listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() { + @Override + public void onList(OfflineRegion[] offlineRegions) { + if (offlineRegions != null && offlineRegions.length > 0) { + adapter.setOfflineRegions(Arrays.asList(offlineRegions)); + } + } + + @Override + public void onError(String error) { + Toast.makeText(DeleteRegionActivity.this, "Error loading regions " + error, Toast.LENGTH_LONG).show(); + } + }); + } + + private static class OfflineRegionAdapter extends BaseAdapter { + + private Context context; + private List<OfflineRegion> offlineRegions; + + OfflineRegionAdapter(Context ctx) { + context = ctx; + offlineRegions = new ArrayList<>(); + } + + void setOfflineRegions(List<OfflineRegion> offlineRegions) { + this.offlineRegions = offlineRegions; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return offlineRegions.size(); + } + + @Override + public OfflineRegion getItem(int position) { + return offlineRegions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + + if (convertView == null) { + holder = new ViewHolder(); + convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false); + holder.text = (TextView) convertView.findViewById(android.R.id.text1); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + holder.text.setText(OfflineUtils.convertRegionName(getItem(position).getMetadata())); + return convertView; + } + + static class ViewHolder { + TextView text; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/OfflineActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/OfflineActivity.java new file mode 100644 index 0000000000..ebce41b102 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/OfflineActivity.java @@ -0,0 +1,321 @@ +package com.mapbox.mapboxsdk.maps.activity.offline; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.TextUtils; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.Mapbox; +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.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.offline.OfflineManager; +import com.mapbox.mapboxsdk.offline.OfflineRegion; +import com.mapbox.mapboxsdk.offline.OfflineRegionError; +import com.mapbox.mapboxsdk.offline.OfflineRegionStatus; +import com.mapbox.mapboxsdk.offline.OfflineTilePyramidRegionDefinition; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.testapp.model.other.OfflineDownloadRegionDialog; +import com.mapbox.mapboxsdk.testapp.model.other.OfflineListRegionsDialog; +import com.mapbox.mapboxsdk.maps.utils.OfflineUtils; + +import java.util.ArrayList; + +import timber.log.Timber; + +/** + * Test activity showcasing the Offline API. + * <p> + * Shows a map of Manhattan and allows the user to download and name a region. + * </p> + */ +public class OfflineActivity extends AppCompatActivity + implements OfflineDownloadRegionDialog.DownloadRegionDialogListener { + + // JSON encoding/decoding + public static final String JSON_CHARSET = "UTF-8"; + public static final String JSON_FIELD_REGION_NAME = "FIELD_REGION_NAME"; + + // Style URL + public static final String STYLE_URL = Style.MAPBOX_STREETS; + + /* + * UI elements + */ + private MapView mapView; + private MapboxMap mapboxMap; + private ProgressBar progressBar; + private Button downloadRegion; + private Button listRegions; + + private boolean isEndNotified; + + /* + * Offline objects + */ + private OfflineManager offlineManager; + private OfflineRegion offlineRegion; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_offline); + + // You can use Mapbox.setConnected(Boolean) to manually set the connectivity + // state of your app. This will override any checks performed via the ConnectivityManager. + // Mapbox.getInstance().setConnected(false); + Boolean connected = Mapbox.isConnected(); + Timber.d("Mapbox is connected: %s", connected); + + // Set up map + mapView = (MapView) findViewById(R.id.mapView); + mapView.setStyleUrl(STYLE_URL); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + Timber.d("Map is ready"); + OfflineActivity.this.mapboxMap = mapboxMap; + + // Set initial position to UNHQ in NYC + mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(new LatLng(40.749851, -73.967966)) + .zoom(14) + .bearing(0) + .tilt(0) + .build())); + }); + + // The progress bar + progressBar = (ProgressBar) findViewById(R.id.progress_bar); + + // Set up button listeners + downloadRegion = (Button) findViewById(R.id.button_download_region); + downloadRegion.setOnClickListener(view -> handleDownloadRegion()); + + listRegions = (Button) findViewById(R.id.button_list_regions); + listRegions.setOnClickListener(view -> handleListRegions()); + + // Set up the OfflineManager + offlineManager = OfflineManager.getInstance(this); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + + /* + * Buttons logic + */ + private void handleDownloadRegion() { + Timber.d("handleDownloadRegion"); + + // Show dialog + OfflineDownloadRegionDialog offlineDownloadRegionDialog = new OfflineDownloadRegionDialog(); + offlineDownloadRegionDialog.show(getSupportFragmentManager(), "download"); + } + + private void handleListRegions() { + Timber.d("handleListRegions"); + + // Query the DB asynchronously + offlineManager.listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() { + @Override + public void onList(OfflineRegion[] offlineRegions) { + // Check result + if (offlineRegions == null || offlineRegions.length == 0) { + Toast.makeText(OfflineActivity.this, "You have no regions yet.", Toast.LENGTH_SHORT).show(); + return; + } + + // Get regions info + ArrayList<String> offlineRegionsNames = new ArrayList<>(); + for (OfflineRegion offlineRegion : offlineRegions) { + offlineRegionsNames.add(OfflineUtils.convertRegionName(offlineRegion.getMetadata())); + } + + // Create args + Bundle args = new Bundle(); + args.putStringArrayList(OfflineListRegionsDialog.ITEMS, offlineRegionsNames); + + // Show dialog + OfflineListRegionsDialog offlineListRegionsDialog = new OfflineListRegionsDialog(); + offlineListRegionsDialog.setArguments(args); + offlineListRegionsDialog.show(getSupportFragmentManager(), "list"); + } + + @Override + public void onError(String error) { + Timber.e("Error: %s" , error); + } + }); + } + + /* + * Dialogs + */ + @Override + public void onDownloadRegionDialogPositiveClick(final String regionName) { + if (TextUtils.isEmpty(regionName)) { + Toast.makeText(OfflineActivity.this, "Region name cannot be empty.", Toast.LENGTH_SHORT).show(); + return; + } + + // Start progress bar + Timber.d("Download started: %s", regionName); + startProgress(); + + // Definition + LatLngBounds bounds = mapboxMap.getProjection().getVisibleRegion().latLngBounds; + double minZoom = mapboxMap.getCameraPosition().zoom; + double maxZoom = mapboxMap.getMaxZoomLevel(); + float pixelRatio = this.getResources().getDisplayMetrics().density; + OfflineTilePyramidRegionDefinition definition = new OfflineTilePyramidRegionDefinition( + STYLE_URL, bounds, minZoom, maxZoom, pixelRatio); + + // Sample way of encoding metadata from a JSONObject + byte[] metadata = OfflineUtils.convertRegionName(regionName); + + // Create region + offlineManager.createOfflineRegion(definition, metadata, new OfflineManager.CreateOfflineRegionCallback() { + @Override + public void onCreate(OfflineRegion offlineRegion) { + Timber.d("Offline region created: %s" , regionName); + OfflineActivity.this.offlineRegion = offlineRegion; + launchDownload(); + } + + @Override + public void onError(String error) { + Timber.e("Error: %s", error); + } + }); + } + + private void launchDownload() { + // Set an observer + offlineRegion.setObserver(new OfflineRegion.OfflineRegionObserver() { + @Override + public void onStatusChanged(OfflineRegionStatus status) { + // Compute a percentage + double percentage = status.getRequiredResourceCount() >= 0 + ? (100.0 * status.getCompletedResourceCount() / status.getRequiredResourceCount()) : + 0.0; + + if (status.isComplete()) { + // Download complete + endProgress("Region downloaded successfully."); + offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE); + offlineRegion.setObserver(null); + return; + } else if (status.isRequiredResourceCountPrecise()) { + // Switch to determinate state + setPercentage((int) Math.round(percentage)); + } + + // Debug + Timber.d("%s/%s resources; %s bytes downloaded.", + String.valueOf(status.getCompletedResourceCount()), + String.valueOf(status.getRequiredResourceCount()), + String.valueOf(status.getCompletedResourceSize())); + } + + @Override + public void onError(OfflineRegionError error) { + Timber.e("onError: %s, %s", error.getReason(), error.getMessage()); + offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE); + } + + @Override + public void mapboxTileCountLimitExceeded(long limit) { + Timber.e("Mapbox tile count limit exceeded: %s", limit); + offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE); + } + }); + + // Change the region state + offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE); + } + + /* + * Progress bar + */ + private void startProgress() { + // Disable buttons + downloadRegion.setEnabled(false); + listRegions.setEnabled(false); + + // Start and show the progress bar + isEndNotified = false; + progressBar.setIndeterminate(true); + progressBar.setVisibility(View.VISIBLE); + } + + private void setPercentage(final int percentage) { + progressBar.setIndeterminate(false); + progressBar.setProgress(percentage); + } + + private void endProgress(final String message) { + // Don't notify more than once + if (isEndNotified) { + return; + } + + // Enable buttons + downloadRegion.setEnabled(true); + listRegions.setEnabled(true); + + // Stop and hide the progress bar + isEndNotified = true; + progressBar.setIndeterminate(false); + progressBar.setVisibility(View.GONE); + + // Show a toast + Toast.makeText(OfflineActivity.this, message, Toast.LENGTH_LONG).show(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/UpdateMetadataActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/UpdateMetadataActivity.java new file mode 100644 index 0000000000..b6ed05ade0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/offline/UpdateMetadataActivity.java @@ -0,0 +1,184 @@ +package com.mapbox.mapboxsdk.maps.activity.offline; + +import android.content.Context; +import android.os.Bundle; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.text.InputType; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.offline.OfflineManager; +import com.mapbox.mapboxsdk.offline.OfflineRegion; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.OfflineUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Test activity showing integration of updating metadata of an OfflineRegion. + */ +public class UpdateMetadataActivity extends AppCompatActivity implements AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { + + private OfflineRegionMetadataAdapter adapter; + + private MapView mapView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_metadata_update); + + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter = new OfflineRegionMetadataAdapter(this)); + listView.setEmptyView(findViewById(android.R.id.empty)); + listView.setOnItemClickListener(this); + listView.setOnItemLongClickListener(this); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + final OfflineRegion region = adapter.getItem(position); + String metadata = OfflineUtils.convertRegionName(region.getMetadata()); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Rename metadata"); + + final EditText input = new EditText(this); + input.setText(metadata); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setSelection(metadata.length()); + builder.setView(input); + + builder.setPositiveButton("OK", (dialog, which) -> + updateMetadata(region, OfflineUtils.convertRegionName(input.getText().toString())) + ); + builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel()); + + builder.show(); + } + + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + ViewGroup container = (ViewGroup) findViewById(R.id.container); + container.removeAllViews(); + container.addView(mapView = new MapView(view.getContext())); + mapView.setOfflineRegionDefinition(adapter.getItem(position).getDefinition()); + mapView.onCreate(null); + mapView.onStart(); + mapView.onResume(); + return true; + } + + private void updateMetadata(OfflineRegion region, byte[] metadata) { + region.updateMetadata(metadata, new OfflineRegion.OfflineRegionUpdateMetadataCallback() { + @Override + public void onUpdate(byte[] metadata) { + adapter.notifyDataSetChanged(); + } + + @Override + public void onError(String error) { + Toast.makeText( + UpdateMetadataActivity.this, + "Region metadata update failed with " + error, + Toast.LENGTH_LONG + ).show(); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + loadOfflineRegions(); + } + + private void loadOfflineRegions() { + OfflineManager.getInstance(this).listOfflineRegions(new OfflineManager.ListOfflineRegionsCallback() { + @Override + public void onList(OfflineRegion[] offlineRegions) { + if (offlineRegions != null && offlineRegions.length > 0) { + adapter.setOfflineRegions(Arrays.asList(offlineRegions)); + } + } + + @Override + public void onError(String error) { + Toast.makeText(UpdateMetadataActivity.this, "Error loading regions " + error, Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (mapView != null) { + mapView.onPause(); + mapView.onStop(); + mapView.onDestroy(); + } + } + + private static class OfflineRegionMetadataAdapter extends BaseAdapter { + + private Context context; + private List<OfflineRegion> offlineRegions; + + OfflineRegionMetadataAdapter(Context ctx) { + context = ctx; + offlineRegions = new ArrayList<>(); + } + + void setOfflineRegions(List<OfflineRegion> offlineRegions) { + this.offlineRegions = offlineRegions; + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return offlineRegions.size(); + } + + @Override + public OfflineRegion getItem(int position) { + return offlineRegions.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ViewHolder holder; + + if (convertView == null) { + holder = new ViewHolder(); + convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1, parent, false); + holder.text = (TextView) convertView.findViewById(android.R.id.text1); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + + holder.text.setText(OfflineUtils.convertRegionName(getItem(position).getMetadata())); + return convertView; + } + + static class ViewHolder { + TextView text; + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestActivity.java new file mode 100644 index 0000000000..54e8ae602e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestActivity.java @@ -0,0 +1,365 @@ +package com.mapbox.mapboxsdk.maps.activity.render; + +import android.content.res.AssetManager; +import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Environment; +import android.support.annotation.Nullable; +import android.support.v7.app.AppCompatActivity; +import android.view.Gravity; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import okio.BufferedSource; +import okio.Okio; +import timber.log.Timber; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.WeakReference; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Activity that generates map snapshots based on the node render test suite. + */ +public class RenderTestActivity extends AppCompatActivity { + + private static final String TEST_BASE_PATH = "integration"; + private static final String RENDER_TEST_BASE_PATH = TEST_BASE_PATH + "/render-tests"; + + // We additionally read out excluded tests from `/platform/node/test/ignore.json` + private static final List<String> EXCLUDED_TESTS = new ArrayList<String>() { + { + add("overlay,background-opacity"); + add("collision-lines-pitched,debug"); + add("1024-circle,extent"); + add("empty,empty"); + add("rotation-alignment-map,icon-pitch-scaling"); + add("rotation-alignment-viewport,icon-pitch-scaling"); + add("pitch15,line-pitch"); + add("pitch30,line-pitch"); + add("line-placement-true-pitched,text-keep-upright"); + add("180,raster-rotation"); + add("45,raster-rotation"); + add("90,raster-rotation"); + add("overlapping,raster-masking"); + add("missing,raster-loading"); + add("pitchAndBearing,line-pitch"); + } + }; + + private final Map<RenderTestDefinition, Bitmap> renderResultMap = new HashMap<>(); + private List<RenderTestDefinition> renderTestDefinitions; + private OnRenderTestCompletionListener onRenderTestCompletionListener; + private MapSnapshotter mapSnapshotter; + private ImageView imageView; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(imageView = new ImageView(RenderTestActivity.this)); + imageView.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER) + ); + } + + @Override + protected void onStop() { + super.onStop(); + if (mapSnapshotter != null) { + mapSnapshotter.cancel(); + } + } + + // + // Loads the ignore tests from assets folder + // + private static class LoadRenderIgnoreTask extends AsyncTask<Void, Void, List<String>> { + + private WeakReference<RenderTestActivity> renderTestActivityWeakReference; + + LoadRenderIgnoreTask(RenderTestActivity renderTestActivity) { + this.renderTestActivityWeakReference = new WeakReference<>(renderTestActivity); + } + + @Override + protected List<String> doInBackground(Void... voids) { + return loadIgnoreList(renderTestActivityWeakReference.get().getAssets()); + } + + @Override + protected void onPostExecute(List<String> strings) { + super.onPostExecute(strings); + if (renderTestActivityWeakReference.get() != null) { + renderTestActivityWeakReference.get().onLoadIgnoreList(strings); + } + } + } + + + // + // Loads the render test definitions from assets folder + // + private static class LoadRenderDefinitionTask extends AsyncTask<Void, Void, List<RenderTestDefinition>> { + + private WeakReference<RenderTestActivity> renderTestActivityWeakReference; + + LoadRenderDefinitionTask(RenderTestActivity renderTestActivity) { + this.renderTestActivityWeakReference = new WeakReference<>(renderTestActivity); + } + + @Override + protected List<RenderTestDefinition> doInBackground(Void... voids) { + List<RenderTestDefinition> definitions = new ArrayList<>(); + AssetManager assetManager = renderTestActivityWeakReference.get().getAssets(); + String[] categories = new String[0]; + try { + categories = assetManager.list(RENDER_TEST_BASE_PATH); + } catch (IOException exception) { + Timber.e(exception); + } + for (int counter = categories.length - 1; counter >= 0; counter--) { + try { + String[] tests = assetManager.list(String.format("%s/%s", RENDER_TEST_BASE_PATH, categories[counter])); + for (String test : tests) { + String styleJson = loadStyleJson(assetManager, categories[counter], test); + RenderTestStyleDefinition renderTestStyleDefinition = new Gson() + .fromJson(styleJson, RenderTestStyleDefinition.class); + RenderTestDefinition definition = new RenderTestDefinition( + categories[counter], test, styleJson, renderTestStyleDefinition); + if (!definition.hasOperations()) { + if (!EXCLUDED_TESTS.contains(definition.getName() + "," + definition.getCategory())) { + definitions.add(definition); + } + } else { + Timber.e("could not add test, test requires operations: %s from %s", test, categories[counter]); + } + } + } catch (Exception exception) { + Timber.e(exception); + } + } + return definitions; + } + + @Override + protected void onPostExecute(List<RenderTestDefinition> renderTestDefinitions) { + super.onPostExecute(renderTestDefinitions); + RenderTestActivity renderTestActivity = renderTestActivityWeakReference.get(); + if (renderTestActivity != null) { + renderTestActivity.startRenderTests(renderTestDefinitions); + } + } + } + + private static List<String> loadIgnoreList(AssetManager assets) { + List<String> ignores = new ArrayList<>(); + try (InputStream input = assets.open(String.format("%s/ignores.json", TEST_BASE_PATH))) { + BufferedSource source = Okio.buffer(Okio.source(input)); + String styleJson = source.readByteString().string(Charset.forName("utf-8")); + JsonObject object = new Gson().fromJson(styleJson, JsonObject.class); + for (Map.Entry<String, JsonElement> stringJsonElementEntry : object.entrySet()) { + String[] parts = stringJsonElementEntry.getKey().split("/"); + ignores.add(String.format("%s,%s", parts[2], parts[1])); + } + } catch (IOException exception) { + Timber.e(exception); + } + return ignores; + } + + private static String loadStyleJson(AssetManager assets, String category, String test) { + String styleJson = null; + try (InputStream input = assets.open(String.format("%s/%s/%s/style.json", RENDER_TEST_BASE_PATH, category, test))) { + BufferedSource source = Okio.buffer(Okio.source(input)); + styleJson = source.readByteString().string(Charset.forName("utf-8")); + } catch (IOException exception) { + Timber.e(exception); + } + return styleJson; + } + + private void startRenderTests(List<RenderTestDefinition> renderTestDefinitions) { + this.renderTestDefinitions = renderTestDefinitions; + if (!renderTestDefinitions.isEmpty()) { + render(renderTestDefinitions.get(0), renderTestDefinitions.size()); + } else { + // no definitions, finish test without rendering + onRenderTestCompletionListener.onFinish(); + } + } + + private void render(final RenderTestDefinition renderTestDefinition, final int testSize) { + Timber.d("Render test %s,%s", renderTestDefinition.getName(), renderTestDefinition.getCategory()); + mapSnapshotter = new RenderTestSnapshotter(this, renderTestDefinition.toOptions()); + mapSnapshotter.start(result -> { + Bitmap snapshot = result.getBitmap(); + imageView.setImageBitmap(snapshot); + renderResultMap.put(renderTestDefinition, snapshot); + if (renderResultMap.size() != testSize) { + continueTesting(renderTestDefinition); + } else { + finishTesting(); + } + }, error -> Timber.e(error)); + } + + private void continueTesting(RenderTestDefinition renderTestDefinition) { + int next = renderTestDefinitions.indexOf(renderTestDefinition) + 1; + Timber.d("Next test: %s / %s", next, renderTestDefinitions.size()); + render(renderTestDefinitions.get(next), renderTestDefinitions.size()); + } + + private void finishTesting() { + new SaveResultToDiskTask(onRenderTestCompletionListener, renderResultMap).execute(); + } + + // + // Save tests results to disk + // + private static class SaveResultToDiskTask extends AsyncTask<Void, Void, Void> { + + private OnRenderTestCompletionListener onRenderTestCompletionListener; + private Map<RenderTestDefinition, Bitmap> renderResultMap; + + SaveResultToDiskTask(OnRenderTestCompletionListener onRenderTestCompletionListener, + Map<RenderTestDefinition, Bitmap> renderResultMap) { + this.onRenderTestCompletionListener = onRenderTestCompletionListener; + this.renderResultMap = renderResultMap; + } + + @Override + protected Void doInBackground(Void... voids) { + if (isExternalStorageWritable()) { + try { + File testResultDir = FileUtils.createTestResultRootFolder(); + String basePath = testResultDir.getAbsolutePath(); + for (Map.Entry<RenderTestDefinition, Bitmap> testResult : renderResultMap.entrySet()) { + writeResultToDisk(basePath, testResult); + } + } catch (final Exception exception) { + Timber.e(exception); + } + } + return null; + } + + private void writeResultToDisk(String path, Map.Entry<RenderTestDefinition, Bitmap> result) { + RenderTestDefinition definition = result.getKey(); + String categoryName = definition.getCategory(); + String categoryPath = String.format("%s/%s", path, categoryName); + FileUtils.createCategoryDirectory(categoryPath); + String testName = result.getKey().getName(); + String testDir = FileUtils.createTestDirectory(categoryPath, testName); + FileUtils.writeTestResultToDisk(testDir, result.getValue()); + } + + private boolean isExternalStorageWritable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (onRenderTestCompletionListener != null) { + onRenderTestCompletionListener.onFinish(); + } + } + } + + // + // Callback configuration to notify test executor of test finishing + // + public interface OnRenderTestCompletionListener { + void onFinish(); + } + + public void setOnRenderTestCompletionListener(OnRenderTestCompletionListener listener) { + this.onRenderTestCompletionListener = listener; + new LoadRenderIgnoreTask(this).execute(); + } + + public void onLoadIgnoreList(List<String> ignoreList) { + Timber.e("We loaded %s amount of tests to be ignored", ignoreList.size()); + EXCLUDED_TESTS.addAll(ignoreList); + new LoadRenderDefinitionTask(this).execute(); + } + + // + // FileUtils + // + + private static class FileUtils { + + private static void createCategoryDirectory(String catPath) { + File testResultDir = new File(catPath); + if (testResultDir.exists()) { + return; + } + + if (!testResultDir.mkdirs()) { + throw new RuntimeException("can't create root test directory"); + } + } + + private static File createTestResultRootFolder() { + File testResultDir = new File(Environment.getExternalStorageDirectory() + + File.separator + "mapbox" + File.separator + "render"); + if (testResultDir.exists()) { + // cleanup old files + deleteRecursive(testResultDir); + } + + if (!testResultDir.mkdirs()) { + throw new RuntimeException("can't create root test directory"); + } + return testResultDir; + } + + private static void deleteRecursive(File fileOrDirectory) { + if (fileOrDirectory.isDirectory()) { + File[] files = fileOrDirectory.listFiles(); + if (files != null) { + for (File file : files) { + deleteRecursive(file); + } + } + } + + if (!fileOrDirectory.delete()) { + Timber.e("can't delete directory"); + } + } + + private static String createTestDirectory(String basePath, String testName) { + File testDir = new File(basePath + "/" + testName); + if (!testDir.exists()) { + if (!testDir.mkdir()) { + throw new RuntimeException("can't create sub directory for " + testName); + } + } + return testDir.getAbsolutePath(); + } + + private static void writeTestResultToDisk(String testPath, Bitmap testResult) { + String filePath = testPath + "/actual.png"; + try (FileOutputStream out = new FileOutputStream(filePath)) { + testResult.compress(Bitmap.CompressFormat.PNG, 100, out); + } catch (IOException exception) { + Timber.e(exception); + } + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestDefinition.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestDefinition.java new file mode 100644 index 0000000000..49d0d4220a --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestDefinition.java @@ -0,0 +1,91 @@ +package com.mapbox.mapboxsdk.maps.activity.render; + +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; + +public class RenderTestDefinition { + + private static final int DEFAULT_WIDTH = 512; + private static final int DEFAULT_HEIGHT = 512; + + private String category; // eg. background-color + private String name; // eg. colorSpace-hcl + private String styleJson; + private RenderTestStyleDefinition definition; + + RenderTestDefinition(String category, String name, String styleJson, RenderTestStyleDefinition definition) { + this.category = category; + this.name = name; + this.styleJson = styleJson; + this.definition = definition; + } + + public String getName() { + return name; + } + + public String getCategory() { + return category; + } + + public int getWidth() { + RenderTestStyleDefinition.Test test = getTest(); + if (test != null) { + Integer testWidth = test.getWidth(); + if (testWidth != null && testWidth > 0) { + return testWidth; + } + } + return DEFAULT_WIDTH; + } + + public int getHeight() { + RenderTestStyleDefinition.Test test = getTest(); + if (test != null) { + Integer testHeight = test.getHeight(); + if (testHeight != null && testHeight > 0) { + return testHeight; + } + } + return DEFAULT_HEIGHT; + } + + public float getPixelRatio() { + RenderTestStyleDefinition.Test test = getTest(); + if (test != null) { + Float pixelRatio = test.getPixelRatio(); + if (pixelRatio != null && pixelRatio > 0) { + return pixelRatio; + } + } + return 1; + } + + public String getStyleJson() { + return styleJson; + } + + public boolean hasOperations() { + return getTest().getOperations() != null; + } + + public RenderTestStyleDefinition.Test getTest() { + return definition.getMetadata().getTest(); + } + + public MapSnapshotter.Options toOptions() { + return new MapSnapshotter + .Options(getWidth(), getHeight()) + .withStyleJson(styleJson) + .withPixelRatio(getPixelRatio()) + .withLogo(false); + } + + @Override + public String toString() { + return "RenderTestDefinition{" + + "category='" + category + '\'' + + ", name='" + name + '\'' + + ", styleJson='" + styleJson + '\'' + + '}'; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestSnapshotter.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestSnapshotter.java new file mode 100644 index 0000000000..32d9453aca --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestSnapshotter.java @@ -0,0 +1,18 @@ +package com.mapbox.mapboxsdk.maps.activity.render; + +import android.content.Context; +import android.support.annotation.NonNull; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; + +public class RenderTestSnapshotter extends MapSnapshotter { + + RenderTestSnapshotter(@NonNull Context context, @NonNull Options options) { + super(context, options); + } + + @Override + protected void addOverlay(MapSnapshot mapSnapshot) { + // don't add an overlay + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestStyleDefinition.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestStyleDefinition.java new file mode 100644 index 0000000000..d8b64dd2ad --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/render/RenderTestStyleDefinition.java @@ -0,0 +1,131 @@ +package com.mapbox.mapboxsdk.maps.activity.render; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RenderTestStyleDefinition { + + private Integer version; + private Metadata metadata; + private Map<String, Object> additionalProperties = new HashMap<String, Object>(); + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public Metadata getMetadata() { + return metadata; + } + + public void setMetadata(Metadata metadata) { + this.metadata = metadata; + } + + public Map<String, Object> getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + public class Metadata { + + private Test test; + private Map<String, Object> additionalProperties = new HashMap<String, Object>(); + + public Test getTest() { + return test; + } + + public void setTest(Test test) { + this.test = test; + } + + public Map<String, Object> getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + } + + public class Test { + + private Integer width; + private Integer height; + private Float pixelRatio; + private List<Integer> center = null; + private Integer zoom; + private Double diff; + private List<List<String>> operations = null; + private Map<String, Object> additionalProperties = new HashMap<String, Object>(); + + public Integer getWidth() { + return width; + } + + public void setWidth(Integer width) { + this.width = width; + } + + public Integer getHeight() { + return height; + } + + public void setHeight(Integer height) { + this.height = height; + } + + public Float getPixelRatio() { + return pixelRatio; + } + + public List<Integer> getCenter() { + return center; + } + + public void setCenter(List<Integer> center) { + this.center = center; + } + + public Integer getZoom() { + return zoom; + } + + public void setZoom(Integer zoom) { + this.zoom = zoom; + } + + public Double getDiff() { + return diff; + } + + public void setDiff(Double diff) { + this.diff = diff; + } + + public List<List<String>> getOperations() { + return operations; + } + + public void setOperations(List<List<String>> operations) { + this.operations = operations; + } + + public Map<String, Object> getAdditionalProperties() { + return this.additionalProperties; + } + + public void setAdditionalProperty(String name, Object value) { + this.additionalProperties.put(name, value); + } + + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterActivity.java new file mode 100644 index 0000000000..e159878a39 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterActivity.java @@ -0,0 +1,126 @@ +package com.mapbox.mapboxsdk.maps.activity.snapshot; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.ViewTreeObserver; +import android.widget.GridLayout; +import android.widget.ImageView; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import timber.log.Timber; + +/** + * Test activity showing how to use a the {@link com.mapbox.mapboxsdk.snapshotter.MapSnapshotter} + */ +public class MapSnapshotterActivity extends AppCompatActivity { + + private GridLayout grid; + private List<MapSnapshotter> snapshotters = new ArrayList<>(); + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter); + + // Find the grid view and start snapshotting as soon + // as the view is measured + grid = (GridLayout) findViewById(R.id.snapshot_grid); + grid.getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + //noinspection deprecation + grid.getViewTreeObserver().removeGlobalOnLayoutListener(this); + addSnapshots(); + } + }); + } + + private void addSnapshots() { + Timber.i("Creating snapshotters"); + + for (int row = 0; row < grid.getRowCount(); row++) { + for (int column = 0; column < grid.getColumnCount(); column++) { + startSnapShot(row, column); + } + } + } + + private void startSnapShot(final int row, final int column) { + + // Define the dimensions + MapSnapshotter.Options options = new MapSnapshotter.Options( + grid.getMeasuredWidth() / grid.getColumnCount(), + grid.getMeasuredHeight() / grid.getRowCount() + ) + // Optionally the pixel ratio + .withPixelRatio(1) + + // Optionally the style + .withStyle((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK); + + // Optionally the visible region + if (row % 2 == 0) { + options.withRegion(new LatLngBounds.Builder() + .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .include(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .build() + ); + } + + // Optionally the camera options + if (column % 2 == 0) { + options.withCameraPosition(new CameraPosition.Builder() + .target(options.getRegion() != null + ? options.getRegion().getCenter() + : new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .bearing(randomInRange(0, 360)) + .tilt(randomInRange(0, 60)) + .zoom(randomInRange(0, 20)) + .build() + ); + } + + MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options); + + snapshotter.start(snapshot -> { + Timber.i("Got the snapshot"); + ImageView imageView = new ImageView(MapSnapshotterActivity.this); + imageView.setImageBitmap(snapshot.getBitmap()); + grid.addView( + imageView, + new GridLayout.LayoutParams(GridLayout.spec(row), GridLayout.spec(column)) + ); + }); + snapshotters.add(snapshotter); + } + + @Override + public void onPause() { + super.onPause(); + + // Make sure to stop the snapshotters on pause + for (MapSnapshotter snapshotter : snapshotters) { + snapshotter.cancel(); + } + snapshotters.clear(); + } + + private static Random random = new Random(); + + public static float randomInRange(float min, float max) { + return (random.nextFloat() * (max - min)) + min; + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterLocalStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterLocalStyleActivity.java new file mode 100644 index 0000000000..e2426c8adf --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterLocalStyleActivity.java @@ -0,0 +1,71 @@ +package com.mapbox.mapboxsdk.maps.activity.snapshot; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; +import timber.log.Timber; + +import java.io.IOException; + +/** + * Test activity showing how to use a the MapSnapshotter with a local style + */ +public class MapSnapshotterLocalStyleActivity extends AppCompatActivity + implements MapSnapshotter.SnapshotReadyCallback { + + private MapSnapshotter mapSnapshotter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter_marker); + + final View container = findViewById(R.id.container); + container.getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + //noinspection deprecation + container.getViewTreeObserver().removeGlobalOnLayoutListener(this); + + String styleJson; + try { + styleJson = ResourceUtils.readRawResource(MapSnapshotterLocalStyleActivity.this, R.raw.sat_style); + } catch (IOException exception) { + throw new RuntimeException(exception); + } + + Timber.i("Starting snapshot"); + mapSnapshotter = new MapSnapshotter( + getApplicationContext(), + new MapSnapshotter + .Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024)) + .withStyleJson(styleJson) + .withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(18).build()) + ); + mapSnapshotter.start(MapSnapshotterLocalStyleActivity.this, error -> Timber.e(error)); + } + }); + } + + @Override + protected void onStop() { + super.onStop(); + mapSnapshotter.cancel(); + } + + @Override + public void onSnapshotReady(MapSnapshot snapshot) { + Timber.i("Snapshot ready"); + ImageView imageView = (ImageView) findViewById(R.id.snapshot_image); + imageView.setImageBitmap(snapshot.getBitmap()); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterMarkerActivity.java new file mode 100644 index 0000000000..1ff35fd13c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterMarkerActivity.java @@ -0,0 +1,96 @@ +package com.mapbox.mapboxsdk.maps.activity.snapshot; + +import android.annotation.SuppressLint; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.PointF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.testapp.R; +import timber.log.Timber; + +/** + * Test activity showing how to use a the {@link MapSnapshotter} and overlay + * {@link android.graphics.Bitmap}s on top. + */ +public class MapSnapshotterMarkerActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback { + + private MapSnapshotter mapSnapshotter; + private MapSnapshot mapSnapshot; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter_marker); + + final View container = findViewById(R.id.container); + container.getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + //noinspection deprecation + container.getViewTreeObserver().removeGlobalOnLayoutListener(this); + + Timber.i("Starting snapshot"); + + mapSnapshotter = new MapSnapshotter( + getApplicationContext(), + new MapSnapshotter + .Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024)) + .withStyle(Style.OUTDOORS) + .withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(15).build()) + ); + mapSnapshotter.start(MapSnapshotterMarkerActivity.this); + } + }); + } + + @Override + protected void onStop() { + super.onStop(); + mapSnapshotter.cancel(); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void onSnapshotReady(MapSnapshot snapshot) { + Timber.i("Snapshot ready"); + ImageView imageView = (ImageView) findViewById(R.id.snapshot_image); + Bitmap image = addMarker(snapshot); + imageView.setImageBitmap(image); + imageView.setOnTouchListener((v, event) -> { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + LatLng latLng = snapshot.latLngForPixel(new PointF(event.getX(), event.getY())); + Timber.e("Clicked LatLng is %s", latLng); + return true; + } + return false; + }); + } + + private Bitmap addMarker(MapSnapshot snapshot) { + Canvas canvas = new Canvas(snapshot.getBitmap()); + Bitmap marker = BitmapFactory.decodeResource(getResources(), R.drawable.mapbox_marker_icon_default, null); + // Dom toren + PointF markerLocation = snapshot.pixelForLatLng(new LatLng(52.090649433011315, 5.121310651302338)); + canvas.drawBitmap(marker, + /* Subtract half of the width so we center the bitmap correctly */ + markerLocation.x - marker.getWidth() / 2, + /* Subtract half of the height so we align the bitmap bottom correctly */ + markerLocation.y - marker.getHeight() / 2, + null + ); + return snapshot.getBitmap(); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterReuseActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterReuseActivity.java new file mode 100644 index 0000000000..c2b9580b90 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/snapshot/MapSnapshotterReuseActivity.java @@ -0,0 +1,106 @@ +package com.mapbox.mapboxsdk.maps.activity.snapshot; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.ImageView; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.Random; + +/** + * Test activity showing how to use a the {@link MapSnapshotter} + */ +public class MapSnapshotterReuseActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback { + + private MapSnapshotter mapSnapshotter; + private View fab; + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter_reuse); + + fab = findViewById(R.id.fab); + fab.setVisibility(View.INVISIBLE); + fab.setOnClickListener(v -> { + fab.setVisibility(View.INVISIBLE); + + mapSnapshotter.setStyleUrl(getRandomStyle()); + if (random.nextInt(2) == 0) { + mapSnapshotter.setCameraPosition(getRandomCameraPosition()); + } else { + mapSnapshotter.setRegion(getRandomBounds()); + } + if (random.nextInt(2) == 0) { + mapSnapshotter.setSize(512, 512); + } else { + mapSnapshotter.setSize(256, 256); + } + mapSnapshotter.start(MapSnapshotterReuseActivity.this); + }); + + mapSnapshotter = new MapSnapshotter( + getApplicationContext(), + new MapSnapshotter.Options(512, 512) + ); + + mapSnapshotter.start(MapSnapshotterReuseActivity.this); + } + + @Override + public void onSnapshotReady(MapSnapshot snapshot) { + fab.setVisibility(View.VISIBLE); + ImageView imageView = (ImageView) findViewById(R.id.snapshot_image); + imageView.setImageBitmap(snapshot.getBitmap()); + } + + private LatLngBounds getRandomBounds() { + return LatLngBounds.from( + randomInRange(-5, 5), + randomInRange(-5, 5), + randomInRange(5, 10), + randomInRange(5, 10) + ); + } + + private CameraPosition getRandomCameraPosition() { + return new CameraPosition.Builder() + .target(new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) + .zoom(randomInRange(2, 10)) + .bearing(randomInRange(0, 90)) + .build(); + } + + public String getRandomStyle() { + switch (random.nextInt(5)) { + case 0: + return Style.DARK; + case 1: + return Style.LIGHT; + case 2: + return Style.MAPBOX_STREETS; + case 3: + return Style.OUTDOORS; + case 4: + return Style.SATELLITE_STREETS; + default: + return Style.TRAFFIC_DAY; + } + } + + private static Random random = new Random(); + + public static float randomInRange(float min, float max) { + return (random.nextFloat() * (max - min)) + min; + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/storage/UrlTransformActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/storage/UrlTransformActivity.java new file mode 100644 index 0000000000..831e809cfd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/storage/UrlTransformActivity.java @@ -0,0 +1,100 @@ +package com.mapbox.mapboxsdk.maps.activity.storage; + +import android.app.Activity; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.storage.Resource; +import com.mapbox.mapboxsdk.testapp.R; + +import timber.log.Timber; + +/** + * Test activity showcasing the url transform + */ +public class UrlTransformActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + /** + * Be sure to use an isolated class so the activity is not leaked when + * the activity goes out of scope (static class in this case). + * <p> + * Alternatively, unregister the callback in {@link Activity#onDestroy()} + */ + private static final class Transform implements FileSource.ResourceTransformCallback { + @Override + public String onURL(@Resource.Kind int kind, String url) { + Timber.i("[%s] Could be rewriting %s", Thread.currentThread().getName(), url); + return url; + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_data_driven_style); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + + // Get a handle to the file source and set the resource transform + FileSource.getInstance(UrlTransformActivity.this).setResourceTransform(new Transform()); + + mapView.getMapAsync(map -> { + Timber.i("Map loaded"); + mapboxMap = map; + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + // Example of how to reset the transform callback + FileSource.getInstance(UrlTransformActivity.this).setResourceTransform(null); + + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/AnimatedImageSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/AnimatedImageSourceActivity.java new file mode 100644 index 0000000000..741fa89d23 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/AnimatedImageSourceActivity.java @@ -0,0 +1,147 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngQuad; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.RasterLayer; +import com.mapbox.mapboxsdk.style.sources.ImageSource; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showing how to use a series of images to create an animation + * with an ImageSource + * <p> + * GL-native equivalent of https://www.mapbox.com/mapbox-gl-js/example/animate-images/ + * </p> + */ +public class AnimatedImageSourceActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String ID_IMAGE_SOURCE = "animated_image_source"; + private static final String ID_IMAGE_LAYER = "animated_image_layer"; + + private MapView mapView; + private MapboxMap mapboxMap; + + private Handler handler; + private Runnable runnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_animated_image_source); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + + // add source + LatLngQuad quad = new LatLngQuad( + new LatLng(46.437, -80.425), + new LatLng(46.437, -71.516), + new LatLng(37.936, -71.516), + new LatLng(37.936, -80.425)); + mapboxMap.addSource(new ImageSource(ID_IMAGE_SOURCE, quad, R.drawable.southeast_radar_0)); + + // add layer + RasterLayer layer = new RasterLayer(ID_IMAGE_LAYER, ID_IMAGE_SOURCE); + mapboxMap.addLayer(layer); + + // loop refresh geojson + handler = new Handler(); + runnable = new RefreshImageRunnable(mapboxMap, handler); + handler.postDelayed(runnable, 100); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + handler.removeCallbacks(runnable); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + private static class RefreshImageRunnable implements Runnable { + + private MapboxMap mapboxMap; + private Handler handler; + private Bitmap[] drawables; + private int drawableIndex; + + Bitmap getBitmap(int resourceId) { + Context context = Mapbox.getApplicationContext(); + Drawable drawable = ContextCompat.getDrawable(context, resourceId); + if (drawable instanceof BitmapDrawable) { + BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; + return bitmapDrawable.getBitmap(); + } + return null; + } + + RefreshImageRunnable(MapboxMap mapboxMap, Handler handler) { + this.mapboxMap = mapboxMap; + this.handler = handler; + drawables = new Bitmap[4]; + drawables[0] = getBitmap(R.drawable.southeast_radar_0); + drawables[1] = getBitmap(R.drawable.southeast_radar_1); + drawables[2] = getBitmap(R.drawable.southeast_radar_2); + drawables[3] = getBitmap(R.drawable.southeast_radar_3); + drawableIndex = 1; + } + + @Override + public void run() { + ((ImageSource) mapboxMap.getSource(ID_IMAGE_SOURCE)).setImage(drawables[drawableIndex++]); + if (drawableIndex > 3) { + drawableIndex = 0; + } + handler.postDelayed(this, 1000); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/BuildingFillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/BuildingFillExtrusionActivity.java new file mode 100644 index 0000000000..ff138c5025 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/BuildingFillExtrusionActivity.java @@ -0,0 +1,148 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyFactory; +import com.mapbox.mapboxsdk.style.light.Light; +import com.mapbox.mapboxsdk.style.light.Position; +import com.mapbox.mapboxsdk.testapp.R; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionBase; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity; + +/** + * Test activity showing 3D buildings with a FillExtrusion Layer + */ +public class BuildingFillExtrusionActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + private Light light; + + private boolean isMapAnchorLight; + private boolean isLowIntensityLight; + private boolean isRedColor; + private boolean isInitPosition; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_building_layer); + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + setupBuildings(); + setupLight(); + }); + } + + private void setupBuildings() { + FillExtrusionLayer fillExtrusionLayer = new FillExtrusionLayer("3d-buildings", "composite"); + fillExtrusionLayer.setSourceLayer("building"); + fillExtrusionLayer.setFilter(eq(get("extrude"), literal("true"))); + fillExtrusionLayer.setMinZoom(15); + fillExtrusionLayer.setProperties( + fillExtrusionColor(Color.LTGRAY), + fillExtrusionHeight(Expression.get("height")), + fillExtrusionBase(Expression.get("min_height")), + fillExtrusionOpacity(0.9f) + ); + mapboxMap.addLayer(fillExtrusionLayer); + } + + private void setupLight() { + light = mapboxMap.getLight(); + + findViewById(R.id.fabLightPosition).setOnClickListener(v -> { + isInitPosition = !isInitPosition; + if (isInitPosition) { + light.setPosition(new Position(1.5f, 90, 80)); + } else { + light.setPosition(new Position(1.15f, 210, 30)); + } + }); + + findViewById(R.id.fabLightColor).setOnClickListener(v -> { + isRedColor = !isRedColor; + light.setColor(PropertyFactory.colorToRgbaString(isRedColor ? Color.RED : Color.BLUE)); + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_building, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (light != null) { + int id = item.getItemId(); + if (id == R.id.menu_action_anchor) { + isMapAnchorLight = !isMapAnchorLight; + light.setAnchor(isMapAnchorLight ? Property.ANCHOR_MAP : Property.ANCHOR_VIEWPORT); + } else if (id == R.id.menu_action_intensity) { + isLowIntensityLight = !isLowIntensityLight; + light.setIntensity(isLowIntensityLight ? 0.35f : 1.0f); + } + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CircleLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CircleLayerActivity.java new file mode 100644 index 0000000000..a26ee9f770 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CircleLayerActivity.java @@ -0,0 +1,240 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +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.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.layers.LineLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + +import java.net.MalformedURLException; +import java.net.URL; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.array; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.has; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; + +/** + * Test activity showcasing adding a Circle Layer to the Map + * <p> + * Uses bus stop data from Singapore as a source and allows to filter into 1 specific route with a line layer. + * </p> + */ +public class CircleLayerActivity extends AppCompatActivity implements View.OnClickListener { + + private MapView mapView; + private MapboxMap mapboxMap; + + private FloatingActionButton styleFab; + private FloatingActionButton routeFab; + + private CircleLayer layer; + private GeoJsonSource source; + + private int currentStyleIndex = 0; + private boolean isLoadingStyle = true; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_circle_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + addBusStopSource(); + addBusStopCircleLayer(); + initFloatingActionButtons(); + isLoadingStyle = false; + }); + } + + private void addBusStopSource() { + try { + source = new GeoJsonSource("bus_stop", + new URL("https://raw.githubusercontent.com/cheeaun/busrouter-sg/master/data/2/bus-stops.geojson")); + } catch (MalformedURLException exception) { + Timber.e(exception, "That's not an url... "); + } + mapboxMap.addSource(source); + } + + private void addBusStopCircleLayer() { + layer = new CircleLayer("stops_layer", "bus_stop"); + layer.setProperties( + circleColor(Color.parseColor("#FF9800")), + circleRadius(2.0f) + ); + mapboxMap.addLayer(layer); + } + + private void initFloatingActionButtons() { + routeFab = (FloatingActionButton) findViewById(R.id.fab_route); + routeFab.setColorFilter(ContextCompat.getColor(CircleLayerActivity.this, R.color.primary)); + routeFab.setOnClickListener(CircleLayerActivity.this); + + styleFab = (FloatingActionButton) findViewById(R.id.fab_style); + styleFab.setOnClickListener(CircleLayerActivity.this); + } + + @Override + public void onClick(View view) { + if (isLoadingStyle) { + return; + } + + if (view.getId() == R.id.fab_route) { + showBusRoute(); + } else if (view.getId() == R.id.fab_style) { + changeMapStyle(); + } + } + + private void showBusRoute() { + removeFabs(); + applyBusRouteFilterToBusStopSource(); + addBusRouteSource(); + addBusRouteLayer(); + } + + private void removeFabs() { + routeFab.setVisibility(View.GONE); + styleFab.setVisibility(View.GONE); + } + + private void applyBusRouteFilterToBusStopSource() { + layer.setFilter(has(Expression.toString(get("number")), array(literal(Data.STOPS_FOR_ROUTE)))); + } + + private void addBusRouteSource() { + try { + mapboxMap.addSource(new GeoJsonSource("bus_route", + new URL("https://gist.githubusercontent.com/tobrun/7fbc0fe7e9ffea509f7608cda2601d5d/raw/" + + "a4b8cc289020f91fa2e1553524820054395e36f5/line.geojson"))); + } catch (MalformedURLException malformedUrlException) { + Timber.e(malformedUrlException, "That's not an url... "); + } + } + + private void addBusRouteLayer() { + LineLayer lineLayer = new LineLayer("route_layer", "bus_route"); + mapboxMap.addLayerBelow(lineLayer, "stops_layer"); + mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(new LatLng(1.3896777, 103.9874997)) + .bearing(225) + .tilt(45) + .zoom(13) + .build() + ), 1750); + } + + private void changeMapStyle() { + isLoadingStyle = true; + removeBusStop(); + loadNewStyle(); + } + + private void removeBusStop() { + mapboxMap.removeLayer(layer); + mapboxMap.removeSource(source); + } + + private void loadNewStyle() { + mapboxMap.setStyleUrl(getNextStyle(), style -> { + addBusStop(); + isLoadingStyle = false; + }); + } + + private void addBusStop() { + mapboxMap.addLayer(layer); + mapboxMap.addSource(source); + } + + private String getNextStyle() { + currentStyleIndex++; + if (currentStyleIndex == Data.STYLES.length) { + currentStyleIndex = 0; + } + return Data.STYLES[currentStyleIndex]; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + private static class Data { + private static final String[] STOPS_FOR_ROUTE = new String[] {"99009", "99131", "99049", "99039", "99029", "99019", + "98079", "98069", "97099", "97089", "97079", "97069", "97209", "97059", "97049", "97039", "97019", "96069", + "96059", "96049", "96099", "96089", "96079", "85079", "85089", "85069", "85059", "85099", "84069", "84059", + "84049", "84039", "84029", "84019", "83099", "83079", "83059", "83049", "83029", "82069", "82049", "82029", + "82109", "81069", "81049", "81029", "80089", "80069", "80049", "80039", "80029", "01319", "01219", "01129", + "01059", "01119", "01019", "04159", "04149", "04229", "04239", "05059", "05049", "05039", "05019", "10589"}; + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.OUTDOORS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE, + Style.SATELLITE_STREETS + }; + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CustomSpriteActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CustomSpriteActivity.java new file mode 100644 index 0000000000..03411dfcc2 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/CustomSpriteActivity.java @@ -0,0 +1,135 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.BitmapFactory; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; + +/** + * Test activity showcasing adding a sprite image and use it in a Symbol Layer + */ +public class CustomSpriteActivity extends AppCompatActivity { + private static final String CUSTOM_ICON = "custom-icon"; + + private MapboxMap mapboxMap; + private MapView mapView; + private Layer layer; + private GeoJsonSource source; + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_add_sprite); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + final FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setColorFilter(ContextCompat.getColor(CustomSpriteActivity.this, R.color.primary)); + fab.setOnClickListener(new View.OnClickListener() { + private Point point; + + @Override + public void onClick(View view) { + if (point == null) { + Timber.i("First click -> Car"); + // Add an icon to reference later + mapboxMap.addImage(CUSTOM_ICON, BitmapFactory.decodeResource(getResources(), R.drawable.ic_car_top)); + + // Add a source with a geojson point + point = Point.fromLngLat(13.400972d, 52.519003d); + source = new GeoJsonSource( + "point", + FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)}) + ); + mapboxMap.addSource(source); + + // Add a symbol layer that references that point source + layer = new SymbolLayer("layer", "point"); + layer.setProperties( + // Set the id of the sprite to use + iconImage(CUSTOM_ICON) + ); + + // lets add a circle below labels! + mapboxMap.addLayerBelow(layer, "waterway-label"); + + fab.setImageResource(R.drawable.ic_directions_car_black); + } else { + // Update point + point = Point.fromLngLat(point.longitude() + 0.001, + point.latitude() + 0.001); + source.setGeoJson(FeatureCollection.fromFeatures(new Feature[] {Feature.fromGeometry(point)})); + + // Move the camera as well + mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng( + point.latitude(), point.longitude()))); + } + } + }); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/DataDrivenStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/DataDrivenStyleActivity.java new file mode 100644 index 0000000000..f8644c9bb3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/DataDrivenStyleActivity.java @@ -0,0 +1,471 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.TextView; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.FillLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.IOException; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.match; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; +import static com.mapbox.mapboxsdk.style.expressions.Expression.step; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.expressions.Expression.color; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOutlineColor; + +/** + * Test activity showcasing the data driven runtime style API. + */ +public class DataDrivenStyleActivity extends AppCompatActivity { + + public static final String AMSTERDAM_PARKS_LAYER = "amsterdam-parks-layer"; + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_data_driven_style); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + + mapView.getMapAsync(map -> { + // Store for later + mapboxMap = map; + + // Add a parks layer + addParksLayer(); + + // Add debug overlay + setupDebugZoomView(); + + // Center and Zoom (Amsterdam, zoomed to streets) + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14)); + }); + } + + private void setupDebugZoomView() { + final TextView textView = (TextView) findViewById(R.id.textZoom); + mapboxMap.setOnCameraChangeListener(new MapboxMap.OnCameraChangeListener() { + @Override + public void onCameraChange(CameraPosition position) { + textView.setText(String.format(getString(R.string.debug_zoom), position.zoom)); + } + }); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_data_driven_style, menu); + return true; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 R.id.action_add_exponential_zoom_function: + addExponentialZoomFunction(); + return true; + case R.id.action_add_interval_zoom_function: + addIntervalZoomFunction(); + return true; + case R.id.action_add_categorical_source_function: + addCategoricalSourceFunction(); + return true; + case R.id.action_add_exponential_source_function: + addExponentialSourceFunction(); + return true; + case R.id.action_add_identity_source_function: + addIdentitySourceFunction(); + return true; + case R.id.action_add_interval_source_function: + addIntervalSourceFunction(); + return true; + case R.id.action_add_composite_categorical_function: + addCompositeCategoricalFunction(); + return true; + case R.id.action_add_composite_exponential_function: + addCompositeExponentialFunction(); + return true; + case R.id.action_add_composite_interval_function: + addCompositeIntervalFunction(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void addExponentialZoomFunction() { + Timber.i("Add exponential zoom function"); + FillLayer layer = mapboxMap.getLayerAs("water"); + assert layer != null; + layer.setProperties( + fillColor( + interpolate( + exponential(0.5f), zoom(), + stop(1, color(Color.RED)), + stop(5, color(Color.BLUE)), + stop(10, color(Color.GREEN)) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addIntervalZoomFunction() { + Timber.i("Add interval zoom function"); + FillLayer layer = mapboxMap.getLayerAs("water"); + assert layer != null; + layer.setProperties( + fillColor( + step(zoom(), + rgba(0.0f, 255.0f, 255.0f, 1.0f), + stop(1, rgba(255.0f, 0.0f, 0.0f, 1.0f)), + stop(5, rgba(0.0f, 0.0f, 255.0f, 1.0f)), + stop(10, rgba(0.0f, 255.0f, 0.0f, 1.0f)) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addExponentialSourceFunction() { + Timber.i("Add exponential source function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + interpolate( + exponential(0.5f), + get("stroke-width"), + stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)), + stop(5f, rgba(0.0f, 0.0f, 255.0f, 1.0f)), + stop(10f, rgba(0.0f, 255.0f, 0.0f, 1.0f)) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addCategoricalSourceFunction() { + Timber.i("Add categorical source function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + literal("Jordaan"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + literal("Prinseneiland"), rgba(0.0f, 255.0f, 0.0f, 1.0f), + rgba(0.0f, 255.0f, 255.0f, 1.0f) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addIdentitySourceFunction() { + Timber.i("Add identity source function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillOpacity( + get("fill-opacity") + ) + ); + + Timber.i("Fill opacity: %s", layer.getFillOpacity()); + } + + private void addIntervalSourceFunction() { + Timber.i("Add interval source function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + step( + get("stroke-width"), + rgba(0.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)), + stop(2f, rgba(0.0f, 0.0f, 255.0f, 1.0f)), + stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f)) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addCompositeExponentialFunction() { + Timber.i("Add composite exponential function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + interpolate( + exponential(1f), + zoom(), + stop(12, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)), + stop(2f, rgba(0.0f, 0.0f, 0.0f, 1.0f)), + stop(3f, rgba(0.0f, 0.0f, 255.0f, 1.0f)) + )), + stop(15, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(255.0f, 255.0f, 0.0f, 1.0f)), + stop(2f, rgba(211.0f, 211.0f, 211.0f, 1.0f)), + stop(3f, rgba(0.0f, 255.0f, 255.0f, 1.0f)) + )), + stop(18, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(0.0f, 0.0f, 0.0f, 1.0f)), + stop(2f, rgba(128.0f, 128.0f, 128.0f, 1.0f)), + stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f))) + ) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addCompositeIntervalFunction() { + Timber.i("Add composite interval function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + interpolate( + linear(), + zoom(), + stop(12, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(255.0f, 0.0f, 0.0f, 1.0f)), + stop(2f, rgba(0.0f, 0.0f, 0.0f, 1.0f)), + stop(3f, rgba(0.0f, 0.0f, 255.0f, 1.0f)) + )), + stop(15, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(255.0f, 255.0f, 0.0f, 1.0f)), + stop(2f, rgba(211.0f, 211.0f, 211.0f, 1.0f)), + stop(3f, rgba(0.0f, 255.0f, 255.0f, 1.0f)) + )), + stop(18, step( + get("stroke-width"), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(1f, rgba(0.0f, 0.0f, 0.0f, 1.0f)), + stop(2f, rgba(128.0f, 128.0f, 128.0f, 1.0f)), + stop(3f, rgba(0.0f, 255.0f, 0.0f, 1.0f)) + )) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addCompositeCategoricalFunction() { + Timber.i("Add composite categorical function"); + FillLayer layer = mapboxMap.getLayerAs(AMSTERDAM_PARKS_LAYER); + assert layer != null; + layer.setProperties( + fillColor( + step(zoom(), + rgba(255.0f, 255.0f, 255.0f, 1.0f), + stop(7f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(8f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(9f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(10f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(11f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(12f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(13f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(14f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + literal("Jordaan"), rgba(0.0f, 255.0f, 0.0f, 1.0f), + literal("PrinsenEiland"), rgba(0.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(15f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(16f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(17f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(18f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + literal("Jordaan"), rgba(0.0f, 255.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(19f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(20f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(21f, match( + get("name"), + literal("Westerpark"), rgba(255.0f, 0.0f, 0.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )), + stop(22f, match( + get("name"), + literal("Westerpark"), rgba(0.0f, 0.0f, 255.0f, 1.0f), + rgba(255.0f, 255.0f, 255.0f, 1.0f) + )) + ) + ) + ); + + Timber.i("Fill color: %s", layer.getFillColor()); + } + + private void addParksLayer() { + // Add a source + Source source; + try { + source = new GeoJsonSource("amsterdam-parks-source", ResourceUtils.readRawResource(this, R.raw.amsterdam)); + mapboxMap.addSource(source); + } catch (IOException ioException) { + Toast.makeText( + DataDrivenStyleActivity.this, + "Couldn't add source: " + ioException.getMessage(), + Toast.LENGTH_SHORT).show(); + return; + } + + // Add a fill layer + mapboxMap.addLayer(new FillLayer(AMSTERDAM_PARKS_LAYER, source.getId()) + .withProperties( + fillColor(color(Color.GREEN)), + fillOutlineColor(rgb(0, 0, 255)), + fillAntialias(true) + ) + ); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionActivity.java new file mode 100644 index 0000000000..994394817e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionActivity.java @@ -0,0 +1,122 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.geojson.Polygon; + +import java.util.Arrays; +import java.util.List; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionHeight; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillExtrusionOpacity; + +/** + * Test activity showcasing fill extrusions + */ +public class FillExtrusionActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_fill_extrusion_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + List<List<Point>> lngLats = Arrays.asList( + Arrays.asList( + Point.fromLngLat(5.12112557888031, 52.09071040847704), + Point.fromLngLat(5.121227502822875, 52.09053901776669), + Point.fromLngLat(5.121484994888306, 52.090601641371805), + Point.fromLngLat(5.1213884353637695, 52.090766439912635), + Point.fromLngLat(5.12112557888031, 52.09071040847704) + ) + ); + + Polygon domTower = Polygon.fromLngLats(lngLats); + + GeoJsonSource source = new GeoJsonSource("extrusion-source", domTower); + map.addSource(source); + + mapboxMap.addLayer( + new FillExtrusionLayer("extrusion-layer", source.getId()) + .withProperties( + fillExtrusionHeight(40f), + fillExtrusionOpacity(0.5f), + fillExtrusionColor(Color.RED) + ) + ); + + mapboxMap.animateCamera( + CameraUpdateFactory.newCameraPosition( + new CameraPosition.Builder() + .target(new LatLng(52.09071040847704, 5.12112557888031)) + .tilt(45.0) + .zoom(18) + .build() + ), + 10000 + ); + }); + } + + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionStyleTestActivity.java new file mode 100644 index 0000000000..81a417ad73 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/FillExtrusionStyleTestActivity.java @@ -0,0 +1,74 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity used for instrumentation tests of fill extrusion. + */ +public class FillExtrusionStyleTestActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_extrusion_test); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> FillExtrusionStyleTestActivity.this.mapboxMap = mapboxMap); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GeoJsonClusteringActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GeoJsonClusteringActivity.java new file mode 100644 index 0000000000..efcf482eb7 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GeoJsonClusteringActivity.java @@ -0,0 +1,210 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.MenuItem; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.utils.BitmapUtils; +import timber.log.Timber; + +import java.net.MalformedURLException; +import java.net.URL; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.all; +import static com.mapbox.mapboxsdk.style.expressions.Expression.division; +import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.gt; +import static com.mapbox.mapboxsdk.style.expressions.Expression.gte; +import static com.mapbox.mapboxsdk.style.expressions.Expression.has; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.lt; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize; + +/** + * Test activity showcasing using a geojson source and visualise that source as a cluster by using filters. + */ +public class GeoJsonClusteringActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_geojson_clustering); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + // noinspection ConstantConditions + mapView.onCreate(savedInstanceState); + + mapView.getMapAsync(map -> { + mapboxMap = map; + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.7749, 122.4194), 0)); + mapboxMap.addImage( + "icon-id", + BitmapUtils.getBitmapFromDrawable(getResources().getDrawable(R.drawable.ic_hearing_black_24dp)), + true + ); + // Add a clustered source with some layers + addClusteredGeoJsonSource(); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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); + } + } + + private void addClusteredGeoJsonSource() { + // Add a clustered source + try { + mapboxMap.addSource( + new GeoJsonSource("earthquakes", + new URL("https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"), + new GeoJsonOptions() + .withCluster(true) + .withClusterMaxZoom(14) + .withClusterRadius(50) + ) + ); + } catch (MalformedURLException malformedUrlException) { + Timber.e(malformedUrlException, "That's not an url... "); + } + + // Add unclustered layer + int[][] layers = new int[][] { + new int[] {150, ResourcesCompat.getColor(getResources(), R.color.redAccent, getTheme())}, + new int[] {20, ResourcesCompat.getColor(getResources(), R.color.greenAccent, getTheme())}, + new int[] {0, ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())} + }; + + SymbolLayer unclustered = new SymbolLayer("unclustered-points", "earthquakes"); + unclustered.setProperties( + iconImage("icon-id"), + iconSize( + division( + get("mag"), literal(4.0f) + ) + ), + iconColor( + interpolate(exponential(1), get("mag"), + stop(2.0, rgb(0, 255, 0)), + stop(4.5, rgb(0, 0, 255)), + stop(7.0, rgb(255, 0, 0)) + ) + ) + ); + + mapboxMap.addLayer(unclustered); + + for (int i = 0; i < layers.length; i++) { + // Add some nice circles + CircleLayer circles = new CircleLayer("cluster-" + i, "earthquakes"); + circles.setProperties( + circleColor(layers[i][1]), + circleRadius(18f) + ); + + Expression pointCount = toNumber(get("point_count")); + circles.setFilter( + i == 0 + ? all(has("point_count"), + gte(pointCount, literal(layers[i][0])) + ) : all(has("point_count"), + gt(pointCount, literal(layers[i][0])), + lt(pointCount, literal(layers[i - 1][0])) + ) + ); + mapboxMap.addLayer(circles); + } + + // Add the count labels + SymbolLayer count = new SymbolLayer("count", "earthquakes"); + count.setProperties( + textField(Expression.toString(get("point_count"))), + textSize(12f), + textColor(Color.WHITE), + textIgnorePlacement(true), + textAllowOverlap(true) + ); + mapboxMap.addLayer(count); + + + // Zoom out to start + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(1)); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GridSourceActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GridSourceActivity.java new file mode 100644 index 0000000000..88000cb9eb --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/GridSourceActivity.java @@ -0,0 +1,152 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.MultiLineString; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.LineLayer; +import com.mapbox.mapboxsdk.style.sources.CustomGeometrySource; +import com.mapbox.mapboxsdk.style.sources.GeometryTileProvider; +import com.mapbox.mapboxsdk.testapp.R; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor; + +/** + * Test activity showcasing using CustomGeometrySource to create a grid overlay on the map. + */ +public class GridSourceActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String ID_GRID_SOURCE = "grid_source"; + private static final String ID_GRID_LAYER = "grid_layer"; + + private MapView mapView; + private MapboxMap mapboxMap; + + /** + * Implementation of GeometryTileProvider that returns features representing a zoom-dependent + * grid. + */ + static class GridProvider implements GeometryTileProvider { + public FeatureCollection getFeaturesForBounds(LatLngBounds bounds, int zoom) { + List<Feature> features = new ArrayList<>(); + double gridSpacing; + if (zoom >= 13) { + gridSpacing = 0.01; + } else if (zoom >= 11) { + gridSpacing = 0.05; + } else if (zoom == 10) { + gridSpacing = .1; + } else if (zoom == 9) { + gridSpacing = 0.25; + } else if (zoom == 8) { + gridSpacing = 0.5; + } else if (zoom >= 6) { + gridSpacing = 1; + } else if (zoom == 5) { + gridSpacing = 2; + } else if (zoom >= 4) { + gridSpacing = 5; + } else if (zoom == 2) { + gridSpacing = 10; + } else { + gridSpacing = 20; + } + + List gridLines = new ArrayList(); + for (double y = Math.ceil(bounds.getLatNorth() / gridSpacing) * gridSpacing; + y >= Math.floor(bounds.getLatSouth() / gridSpacing) * gridSpacing; y -= gridSpacing) { + gridLines.add(Arrays.asList(Point.fromLngLat(bounds.getLonWest(), y), + Point.fromLngLat(bounds.getLonEast(), y))); + } + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); + + gridLines = new ArrayList(); + for (double x = Math.floor(bounds.getLonWest() / gridSpacing) * gridSpacing; + x <= Math.ceil(bounds.getLonEast() / gridSpacing) * gridSpacing; x += gridSpacing) { + gridLines.add(Arrays.asList(Point.fromLngLat(x, bounds.getLatSouth()), + Point.fromLngLat(x, bounds.getLatNorth()))); + } + features.add(Feature.fromGeometry(MultiLineString.fromLngLats(gridLines))); + + return FeatureCollection.fromFeatures(features); + } + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_grid_source); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + + // add source + CustomGeometrySource source = new CustomGeometrySource(ID_GRID_SOURCE, new GridProvider()); + mapboxMap.addSource(source); + + // add layer + LineLayer layer = new LineLayer(ID_GRID_LAYER, ID_GRID_SOURCE); + layer.setProperties( + lineColor(Color.parseColor("#000000")) + ); + + mapboxMap.addLayer(layer); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HeatmapLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HeatmapLayerActivity.java new file mode 100644 index 0000000000..074ff8b036 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HeatmapLayerActivity.java @@ -0,0 +1,226 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.layers.HeatmapLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + +import java.net.MalformedURLException; +import java.net.URL; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.heatmapDensity; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeWidth; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapIntensity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapWeight; + +/** + * Test activity showcasing the heatmap layer api. + */ +public class HeatmapLayerActivity extends AppCompatActivity { + + private static final String EARTHQUAKE_SOURCE_URL = "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"; + private static final String EARTHQUAKE_SOURCE_ID = "earthquakes"; + private static final String HEATMAP_LAYER_ID = "earthquakes-heat"; + private static final String HEATMAP_LAYER_SOURCE = "earthquakes"; + private static final String CIRCLE_LAYER_ID = "earthquakes-circle"; + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_heatmaplayer); + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + addEarthquakeSource(); + addHeatmapLayer(); + addCircleLayer(); + }); + } + + private void addEarthquakeSource() { + try { + mapboxMap.addSource(new GeoJsonSource(EARTHQUAKE_SOURCE_ID, new URL(EARTHQUAKE_SOURCE_URL))); + } catch (MalformedURLException malformedUrlException) { + Timber.e(malformedUrlException, "That's not an url... "); + } + } + + private void addHeatmapLayer() { + HeatmapLayer layer = new HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID); + layer.setMaxZoom(9); + layer.setSourceLayer(HEATMAP_LAYER_SOURCE); + layer.setProperties( + + // Color ramp for heatmap. Domain is 0 (low) to 1 (high). + // Begin color ramp at 0-stop with a 0-transparancy color + // to create a blur-like effect. + heatmapColor( + interpolate( + linear(), heatmapDensity(), + literal(0), rgba(33, 102, 172, 0), + literal(0.2), rgb(103, 169, 207), + literal(0.4), rgb(209, 229, 240), + literal(0.6), rgb(253, 219, 199), + literal(0.8), rgb(239, 138, 98), + literal(1), rgb(178, 24, 43) + ) + ), + + // Increase the heatmap weight based on frequency and property magnitude + heatmapWeight( + interpolate( + linear(), get("mag"), + stop(0, 0), + stop(6, 1) + ) + ), + + // Increase the heatmap color weight weight by zoom level + // heatmap-intensity is a multiplier on top of heatmap-weight + heatmapIntensity( + interpolate( + linear(), zoom(), + stop(0, 1), + stop(9, 3) + ) + ), + + // Adjust the heatmap radius by zoom level + heatmapRadius( + interpolate( + linear(), zoom(), + stop(0, 2), + stop(9, 20) + ) + ), + + // Transition from heatmap to circle layer by zoom level + heatmapOpacity( + interpolate( + linear(), zoom(), + stop(7, 1), + stop(9, 0) + ) + ) + ); + + mapboxMap.addLayerAbove(layer, "waterway-label"); + } + + private void addCircleLayer() { + CircleLayer circleLayer = new CircleLayer(CIRCLE_LAYER_ID, EARTHQUAKE_SOURCE_ID); + circleLayer.setProperties( + + // Size circle radius by earthquake magnitude and zoom level + circleRadius( + interpolate( + linear(), zoom(), + literal(7), interpolate( + linear(), get("mag"), + stop(1, 1), + stop(6, 4) + ), + literal(16), interpolate( + linear(), get("mag"), + stop(1, 5), + stop(6, 50) + ) + ) + ), + + // Color circle by earthquake magnitude + circleColor( + interpolate( + linear(), get("mag"), + literal(1), rgba(33, 102, 172, 0), + literal(2), rgb(103, 169, 207), + literal(3), rgb(209, 229, 240), + literal(4), rgb(253, 219, 199), + literal(5), rgb(239, 138, 98), + literal(6), rgb(178, 24, 43) + ) + ), + + // Transition from heatmap to circle layer by zoom level + circleOpacity( + interpolate( + linear(), zoom(), + stop(7, 0), + stop(8, 1) + ) + ), + circleStrokeColor("white"), + circleStrokeWidth(1.0f) + ); + + mapboxMap.addLayerBelow(circleLayer, HEATMAP_LAYER_ID); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HillshadeLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HillshadeLayerActivity.java new file mode 100644 index 0000000000..1fab4590b9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/HillshadeLayerActivity.java @@ -0,0 +1,84 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.HillshadeLayer; +import com.mapbox.mapboxsdk.style.sources.RasterDemSource; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity showcasing using HillshadeLayer. + */ +public class HillshadeLayerActivity extends AppCompatActivity { + + private static final String LAYER_ID = "hillshade-layer"; + private static final String LAYER_BELOW_ID = "waterway-river-canal"; + private static final String SOURCE_ID = "hillshade-source"; + private static final String SOURCE_URL = "mapbox://mapbox.terrain-rgb"; + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_fill_extrusion_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + + RasterDemSource rasterDemSource = new RasterDemSource(SOURCE_ID, SOURCE_URL); + mapboxMap.addSource(rasterDemSource); + + HillshadeLayer hillshadeLayer = new HillshadeLayer(LAYER_ID, SOURCE_ID); + mapboxMap.addLayerBelow(hillshadeLayer, LAYER_BELOW_ID); + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RealTimeGeoJsonActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RealTimeGeoJsonActivity.java new file mode 100644 index 0000000000..c25f97cb7f --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RealTimeGeoJsonActivity.java @@ -0,0 +1,125 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; + +import java.net.MalformedURLException; +import java.net.URL; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; + +/** + * Test activity showcasing using realtime GeoJSON to move a symbol on your map + * <p> + * GL-native equivalent of https://www.mapbox.com/mapbox-gl-js/example/live-geojson/ + * </p> + */ +public class RealTimeGeoJsonActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String ID_GEOJSON_LAYER = "wanderdrone"; + private static final String ID_GEOJSON_SOURCE = ID_GEOJSON_LAYER; + private static final String URL_GEOJSON_SOURCE = "https://wanderdrone.appspot.com/"; + + private MapView mapView; + private MapboxMap mapboxMap; + + private Handler handler; + private Runnable runnable; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_default); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(@NonNull final MapboxMap map) { + mapboxMap = map; + + // add source + try { + mapboxMap.addSource(new GeoJsonSource(ID_GEOJSON_SOURCE, new URL(URL_GEOJSON_SOURCE))); + } catch (MalformedURLException malformedUrlException) { + Timber.e(malformedUrlException, "Invalid URL"); + } + + // add layer + SymbolLayer layer = new SymbolLayer(ID_GEOJSON_LAYER, ID_GEOJSON_SOURCE); + layer.setProperties(iconImage("rocket-15")); + mapboxMap.addLayer(layer); + + // loop refresh geojson + handler = new Handler(); + runnable = new RefreshGeoJsonRunnable(mapboxMap, handler); + handler.postDelayed(runnable, 2000); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + handler.removeCallbacks(runnable); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + private static class RefreshGeoJsonRunnable implements Runnable { + + private MapboxMap mapboxMap; + private Handler handler; + + RefreshGeoJsonRunnable(MapboxMap mapboxMap, Handler handler) { + this.mapboxMap = mapboxMap; + this.handler = handler; + } + + @Override + public void run() { + ((GeoJsonSource) mapboxMap.getSource(ID_GEOJSON_SOURCE)).setUrl(URL_GEOJSON_SOURCE); + handler.postDelayed(this, 2000); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleActivity.java new file mode 100644 index 0000000000..dcd9db850b --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleActivity.java @@ -0,0 +1,587 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Toast; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.layers.FillLayer; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.LineLayer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.PropertyValue; +import com.mapbox.mapboxsdk.style.layers.RasterLayer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.layers.TransitionOptions; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.RasterSource; +import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.style.sources.TileSet; +import com.mapbox.mapboxsdk.style.sources.VectorSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.all; +import static com.mapbox.mapboxsdk.style.expressions.Expression.color; +import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; +import static com.mapbox.mapboxsdk.style.expressions.Expression.exponential; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.gte; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.lt; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.toNumber; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.Property.FILL_TRANSLATE_ANCHOR_MAP; +import static com.mapbox.mapboxsdk.style.layers.Property.NONE; +import static com.mapbox.mapboxsdk.style.layers.Property.SYMBOL_PLACEMENT_POINT; +import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.backgroundOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillAntialias; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillOutlineColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.fillTranslateAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineCap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineJoin; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.lineWidth; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.symbolPlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +/** + * Test activity showcasing the runtime style API. + */ +public class RuntimeStyleActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_runtime_style); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + + + mapView.getMapAsync(map -> { + // Store for later + mapboxMap = map; + + // Center and Zoom (Amsterdam, zoomed to streets) + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14)); + + mapboxMap.setTransitionDuration(250); + mapboxMap.setTransitionDelay(50); + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_runtime_style, menu); + return true; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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 R.id.action_list_layers: + listLayers(); + return true; + case R.id.action_list_sources: + listSources(); + return true; + case R.id.action_water_color: + setWaterColor(); + return true; + case R.id.action_background_opacity: + setBackgroundOpacity(); + return true; + case R.id.action_road_avoid_edges: + setRoadSymbolPlacement(); + return true; + case R.id.action_layer_visibility: + setLayerInvisible(); + return true; + case R.id.action_remove_layer: + removeBuildings(); + return true; + case R.id.action_add_parks_layer: + addParksLayer(); + return true; + case R.id.action_add_dynamic_parks_layer: + addDynamicParksLayer(); + return true; + case R.id.action_add_terrain_layer: + addTerrainLayer(); + return true; + case R.id.action_add_satellite_layer: + addSatelliteLayer(); + return true; + case R.id.action_update_water_color_on_zoom: + updateWaterColorOnZoom(); + return true; + case R.id.action_add_custom_tiles: + addCustomTileSource(); + return true; + case R.id.action_fill_filter: + styleFillFilterLayer(); + return true; + case R.id.action_line_filter: + styleLineFilterLayer(); + return true; + case R.id.action_numeric_filter: + styleNumericFillLayer(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private void listLayers() { + List<Layer> layers = mapboxMap.getLayers(); + StringBuilder builder = new StringBuilder("Layers:"); + for (Layer layer : layers) { + builder.append("\n"); + builder.append(layer.getId()); + } + Toast.makeText(this, builder.toString(), Toast.LENGTH_LONG).show(); + } + + private void listSources() { + List<Source> sources = mapboxMap.getSources(); + StringBuilder builder = new StringBuilder("Sources:"); + for (Source source : sources) { + builder.append("\n"); + builder.append(source.getId()); + } + Toast.makeText(this, builder.toString(), Toast.LENGTH_LONG).show(); + } + + private void setLayerInvisible() { + String[] roadLayers = new String[] {"water"}; + for (String roadLayer : roadLayers) { + Layer layer = mapboxMap.getLayer(roadLayer); + if (layer != null) { + layer.setProperties(visibility(NONE)); + } + } + } + + private void setRoadSymbolPlacement() { + // Zoom so that the labels are visible first + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(14), new DefaultCallback() { + @Override + public void onFinish() { + String[] roadLayers = new String[] {"road-label-small", "road-label-medium", "road-label-large"}; + for (String roadLayer : roadLayers) { + Layer layer = mapboxMap.getLayer(roadLayer); + if (layer != null) { + layer.setProperties(symbolPlacement(SYMBOL_PLACEMENT_POINT)); + } + } + } + }); + } + + private void setBackgroundOpacity() { + Layer background = mapboxMap.getLayer("background"); + if (background != null) { + background.setProperties(backgroundOpacity(0.2f)); + } + } + + private void setWaterColor() { + FillLayer water = mapboxMap.getLayerAs("water"); + if (water != null) { + water.setFillColorTransition(new TransitionOptions(7500, 1000)); + water.setProperties( + visibility(VISIBLE), + fillColor(Color.RED) + ); + } else { + Toast.makeText(RuntimeStyleActivity.this, "No water layer in this style", Toast.LENGTH_SHORT).show(); + } + } + + private void removeBuildings() { + // Zoom to see buildings first + mapboxMap.removeLayer("building"); + } + + private void addParksLayer() { + // Add a source + Source source; + try { + source = new GeoJsonSource("amsterdam-spots", ResourceUtils.readRawResource(this, R.raw.amsterdam)); + } catch (IOException ioException) { + Toast.makeText( + RuntimeStyleActivity.this, + "Couldn't add source: " + ioException.getMessage(), + Toast.LENGTH_SHORT).show(); + return; + } + + mapboxMap.addSource(source); + + FillLayer layer = new FillLayer("parksLayer", "amsterdam-spots"); + layer.setProperties( + fillColor(Color.RED), + fillOutlineColor(Color.BLUE), + fillOpacity(0.3f), + fillAntialias(true) + ); + + // Only show me parks (except westerpark with stroke-width == 3) + layer.setFilter(all(eq(get("type"), literal("park")), eq(get("stroke-width"), literal(3)))); + + mapboxMap.addLayerBelow(layer, "building"); + // layer.setPaintProperty(fillColor(Color.RED)); // XXX But not after the object is attached + + // Or get the object later and set it. It's all good. + mapboxMap.getLayer("parksLayer").setProperties(fillColor(Color.RED)); + + // You can get a typed layer, if you're sure it's of that type. Use with care + layer = mapboxMap.getLayerAs("parksLayer"); + // And get some properties + PropertyValue<Boolean> fillAntialias = layer.getFillAntialias(); + Timber.d("Fill anti alias: %s", fillAntialias.getValue()); + layer.setProperties(fillTranslateAnchor(FILL_TRANSLATE_ANCHOR_MAP)); + PropertyValue<String> fillTranslateAnchor = layer.getFillTranslateAnchor(); + Timber.d("Fill translate anchor: %s", fillTranslateAnchor.getValue()); + PropertyValue<String> visibility = layer.getVisibility(); + Timber.d("Visibility: %s", visibility.getValue()); + + // Get a good look at it all + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12)); + } + + private void addDynamicParksLayer() { + // Load some data + FeatureCollection parks; + try { + String json = ResourceUtils.readRawResource(this, R.raw.amsterdam); + parks = FeatureCollection.fromJson(json); + } catch (IOException ioException) { + Toast.makeText( + RuntimeStyleActivity.this, + "Couldn't add source: " + ioException.getMessage(), + Toast.LENGTH_SHORT + ).show(); + return; + } + + // Add an empty source + mapboxMap.addSource(new GeoJsonSource("dynamic-park-source")); + + FillLayer layer = new FillLayer("dynamic-parks-layer", "dynamic-park-source"); + layer.setProperties( + fillColor(Color.GREEN), + fillOutlineColor(Color.GREEN), + fillOpacity(0.8f), + fillAntialias(true) + ); + + // Only show me parks + layer.setFilter(all(eq(get("type"), literal("park")))); + + mapboxMap.addLayer(layer); + + // Get a good look at it all + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12)); + + // Animate the parks source + animateParksSource(parks, 0); + } + + private void animateParksSource(final FeatureCollection parks, final int counter) { + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(() -> { + if (mapboxMap == null) { + return; + } + + Timber.d("Updating parks source"); + // change the source + int park = counter < parks.features().size() - 1 ? counter : 0; + + GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source"); + + if (source == null) { + Timber.e("Source not found"); + Toast.makeText(RuntimeStyleActivity.this, "Source not found", Toast.LENGTH_SHORT).show(); + return; + } + + List<Feature> features = new ArrayList<>(); + features.add(parks.features().get(park)); + source.setGeoJson(FeatureCollection.fromFeatures(features)); + + // Re-post + animateParksSource(parks, park + 1); + }, counter == 0 ? 100 : 1000); + } + + private void addTerrainLayer() { + // Add a source + Source source = new VectorSource("my-terrain-source", "mapbox://mapbox.mapbox-terrain-v2"); + mapboxMap.addSource(source); + + LineLayer layer = new LineLayer("terrainLayer", "my-terrain-source"); + layer.setSourceLayer("contour"); + layer.setProperties( + lineJoin(Property.LINE_JOIN_ROUND), + lineCap(Property.LINE_CAP_ROUND), + lineColor(Color.RED), + lineWidth(20f) + ); + + // adding layers below "road" layers + List<Layer> layers = mapboxMap.getLayers(); + Layer latestLayer = null; + Collections.reverse(layers); + for (Layer currentLayer : layers) { + if (currentLayer instanceof FillLayer && ((FillLayer) currentLayer).getSourceLayer().equals("road")) { + latestLayer = currentLayer; + } else if (currentLayer instanceof CircleLayer && ((CircleLayer) currentLayer).getSourceLayer().equals("road")) { + latestLayer = currentLayer; + } else if (currentLayer instanceof SymbolLayer && ((SymbolLayer) currentLayer).getSourceLayer().equals("road")) { + latestLayer = currentLayer; + } else if (currentLayer instanceof LineLayer && ((LineLayer) currentLayer).getSourceLayer().equals("road")) { + latestLayer = currentLayer; + } + } + + if (latestLayer != null) { + mapboxMap.addLayerBelow(layer, latestLayer.getId()); + } + + // Need to get a fresh handle + layer = mapboxMap.getLayerAs("terrainLayer"); + + // Make sure it's also applied after the fact + layer.setMinZoom(10); + layer.setMaxZoom(15); + + layer = (LineLayer) mapboxMap.getLayer("terrainLayer"); + Toast.makeText(this, String.format( + "Set min/max zoom to %s - %s", layer.getMinZoom(), layer.getMaxZoom()), Toast.LENGTH_SHORT).show(); + } + + private void addSatelliteLayer() { + // Add a source + Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite", 512); + mapboxMap.addSource(source); + + // Add a layer + mapboxMap.addLayer(new RasterLayer("satellite-layer", "my-raster-source")); + } + + private void updateWaterColorOnZoom() { + FillLayer layer = mapboxMap.getLayerAs("water"); + if (layer == null) { + return; + } + + // Set a zoom function to update the color of the water + layer.setProperties( + fillColor( + interpolate( + exponential(0.8f), + zoom(), + stop(1, color(Color.GREEN)), + stop(4, color(Color.BLUE)), + stop(12, color(Color.RED)), + stop(20, color(Color.BLACK)) + ) + ) + ); + + // do some animations to show it off properly + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(1), 1500); + } + + private void addCustomTileSource() { + // Add a source + TileSet tileSet = new TileSet("2.1.0", "https://d25uarhxywzl1j.cloudfront.net/v0.1/{z}/{x}/{y}.mvt"); + tileSet.setMinZoom(0); + tileSet.setMaxZoom(14); + Source source = new VectorSource("custom-tile-source", tileSet); + mapboxMap.addSource(source); + + // Add a layer + LineLayer lineLayer = new LineLayer("custom-tile-layers", "custom-tile-source"); + lineLayer.setSourceLayer("mapillary-sequences"); + lineLayer.setProperties( + lineCap(Property.LINE_CAP_ROUND), + lineJoin(Property.LINE_JOIN_ROUND), + lineOpacity(0.6f), + lineWidth(2.0f), + lineColor(Color.GREEN) + ); + mapboxMap.addLayer(lineLayer); + } + + private void styleFillFilterLayer() { + mapboxMap.setStyleUrl("asset://fill_filter_style.json"); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(31, -100), 3)); + + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(() -> { + if (mapboxMap == null) { + return; + } + + Timber.d("Styling filtered fill layer"); + + FillLayer states = (FillLayer) mapboxMap.getLayer("states"); + + if (states != null) { + states.setFilter(eq(get("name"), literal("Texas"))); + states.setFillOpacityTransition(new TransitionOptions(2500, 0)); + states.setFillColorTransition(new TransitionOptions(2500, 0)); + states.setProperties( + fillColor(Color.RED), + fillOpacity(0.25f) + ); + } else { + Toast.makeText(RuntimeStyleActivity.this, "No states layer in this style", Toast.LENGTH_SHORT).show(); + } + }, 2000); + } + + private void styleLineFilterLayer() { + mapboxMap.setStyleUrl("asset://line_filter_style.json"); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5)); + + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(() -> { + if (mapboxMap == null) { + return; + } + + Timber.d("Styling filtered line layer"); + + LineLayer counties = (LineLayer) mapboxMap.getLayer("counties"); + + if (counties != null) { + counties.setFilter(eq(get("NAME10"), "Washington")); + + counties.setProperties( + lineColor(Color.RED), + lineOpacity(0.75f), + lineWidth(5f) + ); + } else { + Toast.makeText(RuntimeStyleActivity.this, "No counties layer in this style", Toast.LENGTH_SHORT).show(); + } + }, 2000); + } + + private void styleNumericFillLayer() { + mapboxMap.setStyleUrl("asset://numeric_filter_style.json"); + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(40, -97), 5)); + + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(() -> { + if (mapboxMap == null) { + return; + } + + Timber.d("Styling numeric fill layer"); + + FillLayer regions = (FillLayer) mapboxMap.getLayer("regions"); + + if (regions != null) { + regions.setFilter(all( + gte(toNumber(get("HRRNUM")), literal(200)), + lt(toNumber(get("HRRNUM")), literal(300))) + ); + + regions.setProperties( + fillColor(Color.BLUE), + fillOpacity(0.5f) + ); + } else { + Toast.makeText(RuntimeStyleActivity.this, "No regions layer in this style", Toast.LENGTH_SHORT).show(); + } + }, 2000); + } + + private static class DefaultCallback implements MapboxMap.CancelableCallback { + + @Override + public void onCancel() { + // noop + } + + @Override + public void onFinish() { + // noop + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTestActivity.java new file mode 100644 index 0000000000..ce3770d961 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTestActivity.java @@ -0,0 +1,74 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test activity used for instrumentation test execution. + */ +public class RuntimeStyleTestActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_runtime_style); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> RuntimeStyleTestActivity.this.mapboxMap = mapboxMap); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTimingTestActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTimingTestActivity.java new file mode 100644 index 0000000000..dc4825c2db --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/RuntimeStyleTimingTestActivity.java @@ -0,0 +1,96 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Color; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.CircleLayer; +import com.mapbox.mapboxsdk.style.sources.VectorSource; +import com.mapbox.mapboxsdk.testapp.R; + +import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +/** + * Test activity for unit test execution + */ +public class RuntimeStyleTimingTestActivity extends AppCompatActivity { + + public MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_runtime_style); + + // Initialize map as normal + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(mapboxMap -> { + RuntimeStyleTimingTestActivity.this.mapboxMap = mapboxMap; + VectorSource museums = new VectorSource("museums_source", "mapbox://mapbox.2opop9hr"); + mapboxMap.addSource(museums); + + CircleLayer museumsLayer = new CircleLayer("museums", "museums_source"); + museumsLayer.setSourceLayer("museum-cusco"); + museumsLayer.setProperties( + visibility(VISIBLE), + circleRadius(8f), + circleColor(Color.argb(1, 55, 148, 179)) + ); + + mapboxMap.addLayer(museumsLayer); + }); + } + + public MapboxMap getMapboxMap() { + return mapboxMap; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/StyleFileActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/StyleFileActivity.java new file mode 100644 index 0000000000..4067f4ebd5 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/StyleFileActivity.java @@ -0,0 +1,177 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.content.Context; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.AppCompatActivity; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.ref.WeakReference; + +import timber.log.Timber; + +/** + * Test activity showcasing how to use a file:// resource for the style.json and how to use MapboxMap#setStyleJson. + */ +public class StyleFileActivity extends AppCompatActivity { + + private MapboxMap mapboxMap; + private MapView mapView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_style_file); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_file); + fab.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary)); + fab.setOnClickListener(view -> new CreateStyleFileTask(view.getContext(), mapboxMap).execute()); + + FloatingActionButton fabStyleJson = (FloatingActionButton) findViewById(R.id.fab_style_json); + fabStyleJson.setColorFilter(ContextCompat.getColor(StyleFileActivity.this, R.color.primary)); + fabStyleJson.setOnClickListener(view -> new LoadStyleFileTask(view.getContext(), mapboxMap).execute()); + }); + } + + /** + * Task to read a style file from the raw folder + */ + private static class LoadStyleFileTask extends AsyncTask<Void, Void, String> { + private WeakReference<Context> context; + private WeakReference<MapboxMap> mapboxMap; + + LoadStyleFileTask(Context context, MapboxMap mapboxMap) { + this.context = new WeakReference<>(context); + this.mapboxMap = new WeakReference<>(mapboxMap); + } + + @Override + protected String doInBackground(Void... voids) { + String styleJson = ""; + try { + styleJson = ResourceUtils.readRawResource(context.get(), R.raw.sat_style); + } catch (Exception exception) { + Timber.e(exception, "Can't load local file style"); + } + return styleJson; + } + + @Override + protected void onPostExecute(String json) { + super.onPostExecute(json); + Timber.d("Read json, %s", json); + MapboxMap mapboxMap = this.mapboxMap.get(); + if (mapboxMap != null) { + mapboxMap.setStyleJson(json); + } + } + } + + /** + * Task to write a style file to local disk and load it in the map view + */ + private static class CreateStyleFileTask extends AsyncTask<Void, Integer, Long> { + private File cacheStyleFile; + private WeakReference<Context> context; + private WeakReference<MapboxMap> mapboxMap; + + CreateStyleFileTask(Context context, MapboxMap mapboxMap) { + this.context = new WeakReference<>(context); + this.mapboxMap = new WeakReference<>(mapboxMap); + } + + @Override + protected Long doInBackground(Void... params) { + try { + cacheStyleFile = File.createTempFile("my-", ".style.json"); + cacheStyleFile.createNewFile(); + Timber.i("Writing style file to: %s", cacheStyleFile.getAbsolutePath()); + Context context = this.context.get(); + if (context != null) { + writeToFile(cacheStyleFile, ResourceUtils.readRawResource(context, R.raw.local_style)); + } + } catch (Exception exception) { + Toast.makeText(context.get(), "Could not create style file in cache dir", Toast.LENGTH_SHORT).show(); + } + return 1L; + } + + protected void onPostExecute(Long result) { + // Actual file:// usage + MapboxMap mapboxMap = this.mapboxMap.get(); + if (mapboxMap != null) { + mapboxMap.setStyleUrl("file://" + cacheStyleFile.getAbsolutePath()); + } + } + + private void writeToFile(File file, String contents) throws IOException { + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(file)); + writer.write(contents); + } finally { + if (writer != null) { + writer.close(); + } + } + } + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolGeneratorActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolGeneratorActivity.java new file mode 100644 index 0000000000..a1fe353945 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolGeneratorActivity.java @@ -0,0 +1,352 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PointF; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.expressions.Expression; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.List; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.concat; +import static com.mapbox.mapboxsdk.style.expressions.Expression.division; +import static com.mapbox.mapboxsdk.style.expressions.Expression.downcase; +import static com.mapbox.mapboxsdk.style.expressions.Expression.eq; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.match; +import static com.mapbox.mapboxsdk.style.expressions.Expression.number; +import static com.mapbox.mapboxsdk.style.expressions.Expression.pi; +import static com.mapbox.mapboxsdk.style.expressions.Expression.product; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; +import static com.mapbox.mapboxsdk.style.expressions.Expression.step; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.string; +import static com.mapbox.mapboxsdk.style.expressions.Expression.upcase; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ANCHOR_BOTTOM; +import static com.mapbox.mapboxsdk.style.layers.Property.TEXT_ANCHOR_TOP; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize; + +/** + * Test activity showcasing using a symbol generator that generates Bitmaps from Android SDK Views. + */ +public class SymbolGeneratorActivity extends AppCompatActivity implements OnMapReadyCallback { + + private static final String SOURCE_ID = "com.mapbox.mapboxsdk.style.layers.symbol.source.id"; + private static final String LAYER_ID = "com.mapbox.mapboxsdk.style.layers.symbol.layer.id"; + private static final String FEATURE_ID = "brk_name"; + private static final String FEATURE_RANK = "scalerank"; + private static final String FEATURE_NAME = "name_sort"; + private static final String FEATURE_TYPE = "type"; + private static final String FEATURE_REGION = "continent"; + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_symbol_generator); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(final MapboxMap map) { + mapboxMap = map; + addSymbolClickListener(); + new LoadDataTask(this).execute(); + } + + private void addSymbolClickListener() { + mapboxMap.setOnMapClickListener(point -> { + PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> features = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID); + if (!features.isEmpty()) { + Feature feature = features.get(0); + Timber.v("Feature was clicked with data: %s", feature.toJson()); + Toast.makeText( + SymbolGeneratorActivity.this, + "hello from: " + feature.getStringProperty(FEATURE_NAME), + Toast.LENGTH_LONG).show(); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_generator_symbol, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.menu_action_icon_overlap) { + SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID); + layer.setProperties(iconAllowOverlap(!layer.getIconAllowOverlap().getValue())); + return true; + } else if (item.getItemId() == R.id.menu_action_filter) { + SymbolLayer layer = mapboxMap.getLayerAs(LAYER_ID); + layer.setFilter(eq(get(FEATURE_RANK), literal(1))); + Timber.e("Filter that was set: %s", layer.getFilter()); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + /** + * Utility class to generate Bitmaps for Symbol. + * <p> + * Bitmaps can be added to the map with {@link com.mapbox.mapboxsdk.maps.MapboxMap#addImage(String, Bitmap)} + * </p> + */ + private static class SymbolGenerator { + + /** + * Generate a Bitmap from an Android SDK View. + * + * @param view the View to be drawn to a Bitmap + * @return the generated bitmap + */ + public static Bitmap generate(@NonNull View view) { + int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + view.measure(measureSpec, measureSpec); + + int measuredWidth = view.getMeasuredWidth(); + int measuredHeight = view.getMeasuredHeight(); + + view.layout(0, 0, measuredWidth, measuredHeight); + Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888); + bitmap.eraseColor(Color.TRANSPARENT); + Canvas canvas = new Canvas(bitmap); + view.draw(canvas); + return bitmap; + } + } + + private static class LoadDataTask extends AsyncTask<Void, Void, FeatureCollection> { + + private WeakReference<SymbolGeneratorActivity> activity; + + LoadDataTask(SymbolGeneratorActivity activity) { + this.activity = new WeakReference<>(activity); + } + + @Override + protected FeatureCollection doInBackground(Void... params) { + Context context = activity.get(); + if (context != null) { + try { + // read local geojson from raw folder + String tinyCountriesJson = ResourceUtils.readRawResource(context, R.raw.tiny_countries); + return FeatureCollection.fromJson(tinyCountriesJson); + + } catch (IOException exception) { + Timber.e(exception); + } + } + return null; + } + + @Override + protected void onPostExecute(FeatureCollection featureCollection) { + super.onPostExecute(featureCollection); + SymbolGeneratorActivity activity = this.activity.get(); + if (featureCollection == null || activity == null) { + return; + } + + activity.onDataLoaded(featureCollection); + } + } + + public void onDataLoaded(@NonNull FeatureCollection featureCollection) { + // create expressions + Expression iconImageExpression = string(get(literal(FEATURE_ID))); + Expression iconSizeExpression = division(number(get(literal(FEATURE_RANK))), literal(2.0f)); + Expression textSizeExpression = product(get(literal(FEATURE_RANK)), pi()); + Expression textFieldExpression = concat(upcase(literal("a ")), upcase(string(get(literal(FEATURE_TYPE)))), + downcase(literal(" IN ")), string(get(literal(FEATURE_REGION))) + ); + Expression textColorExpression = match(get(literal(FEATURE_RANK)), + literal(1), rgba(255, 0, 0, 1.0f), + literal(2), rgba(0, 0, 255.0f, 1.0f), + rgba(0.0f, 255.0f, 0.0f, 1.0f) + ); + + rgba( + division(literal(255), get(FEATURE_RANK)), + literal(0.0f), + literal(0.0f), + literal(1.0f) + ); + + // create symbol layer + SymbolLayer symbolLayer = new SymbolLayer(LAYER_ID, SOURCE_ID) + .withProperties( + // icon configuration + iconImage(iconImageExpression), + iconAllowOverlap(false), + iconSize(iconSizeExpression), + iconAnchor(ICON_ANCHOR_BOTTOM), + iconOffset(step(zoom(), literal(new float[] {0f, 0f}), + stop(1, new Float[] {0f, 0f}), + stop(10, new Float[] {0f, -35f}) + )), + + // text field configuration + textField(textFieldExpression), + textSize(textSizeExpression), + textAnchor(TEXT_ANCHOR_TOP), + textColor(textColorExpression) + ); + + // add a geojson source to the map + Source source = new GeoJsonSource(SOURCE_ID, featureCollection); + mapboxMap.addSource(source); + + // add symbol layer + mapboxMap.addLayer(symbolLayer); + + // get expressions + Expression iconImageExpressionResult = symbolLayer.getIconImage().getExpression(); + Expression iconSizeExpressionResult = symbolLayer.getIconSize().getExpression(); + Expression textSizeExpressionResult = symbolLayer.getTextSize().getExpression(); + Expression textFieldExpressionResult = symbolLayer.getTextField().getExpression(); + Expression textColorExpressionResult = symbolLayer.getTextColor().getExpression(); + + // log expressions + Timber.e(iconImageExpressionResult.toString()); + Timber.e(iconSizeExpressionResult.toString()); + Timber.e(textSizeExpressionResult.toString()); + Timber.e(textFieldExpressionResult.toString()); + Timber.e(textColorExpressionResult.toString()); + + // reset expressions + symbolLayer.setProperties( + iconImage(iconImageExpressionResult), + iconSize(iconSizeExpressionResult), + textSize(textSizeExpressionResult), + textField(textFieldExpressionResult), + textColor(textColorExpressionResult) + ); + + new GenerateSymbolTask(mapboxMap, this).execute(featureCollection); + } + + private static class GenerateSymbolTask extends AsyncTask<FeatureCollection, Void, HashMap<String, Bitmap>> { + + private MapboxMap mapboxMap; + private WeakReference<Context> context; + + GenerateSymbolTask(MapboxMap mapboxMap, Context context) { + this.mapboxMap = mapboxMap; + this.context = new WeakReference<>(context); + } + + @SuppressWarnings("WrongThread") + @Override + protected HashMap<String, Bitmap> doInBackground(FeatureCollection... params) { + HashMap<String, Bitmap> imagesMap = new HashMap<>(); + Context context = this.context.get(); + List<Feature> features = params[0].features(); + if (context != null && features != null) { + for (Feature feature : features) { + String countryName = feature.getStringProperty(FEATURE_ID); + TextView textView = new TextView(context); + textView.setBackgroundColor(context.getResources().getColor(R.color.blueAccent)); + textView.setPadding(10, 5, 10, 5); + textView.setTextColor(Color.WHITE); + textView.setText(countryName); + imagesMap.put(countryName, SymbolGenerator.generate(textView)); + } + } + return imagesMap; + } + + @Override + protected void onPostExecute(HashMap<String, Bitmap> bitmapHashMap) { + super.onPostExecute(bitmapHashMap); + mapboxMap.addImages(bitmapHashMap); + } + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolLayerActivity.java new file mode 100644 index 0000000000..d3cddeefb3 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/SymbolLayerActivity.java @@ -0,0 +1,214 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.Bitmap; +import android.graphics.Color; +import android.graphics.PointF; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; + +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.utils.BitmapUtils; + + +import java.util.Arrays; +import java.util.List; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.any; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.has; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.lte; +import static com.mapbox.mapboxsdk.style.expressions.Expression.not; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textField; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textFont; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.textSize; + +/** + * Test activity showcasing runtime manipulation of symbol layers. + */ +public class SymbolLayerActivity extends AppCompatActivity implements MapboxMap.OnMapClickListener { + + public static final String MARKER_SOURCE = "marker-source"; + public static final String MARKER_LAYER = "marker-layer"; + private MapboxMap mapboxMap; + private MapView mapView; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_symbollayer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + + // Add a sdf image for the makers + Drawable icLayersDrawable = getResources().getDrawable(R.drawable.ic_layers); + Bitmap icLayersBitmap = BitmapUtils.getBitmapFromDrawable(icLayersDrawable); + mapboxMap.addImage( + "my-layers-image", + icLayersBitmap, + true + ); + + // Add a source + FeatureCollection markers = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.35673), featureProperties("Marker 1")), + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("Marker 2")) + }); + mapboxMap.addSource(new GeoJsonSource(MARKER_SOURCE, markers)); + + // Add the symbol-layer + mapboxMap.addLayer( + new SymbolLayer(MARKER_LAYER, MARKER_SOURCE) + .withProperties( + iconImage("my-layers-image"), + iconAllowOverlap(true), + iconAnchor(Property.ICON_ANCHOR_BOTTOM), + textField(get("title")), + iconColor(Color.RED), + textColor(Color.RED), + textAnchor(Property.TEXT_ANCHOR_TOP), + textSize(10f) + ).withFilter((any(not(has("price")), lte(get("price"), literal(25))))) + ); + + // Show + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.35273, 4.91638), 14)); + + // Set a click-listener so we can manipulate the map + mapboxMap.setOnMapClickListener(SymbolLayerActivity.this); + }); + } + + @Override + public void onMapClick(@NonNull LatLng point) { + // Query which features are clicked + PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> features = mapboxMap.queryRenderedFeatures(screenLoc, MARKER_LAYER); + + SymbolLayer layer = mapboxMap.getLayerAs(MARKER_LAYER); + if (features.size() == 0) { + // Reset + layer.setProperties(iconSize(1f)); + } else { + layer.setProperties(iconSize(3f)); + } + } + + private void toggleTextSize() { + SymbolLayer layer = mapboxMap.getLayerAs(MARKER_LAYER); + layer.setProperties(layer.getTextSize().getValue() > 10 ? textSize(10f) : textSize(20f)); + } + + private void toggleTextField() { + SymbolLayer layer = mapboxMap.getLayerAs(MARKER_LAYER); + layer.setProperties("{title}".equals(layer.getTextField().getValue()) ? textField("āA") : textField("{title}")); + } + + private void toggleTextFont() { + SymbolLayer layer = mapboxMap.getLayerAs(MARKER_LAYER); + + String[] fonts = layer.getTextFont().getValue(); + if (fonts == null || fonts.length == 0 || Arrays.asList(fonts).contains("Arial Unicode MS Regular")) { + layer.setProperties(textFont(new String[] {"DIN Offc Pro Bold", "Arial Unicode MS Bold"})); + } else { + layer.setProperties(textFont(new String[] {"DIN Offc Pro Medium", "Arial Unicode MS Regular"})); + } + } + + private JsonObject featureProperties(String title) { + JsonObject object = new JsonObject(); + object.add("title", new JsonPrimitive(title)); + return object; + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_symbol_layer, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_toggle_text_size: + toggleTextSize(); + return true; + case R.id.action_toggle_text_field: + toggleTextField(); + return true; + case R.id.action_toggle_text_font: + toggleTextFont(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/ZoomFunctionSymbolLayerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/ZoomFunctionSymbolLayerActivity.java new file mode 100644 index 0000000000..b2552789cd --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/style/ZoomFunctionSymbolLayerActivity.java @@ -0,0 +1,191 @@ +package com.mapbox.mapboxsdk.maps.activity.style; + +import android.graphics.PointF; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.Menu; +import android.view.MenuItem; +import com.google.gson.JsonObject; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.testapp.R; +import timber.log.Timber; + +import java.util.List; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.step; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +/** + * Test activity showcasing changing the icon with a zoom function and adding selection state to a SymbolLayer. + */ +public class ZoomFunctionSymbolLayerActivity extends AppCompatActivity { + + private static final String LAYER_ID = "symbolLayer"; + private static final String SOURCE_ID = "poiSource"; + private static final String BUS_MAKI_ICON_ID = "bus-11"; + private static final String CAFE_MAKI_ICON_ID = "cafe-11"; + private static final String KEY_PROPERTY_SELECTED = "selected"; + private static final float ZOOM_STOP_MAX_VALUE = 12.0f; + + private MapView mapView; + private MapboxMap mapboxMap; + private GeoJsonSource source; + private SymbolLayer layer; + + private boolean isInitialPosition = true; + private boolean isSelected = false; + private boolean isShowingSymbolLayer = true; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_zoom_symbol_layer); + + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + updateSource(); + addLayer(); + addMapClickListener(); + }); + } + + private void updateSource() { + FeatureCollection featureCollection = createFeatureCollection(); + if (source != null) { + source.setGeoJson(featureCollection); + } else { + source = new GeoJsonSource(SOURCE_ID, featureCollection); + mapboxMap.addSource(source); + } + } + + private void toggleSymbolLayerVisibility() { + layer.setProperties( + visibility(isShowingSymbolLayer ? Property.NONE : Property.VISIBLE) + ); + isShowingSymbolLayer = !isShowingSymbolLayer; + } + + private FeatureCollection createFeatureCollection() { + Point point = isInitialPosition + ? Point.fromLngLat(-74.01618140, 40.701745) + : Point.fromLngLat(-73.988097, 40.749864); + + JsonObject properties = new JsonObject(); + properties.addProperty(KEY_PROPERTY_SELECTED, isSelected); + Feature feature = Feature.fromGeometry(point, properties); + return FeatureCollection.fromFeatures(new Feature[] {feature}); + } + + private void addLayer() { + layer = new SymbolLayer(LAYER_ID, SOURCE_ID); + layer.setProperties( + iconImage( + step(zoom(), literal(BUS_MAKI_ICON_ID), + stop(ZOOM_STOP_MAX_VALUE, CAFE_MAKI_ICON_ID) + ) + ), + iconSize( + switchCase( + get(KEY_PROPERTY_SELECTED), literal(3.0f), + literal(1.0f) + ) + ), + iconAllowOverlap(true) + ); + mapboxMap.addLayer(layer); + } + + private void addMapClickListener() { + mapboxMap.setOnMapClickListener(point -> { + PointF screenPoint = mapboxMap.getProjection().toScreenLocation(point); + List<Feature> featureList = mapboxMap.queryRenderedFeatures(screenPoint, LAYER_ID); + if (!featureList.isEmpty()) { + Feature feature = featureList.get(0); + boolean selectedNow = feature.getBooleanProperty(KEY_PROPERTY_SELECTED); + isSelected = !selectedNow; + updateSource(); + } else { + Timber.e("No features found"); + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_symbols, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (mapboxMap != null) { + if (item.getItemId() == R.id.menu_action_change_location) { + isInitialPosition = !isInitialPosition; + updateSource(); + } else if (item.getItemId() == R.id.menu_action_toggle_source) { + toggleSymbolLayerVisibility(); + } + } + return super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewAnimationActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewAnimationActivity.java new file mode 100644 index 0000000000..0f2c3e7c94 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewAnimationActivity.java @@ -0,0 +1,145 @@ +package com.mapbox.mapboxsdk.maps.activity.textureview; + +import android.animation.ObjectAnimator; +import android.os.Bundle; +import android.os.Handler; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.Locale; + +/** + * Test animating a {@link android.view.TextureView} backed map. + */ +public class TextureViewAnimationActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + private Handler handler; + private Runnable delayed; + + private static LatLng[] PLACES = { + new LatLng(37.7749, -122.4194), // SF + new LatLng(38.9072, -77.0369), // DC + new LatLng(52.3702, 4.8952), // AMS + new LatLng(60.1699, 24.9384), // HEL + new LatLng(-13.1639, -74.2236), // AYA + new LatLng(52.5200, 13.4050), // BER + new LatLng(12.9716, 77.5946), // BAN + new LatLng(31.2304, 121.4737) // SHA + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_textureview_animate); + handler = new Handler(getMainLooper()); + setupToolbar(); + setupMapView(savedInstanceState); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.getMapAsync(mapboxMap -> { + TextureViewAnimationActivity.this.mapboxMap = mapboxMap; + + setFpsView(mapboxMap); + + // Animate the map view + ObjectAnimator animation = ObjectAnimator.ofFloat(mapView, "rotationY", 0.0f, 360f); + animation.setDuration(3600); + animation.setRepeatCount(ObjectAnimator.INFINITE); + animation.start(); + + // Start an animation on the map as well + flyTo(mapboxMap, 0, 14); + }); + } + + private void flyTo(final MapboxMap mapboxMap, final int place, final double zoom) { + mapboxMap.animateCamera( + CameraUpdateFactory.newLatLngZoom(PLACES[place], zoom), + 10000, + new MapboxMap.CancelableCallback() { + @Override + public void onCancel() { + delayed = () -> { + delayed = null; + flyTo(mapboxMap, place, zoom); + }; + handler.postDelayed(delayed, 2000); + } + + @Override + public void onFinish() { + flyTo(mapboxMap, place == (PLACES.length - 1) ? 0 : place + 1, zoom); + } + }); + } + + private void setFpsView(MapboxMap mapboxMap) { + final TextView fpsView = (TextView) findViewById(R.id.fpsView); + mapboxMap.setOnFpsChangedListener(fps -> fpsView.setText(String.format(Locale.US, "FPS: %4.2f", fps))); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + if (handler != null && delayed != null) { + handler.removeCallbacks(delayed); + } + } + + @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/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewDebugModeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewDebugModeActivity.java new file mode 100644 index 0000000000..c202ed866c --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewDebugModeActivity.java @@ -0,0 +1,265 @@ +package com.mapbox.mapboxsdk.maps.activity.textureview; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ListView; +import android.widget.TextView; + +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.testapp.R; + +import java.util.List; +import java.util.Locale; + +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; + +/** + * Test activity showcasing the different debug modes and allows to cycle between the default map styles. + */ +public class TextureViewDebugModeActivity extends AppCompatActivity implements OnMapReadyCallback { + + private MapView mapView; + private MapboxMap mapboxMap; + private ActionBarDrawerToggle actionBarDrawerToggle; + private int currentStyleIndex = 0; + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.OUTDOORS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE, + Style.SATELLITE_STREETS, + Style.TRAFFIC_DAY, + Style.TRAFFIC_NIGHT + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_textureview_debug_mode); + setupToolbar(); + setupMapView(savedInstanceState); + setupDebugChangeView(); + setupStyleChangeView(); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + actionBarDrawerToggle = new ActionBarDrawerToggle(this, + drawerLayout, + R.string.navigation_drawer_open, + R.string.navigation_drawer_close + ); + actionBarDrawerToggle.setDrawerIndicatorEnabled(true); + actionBarDrawerToggle.syncState(); + } + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.addOnMapChangedListener(change -> { + if (change == MapView.DID_FINISH_LOADING_STYLE && mapboxMap != null) { + Timber.v("New style loaded with JSON: %s", mapboxMap.getStyleJson()); + setupNavigationView(mapboxMap.getLayers()); + } + }); + + mapView.setTag(true); + mapView.setStyleUrl(STYLES[currentStyleIndex]); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap map) { + mapboxMap = map; + mapboxMap.getUiSettings().setZoomControlsEnabled(true); + + setupNavigationView(mapboxMap.getLayers()); + setupZoomView(); + setFpsView(); + } + + private void setFpsView() { + final TextView fpsView = (TextView) findViewById(R.id.fpsView); + mapboxMap.setOnFpsChangedListener(fps -> fpsView.setText(String.format(Locale.US,"FPS: %4.2f", fps))); + } + + private void setupNavigationView(List<Layer> layerList) { + final LayerListAdapter adapter = new LayerListAdapter(this, layerList); + ListView listView = (ListView) findViewById(R.id.listView); + listView.setAdapter(adapter); + listView.setOnItemClickListener((parent, view, position, id) -> { + Layer clickedLayer = adapter.getItem(position); + toggleLayerVisibility(clickedLayer); + closeNavigationView(); + }); + } + + private void toggleLayerVisibility(Layer layer) { + boolean isVisible = layer.getVisibility().getValue().equals(Property.VISIBLE); + layer.setProperties( + visibility( + isVisible ? Property.NONE : Property.VISIBLE + ) + ); + } + + private void closeNavigationView() { + DrawerLayout drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + drawerLayout.closeDrawers(); + } + + private void setupZoomView() { + final TextView textView = (TextView) findViewById(R.id.textZoom); + mapboxMap.setOnCameraChangeListener(position -> + textView.setText(String.format(getString(R.string.debug_zoom), position.zoom))); + } + + private void setupDebugChangeView() { + FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabDebug); + fabDebug.setOnClickListener(view -> { + if (mapboxMap != null) { + Timber.d("Debug FAB: isDebug Active? %s", mapboxMap.isDebugActive()); + mapboxMap.cycleDebugOptions(); + } + }); + } + + private void setupStyleChangeView() { + FloatingActionButton fabStyles = (FloatingActionButton) findViewById(R.id.fabStyles); + fabStyles.setOnClickListener(view -> { + if (mapboxMap != null) { + currentStyleIndex++; + if (currentStyleIndex == STYLES.length) { + currentStyleIndex = 0; + } + mapboxMap.setStyleUrl(STYLES[currentStyleIndex], style -> Timber.d("Style loaded %s", style)); + } + }); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + + private static class LayerListAdapter extends BaseAdapter { + + private LayoutInflater layoutInflater; + private List<Layer> layers; + + LayerListAdapter(Context context, List<Layer> layers) { + this.layoutInflater = LayoutInflater.from(context); + this.layers = layers; + } + + @Override + public int getCount() { + return layers.size(); + } + + @Override + public Layer getItem(int position) { + return layers.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + Layer layer = layers.get(position); + View view = convertView; + if (view == null) { + view = layoutInflater.inflate(android.R.layout.simple_list_item_2, parent, false); + ViewHolder holder = new ViewHolder( + (TextView) view.findViewById(android.R.id.text1), + (TextView) view.findViewById(android.R.id.text2) + ); + view.setTag(holder); + } + ViewHolder holder = (ViewHolder) view.getTag(); + holder.text.setText(layer.getClass().getSimpleName()); + holder.subText.setText(layer.getId()); + return view; + } + + private static class ViewHolder { + final TextView text; + final TextView subText; + + ViewHolder(TextView text, TextView subText) { + this.text = text; + this.subText = subText; + } + } + } +} diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewResizeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewResizeActivity.java new file mode 100644 index 0000000000..bbb0d32714 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewResizeActivity.java @@ -0,0 +1,98 @@ +package com.mapbox.mapboxsdk.maps.activity.textureview; + +import android.os.Bundle; +import android.support.design.widget.CoordinatorLayout; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; + +/** + * Test resizing a {@link android.view.TextureView} backed map on the fly. + */ +public class TextureViewResizeActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_textureview_resize); + setupToolbar(); + setupMapView(savedInstanceState); + setupFab(); + } + + private void setupToolbar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.getMapAsync(mapboxMap -> TextureViewResizeActivity.this.mapboxMap = mapboxMap); + } + + private void setupFab() { + FloatingActionButton fabDebug = (FloatingActionButton) findViewById(R.id.fabResize); + fabDebug.setOnClickListener(view -> { + if (mapView != null) { + View parent = findViewById(R.id.coordinator_layout); + int width = parent.getWidth() == mapView.getWidth() ? parent.getWidth() / 2 : parent.getWidth(); + int height = parent.getHeight() == mapView.getHeight() ? parent.getHeight() / 2 : parent.getHeight(); + mapView.setLayoutParams(new CoordinatorLayout.LayoutParams(width, height)); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewTransparentBackgroundActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewTransparentBackgroundActivity.java new file mode 100644 index 0000000000..8322abfefc --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/maps/activity/textureview/TextureViewTransparentBackgroundActivity.java @@ -0,0 +1,94 @@ +package com.mapbox.mapboxsdk.maps.activity.textureview; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.widget.ImageView; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.mapboxsdk.maps.utils.ResourceUtils; + +import java.io.IOException; + +import timber.log.Timber; + +/** + * Example showcasing how to create a TextureView with a transparent background. + */ +public class TextureViewTransparentBackgroundActivity extends AppCompatActivity { + + private MapView mapView; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_textureview_transparent); + setupBackground(); + setupMapView(savedInstanceState); + } + + private void setupBackground() { + ImageView imageView = (ImageView) findViewById(R.id.imageView); + imageView.setImageResource(R.drawable.water); + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + } + + private void setupMapView(Bundle savedInstanceState) { + mapView = (MapView) findViewById(R.id.mapView); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(map -> { + mapboxMap = map; + + try { + map.setStyleJson(ResourceUtils.readRawResource(getApplicationContext(), R.raw.no_bg_style)); + } catch (IOException exception) { + Timber.e(exception); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + } + + @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(); + } + +}
\ No newline at end of file |