[android] immediate location animation based on map's projection
This commit is contained in:
committed by
Łukasz Paczos
parent
bbeec8dacb
commit
e86d1b2ac9
@@ -13,6 +13,7 @@ import android.view.animation.LinearInterpolator;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -39,12 +40,17 @@ final class LocationAnimatorCoordinator {
|
||||
final List<MapboxAnimator.OnLayerAnimationsValuesChangeListener> layerListeners = new ArrayList<>();
|
||||
final List<MapboxAnimator.OnCameraAnimationsValuesChangeListener> cameraListeners = new ArrayList<>();
|
||||
|
||||
private final Projection projection;
|
||||
private Location previousLocation;
|
||||
private float previousAccuracyRadius = -1;
|
||||
private float previousCompassBearing = -1;
|
||||
private long locationUpdateTimestamp = -1;
|
||||
private float durationMultiplier;
|
||||
|
||||
LocationAnimatorCoordinator(Projection projection) {
|
||||
this.projection = projection;
|
||||
}
|
||||
|
||||
void addLayerListener(MapboxAnimator.OnLayerAnimationsValuesChangeListener listener) {
|
||||
layerListeners.add(listener);
|
||||
}
|
||||
@@ -81,8 +87,8 @@ final class LocationAnimatorCoordinator {
|
||||
updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing);
|
||||
updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing);
|
||||
|
||||
boolean snap = immediateAnimation(previousCameraLatLng, targetLatLng, currentCameraPosition.zoom)
|
||||
|| immediateAnimation(previousLayerLatLng, targetLatLng, currentCameraPosition.zoom);
|
||||
boolean snap = immediateAnimation(projection, previousCameraLatLng, targetLatLng)
|
||||
|| immediateAnimation(projection, previousLayerLatLng, targetLatLng);
|
||||
playLocationAnimators(snap ? 0 : getAnimationDuration());
|
||||
|
||||
previousLocation = newLocation;
|
||||
@@ -317,7 +323,7 @@ final class LocationAnimatorCoordinator {
|
||||
createNewAnimator(ANIMATOR_CAMERA_LATLNG,
|
||||
new CameraLatLngAnimator(previousCameraTarget, currentTarget, cameraListeners));
|
||||
|
||||
return immediateAnimation(previousCameraTarget, currentTarget, currentCameraPosition.zoom);
|
||||
return immediateAnimation(projection, previousCameraTarget, currentTarget);
|
||||
}
|
||||
|
||||
private void resetCameraGpsBearingAnimation(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
|
||||
|
||||
@@ -129,7 +129,7 @@ final class LocationCameraController implements MapboxAnimator.OnCameraAnimation
|
||||
};
|
||||
|
||||
CameraPosition currentPosition = mapboxMap.getCameraPosition();
|
||||
if (Utils.immediateAnimation(currentPosition.target, target, currentPosition.zoom)) {
|
||||
if (Utils.immediateAnimation(mapboxMap.getProjection(), currentPosition.target, target)) {
|
||||
mapboxMap.moveCamera(
|
||||
update,
|
||||
callback);
|
||||
|
||||
@@ -1015,7 +1015,7 @@ public final class LocationComponent {
|
||||
locationCameraController = new LocationCameraController(
|
||||
context, mapboxMap, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener);
|
||||
|
||||
locationAnimatorCoordinator = new LocationAnimatorCoordinator();
|
||||
locationAnimatorCoordinator = new LocationAnimatorCoordinator(mapboxMap.getProjection());
|
||||
locationAnimatorCoordinator.addLayerListener(locationLayerController);
|
||||
locationAnimatorCoordinator.addCameraListener(locationCameraController);
|
||||
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options
|
||||
|
||||
@@ -24,7 +24,7 @@ final class LocationComponentConstants {
|
||||
static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250;
|
||||
|
||||
// Threshold value to perform immediate camera/layer position update.
|
||||
static final double INSTANT_LOCATION_TRANSITION_THRESHOLD = 500_000;
|
||||
static final double INSTANT_LOCATION_TRANSITION_THRESHOLD = 50_000;
|
||||
|
||||
// Default interval between location updates
|
||||
static final long DEFAULT_INTERVAL_MILLIS = 1000;
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.support.v4.content.ContextCompat;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.Projection;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.INSTANT_LOCATION_TRANSITION_THRESHOLD;
|
||||
|
||||
@@ -92,13 +93,10 @@ public final class Utils {
|
||||
return (float) (location.getAccuracy() * (1 / metersPerPixel));
|
||||
}
|
||||
|
||||
static boolean immediateAnimation(LatLng current, @NonNull LatLng target, double zoom) {
|
||||
// TODO: calculate the value based on the projection
|
||||
static boolean immediateAnimation(@NonNull Projection projection, @NonNull LatLng current, @NonNull LatLng target) {
|
||||
double metersPerPixel = projection.getMetersPerPixelAtLatitude((current.getLatitude() + target.getLatitude()) / 2);
|
||||
double distance = current.distanceTo(target);
|
||||
if (zoom > 10) {
|
||||
distance *= zoom;
|
||||
}
|
||||
return distance > INSTANT_LOCATION_TRANSITION_THRESHOLD;
|
||||
return distance / metersPerPixel > INSTANT_LOCATION_TRANSITION_THRESHOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,12 +6,14 @@ import com.mapbox.mapboxsdk.geometry.LatLng
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION
|
||||
import com.mapbox.mapboxsdk.location.MapboxAnimator.*
|
||||
import com.mapbox.mapboxsdk.maps.Projection
|
||||
import junit.framework.Assert.assertEquals
|
||||
import junit.framework.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.mock
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@@ -22,7 +24,7 @@ class LocationAnimatorCoordinatorTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
locationAnimatorCoordinator = LocationAnimatorCoordinator()
|
||||
locationAnimatorCoordinator = LocationAnimatorCoordinator(mock(Projection::class.java))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -454,6 +454,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_trackingChanged() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -478,6 +481,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_trackingNotChanged() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -504,6 +510,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_canceled() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -528,6 +537,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_mapboxCallbackFinished() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -557,10 +569,48 @@ public class LocationCameraControllerTest {
|
||||
verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transition_mapboxCallbackFinishedImmediately() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
Location location = mock(Location.class);
|
||||
when(location.getLatitude()).thenReturn(1.0);
|
||||
when(location.getLongitude()).thenReturn(1.0);
|
||||
when(location.getBearing()).thenReturn(30f);
|
||||
when(location.getAltitude()).thenReturn(0.0);
|
||||
|
||||
ArgumentCaptor<MapboxMap.CancelableCallback> callbackCaptor
|
||||
= ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class);
|
||||
|
||||
camera.setCameraMode(CameraMode.TRACKING, location, listener);
|
||||
|
||||
CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location));
|
||||
verify(mapboxMap).moveCamera(
|
||||
eq(CameraUpdateFactory.newCameraPosition(builder.build())),
|
||||
callbackCaptor.capture());
|
||||
|
||||
Assert.assertTrue(camera.isTransitioning());
|
||||
|
||||
callbackCaptor.getValue().onFinish();
|
||||
|
||||
Assert.assertFalse(camera.isTransitioning());
|
||||
|
||||
verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void transition_mapboxCallbackCanceled() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -594,6 +644,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_mapboxAnimateBearing() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -616,6 +669,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_mapboxAnimateNorth() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -638,6 +694,9 @@ public class LocationCameraControllerTest {
|
||||
public void transition_animatorValuesDuringTransition() {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT);
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
LocationCameraController camera = buildCamera(mapboxMap);
|
||||
camera.initializeOptions(mock(LocationComponentOptions.class));
|
||||
final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class);
|
||||
@@ -674,6 +733,9 @@ public class LocationCameraControllerTest {
|
||||
private LocationCameraController buildCamera(OnCameraTrackingChangedListener onCameraTrackingChangedListener) {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class);
|
||||
OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
|
||||
AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class);
|
||||
@@ -685,6 +747,9 @@ public class LocationCameraControllerTest {
|
||||
private LocationCameraController buildCamera(MoveGestureDetector moveGestureDetector) {
|
||||
MapboxMap mapboxMap = mock(MapboxMap.class);
|
||||
when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class));
|
||||
Projection projection = mock(Projection.class);
|
||||
when(mapboxMap.getProjection()).thenReturn(projection);
|
||||
when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000));
|
||||
OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class);
|
||||
OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class);
|
||||
AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class);
|
||||
|
||||
@@ -1165,12 +1165,15 @@ class LocationComponentTest : BaseActivityTest() {
|
||||
uiController: UiController, context: Context) {
|
||||
component.activateLocationComponent(context, mapboxMap.style!!, false)
|
||||
component.isLocationComponentEnabled = true
|
||||
val target = LatLng(51.0, 17.0)
|
||||
assertTrue(target.distanceTo(LatLng(location)) > LocationComponentConstants.INSTANT_LOCATION_TRANSITION_THRESHOLD)
|
||||
component.cameraMode = CameraMode.NONE
|
||||
component.forceLocationUpdate(location)
|
||||
|
||||
val target = LatLng(51.0, 17.0)
|
||||
mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(target))
|
||||
mapboxMap.moveCamera(CameraUpdateFactory.zoomTo(15.0))
|
||||
mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(90.0))
|
||||
assertTrue(Utils.immediateAnimation(mapboxMap.projection, mapboxMap.cameraPosition.target, LatLng(location)))
|
||||
|
||||
component.cameraMode = CameraMode.TRACKING_GPS
|
||||
assertEquals(location.bearing.toDouble(), mapboxMap.cameraPosition.bearing, 0.1)
|
||||
assertEquals(location.latitude, mapboxMap.cameraPosition.target.latitude, 0.1)
|
||||
|
||||
Reference in New Issue
Block a user