From fd86627219abcfdc7b67c6d89bdda53cc06c7d76 Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 4 Apr 2018 17:43:02 +0300 Subject: [android] test for #11556 --- .../src/main/AndroidManifest.xml | 1 + .../annotation/AnimatedMarkerActivity.java | 425 ++++++++++++++------- 2 files changed, 291 insertions(+), 135 deletions(-) diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 70a6264d22..beae7d4a29 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -69,6 +69,7 @@ android:value=".activity.FeatureOverviewActivity" /> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java index e6db071141..1874cf728b 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedMarkerActivity.java @@ -2,53 +2,68 @@ package com.mapbox.mapboxsdk.testapp.activity.annotation; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; +import android.graphics.drawable.BitmapDrawable; import android.os.Bundle; -import android.support.annotation.DrawableRes; -import android.support.v4.content.res.ResourcesCompat; import android.support.v7.app.AppCompatActivity; -import android.view.View; 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.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerView; -import com.mapbox.mapboxsdk.annotations.MarkerViewManager; -import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; 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.mapboxsdk.testapp.utils.IconUtils; 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 AnimatedMarkerActivity 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 LatLng dupontCircle = new LatLng(38.90962, -77.04341); - - private Marker passengerMarker = null; - private MarkerView carMarker = null; + private List randomCars = new ArrayList<>(); + private GeoJsonSource randomCarSource; + private Car taxi; + private GeoJsonSource taxiSource; + private LatLng passenger; - private Runnable animationRunnable; - - private List markerViews = new ArrayList<>(); - private boolean stopped; + private List animators = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -59,130 +74,254 @@ public class AnimatedMarkerActivity extends AppCompatActivity { mapView.onCreate(savedInstanceState); mapView.getMapAsync(mapboxMap -> { AnimatedMarkerActivity.this.mapboxMap = mapboxMap; - setupMap(); - - animationRunnable = () -> { - for (int i = 0; i < 10; i++) { - addRandomCar(); - } - addPassenger(); - addMainCar(); - }; - mapView.post(animationRunnable); + setupCars(); + animateRandomRoutes(); + animateTaxi(); }); } - private void setupMap() { - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(dupontCircle) - .zoom(15) - .build(); - mapboxMap.setCameraPosition(cameraPosition); + private void setupCars() { + addRandomCars(); + addPassenger(); + addMainCar(); } - private void addPassenger() { - if (isActivityStopped()) { - return; - } + 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(); + } + }); + } - LatLng randomLatLng = getLatLngInBounds(); + valueAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + super.onAnimationStart(animation); + car.feature.properties().addProperty("bearing", Car.getBearing(car.current, car.next)); + } + }); - if (passengerMarker == null) { - Icon icon = IconUtils.drawableToIcon(this, R.drawable.ic_directions_run_black, - ResourcesCompat.getColor(getResources(), R.color.blueAccent, getTheme())); - passengerMarker = mapboxMap.addMarker(new MarkerViewOptions() - .position(randomLatLng) - .icon(icon)); - } else { - passengerMarker.setPosition(randomLatLng); - } - } + 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(); - private void addMainCar() { - if (isActivityStopped()) { - return; + animators.add(valueAnimator); } + } - LatLng randomLatLng = getLatLngInBounds(); + private void animateTaxi() { + ValueAnimator valueAnimator = ValueAnimator.ofObject(new LatLngEvaluator(), taxi.current, taxi.next); + valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - if (carMarker == null) { - carMarker = createCarMarker(randomLatLng, R.drawable.ic_taxi_top, - markerView -> { - // Make sure the car marker is selected so that it's always brought to the front (#5285) - mapboxMap.selectMarker(carMarker); - animateMoveToPassenger(carMarker); - }); - markerViews.add(carMarker); - } else { - carMarker.setPosition(randomLatLng); - } - } + private LatLng latLng; - private void animateMoveToPassenger(final MarkerView car) { - if (isActivityStopped()) { - return; - } + @Override + public void onAnimationUpdate(ValueAnimator animation) { + latLng = (LatLng) animation.getAnimatedValue(); + taxi.current = latLng; + updateTaxiSource(); + } + }); - ValueAnimator animator = animateMoveMarker(car, passengerMarker.getPosition()); - animator.addListener(new AnimatorListenerAdapter() { + valueAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { - addPassenger(); - animateMoveToPassenger(car); + 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); } - protected void addRandomCar() { - markerViews.add(createCarMarker(getLatLngInBounds(), R.drawable.ic_car_top, - markerView -> randomlyMoveMarker(markerView))); + private void updatePassenger() { + passenger = getLatLngInBounds(); + updatePassengerSource(); + taxi.setNext(passenger); } - private void randomlyMoveMarker(final MarkerView marker) { - if (isActivityStopped()) { - return; - } + 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); + } - ValueAnimator animator = animateMoveMarker(marker, getLatLngInBounds()); + private void updateTaxiSource() { + taxi.updateFeature(); + taxiSource.setGeoJson(taxi.feature); + } - // Add listener to restart animation on end - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - randomlyMoveMarker(marker); + 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 features = new ArrayList<>(); + for (Car randomCarsRoute : randomCars) { + features.add(randomCarsRoute.feature); + } + return FeatureCollection.fromFeatures(features); } - private ValueAnimator animateMoveMarker(final MarkerView marker, LatLng to) { - marker.setRotation((float) getBearing(marker.getPosition(), to)); + private long getDuration() { + return random.nextInt(DURATION_RANDOM_MAX) + DURATION_BASE; + } - final ValueAnimator markerAnimator = ObjectAnimator.ofObject( - marker, "position", new LatLngEvaluator(), marker.getPosition(), to); - markerAnimator.setDuration((long) (10 * marker.getPosition().distanceTo(to))); - markerAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + private void addRandomCars() { + LatLng latLng; + LatLng next; + for (int i = 0; i < 3000; 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()) + ); + } - // Start - markerAnimator.start(); + 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("bearing")), + iconIgnorePlacement(true) + ); - return markerAnimator; + mapboxMap.addLayerBelow(symbolLayer, WATERWAY_LAYER_ID); } - private MarkerView createCarMarker(LatLng start, @DrawableRes int carResource, - MarkerViewManager.OnMarkerViewAddedListener listener) { - Icon icon = IconFactory.getInstance(AnimatedMarkerActivity.this) - .fromResource(carResource); + private void addPassenger() { + passenger = getLatLngInBounds(); + FeatureCollection featureCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry( + Point.fromLngLat( + passenger.getLongitude(), + passenger.getLatitude() + ) + ) + }); - // View Markers - return mapboxMap.addMarker(new MarkerViewOptions() - .position(start) - .icon(icon), listener); + mapboxMap.addImage(PASSENGER, + ((BitmapDrawable) getResources().getDrawable(R.drawable.icon_burned)).getBitmap()); - // GL Markers -// return mapboxMap.addMarker(new MarkerOptions() -// .position(start) -// .icon(icon)); + 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() { @@ -216,23 +355,7 @@ public class AnimatedMarkerActivity extends AppCompatActivity { @Override protected void onStop() { super.onStop(); - - stopped = true; - - // Stop ongoing animations, prevent memory leaks - if (mapboxMap != null) { - MarkerViewManager markerViewManager = mapboxMap.getMarkerViewManager(); - for (MarkerView markerView : markerViews) { - View view = markerViewManager.getView(markerView); - if (view != null) { - view.animate().cancel(); - } - } - } - - // onStop mapView.onStop(); - mapView.removeCallbacks(animationRunnable); } @Override @@ -244,6 +367,14 @@ public class AnimatedMarkerActivity extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); + + for (Animator animator : animators) { + if (animator != null) { + animator.removeAllListeners(); + animator.cancel(); + } + } + mapView.onDestroy(); } @@ -270,14 +401,38 @@ public class AnimatedMarkerActivity extends AppCompatActivity { } } - private double getBearing(LatLng from, LatLng to) { - return TurfMeasurement.bearing( - Point.fromLngLat(from.getLongitude(), from.getLatitude()), - Point.fromLngLat(to.getLongitude(), to.getLatitude()) - ); - } - private boolean isActivityStopped() { - return stopped; + 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 -- cgit v1.2.1