[android] Cherry picking 9133 (#9145)
* [android] Fix tracking mode + camera race condition (#9133) * [android] fix tracking mode + camera race condition * fix resetTrackingModesIfRequired bug (comparing current camera position with target camera position * cherry pick #9133 and update CHANGELOG * add null check to prevent null pointer exception
This commit is contained in:
@@ -113,7 +113,6 @@ public class MapboxConstants {
|
||||
public static final String STATE_MY_BEARING_TRACKING_MODE = "mapbox_myBearingTracking";
|
||||
public static final String STATE_MY_LOCATION_TRACKING_DISMISS = "mapbox_myLocationTrackingDismiss";
|
||||
public static final String STATE_MY_BEARING_TRACKING_DISMISS = "mapbox_myBearingTrackingDismiss";
|
||||
public static final String STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA = "mapbox_myBearingTrackingDismiss";
|
||||
public static final String STATE_COMPASS_ENABLED = "mapbox_compassEnabled";
|
||||
public static final String STATE_COMPASS_GRAVITY = "mapbox_compassGravity";
|
||||
public static final String STATE_COMPASS_MARGIN_LEFT = "mapbox_compassMarginLeft";
|
||||
|
||||
@@ -357,7 +357,7 @@ final class MapGestureDetector {
|
||||
return false;
|
||||
}
|
||||
|
||||
trackingSettings.resetTrackingModesIfRequired(true, false);
|
||||
trackingSettings.resetTrackingModesIfRequired(true, false, false);
|
||||
|
||||
// cancel any animation
|
||||
transform.cancelTransitions();
|
||||
@@ -402,7 +402,7 @@ final class MapGestureDetector {
|
||||
}
|
||||
|
||||
// reset tracking if needed
|
||||
trackingSettings.resetTrackingModesIfRequired(true, false);
|
||||
trackingSettings.resetTrackingModesIfRequired(true, false, false);
|
||||
|
||||
// Scroll the map
|
||||
transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/);
|
||||
@@ -484,7 +484,7 @@ final class MapGestureDetector {
|
||||
// to be in the center of the map. Therefore the zoom will translate the map center, so tracking
|
||||
// should be disabled.
|
||||
|
||||
trackingSettings.resetTrackingModesIfRequired(!quickZoom, false);
|
||||
trackingSettings.resetTrackingModesIfRequired(!quickZoom, false, false);
|
||||
// Scale the map
|
||||
if (focalPoint != null) {
|
||||
// arround user provided focal point
|
||||
@@ -568,7 +568,7 @@ final class MapGestureDetector {
|
||||
}
|
||||
// rotation constitutes translation of anything except the center of
|
||||
// rotation, so cancel both location and bearing tracking if required
|
||||
trackingSettings.resetTrackingModesIfRequired(true, true);
|
||||
trackingSettings.resetTrackingModesIfRequired(true, true, false);
|
||||
|
||||
// Get rotate value
|
||||
double bearing = transform.getRawBearing();
|
||||
|
||||
@@ -746,10 +746,38 @@ public final class MapboxMap {
|
||||
@UiThread
|
||||
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
|
||||
final MapboxMap.CancelableCallback callback) {
|
||||
easeCamera(update, durationMs, easingInterpolator, callback, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gradually move the camera by a specified duration in milliseconds, zoom will not be affected
|
||||
* unless specified within {@link CameraUpdate}. A callback can be used to be notified when
|
||||
* easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it
|
||||
* will return the current location of the camera in flight.
|
||||
* <p>
|
||||
* Note that this will cancel location tracking mode if enabled. You can change this behaviour by calling
|
||||
* {@link TrackingSettings#setDismissTrackingModeForCameraPositionChange(boolean)} with false before invoking this
|
||||
* method and calling it with true in the {@link CancelableCallback#onFinish()}.
|
||||
* </p>
|
||||
*
|
||||
* @param update The change that should be applied to the camera.
|
||||
* @param durationMs The duration of the animation in milliseconds. This must be strictly
|
||||
* positive, otherwise an IllegalArgumentException will be thrown.
|
||||
* @param easingInterpolator True for easing interpolator, false for linear.
|
||||
* @param callback An optional callback to be notified from the main thread when the animation
|
||||
* stops. If the animation stops due to its natural completion, the callback
|
||||
* will be notified with onFinish(). If the animation stops due to interruption
|
||||
* by a later camera movement or a user gesture, onCancel() will be called.
|
||||
* Do not update or ease the camera from within onCancel().
|
||||
* @param isDismissable true will allow animated camera changes dismiss a tracking mode.
|
||||
*/
|
||||
@UiThread
|
||||
public final void easeCamera(final CameraUpdate update, final int durationMs, final boolean easingInterpolator,
|
||||
final MapboxMap.CancelableCallback callback, final boolean isDismissable) {
|
||||
new Handler().post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback);
|
||||
transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback, isDismissable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ public final class TrackingSettings {
|
||||
private boolean myLocationEnabled;
|
||||
private boolean dismissLocationTrackingOnGesture = true;
|
||||
private boolean dismissBearingTrackingOnGesture = true;
|
||||
private boolean isResetTrackingWithCameraPositionChange = true;
|
||||
|
||||
private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener;
|
||||
private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener;
|
||||
@@ -57,8 +56,6 @@ public final class TrackingSettings {
|
||||
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, isDismissLocationTrackingOnGesture());
|
||||
outState.putBoolean(MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, isDismissBearingTrackingOnGesture());
|
||||
outState.putBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED, isMyLocationEnabled());
|
||||
outState.putBoolean(MapboxConstants.STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA,
|
||||
isDismissTrackingModesForCameraPositionChange());
|
||||
}
|
||||
|
||||
void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
@@ -77,8 +74,6 @@ public final class TrackingSettings {
|
||||
MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, true));
|
||||
setDismissBearingTrackingOnGesture(savedInstanceState.getBoolean(
|
||||
MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, true));
|
||||
setDismissTrackingModeForCameraPositionChange(savedInstanceState.getBoolean(
|
||||
MapboxConstants.STATE_MY_TRACKING_MODE_DISMISS_FOR_CAMERA, true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,15 +254,16 @@ public final class TrackingSettings {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the tracking modes as necessary. Location tracking is reset if the map center is changed,
|
||||
* bearing tracking if there is a rotation.
|
||||
* Reset the tracking modes as necessary. Location tracking is reset if the map center is changed and not from
|
||||
* location, bearing tracking if there is a rotation.
|
||||
*
|
||||
* @param translate true if translation
|
||||
* @param rotate true if rotation
|
||||
* @param translate true if translation
|
||||
* @param rotate true if rotation
|
||||
* @param isFromLocation true if from location
|
||||
*/
|
||||
void resetTrackingModesIfRequired(boolean translate, boolean rotate) {
|
||||
void resetTrackingModesIfRequired(boolean translate, boolean rotate, boolean isFromLocation) {
|
||||
// if tracking is on, and we should dismiss tracking with gestures, and this is a scroll action, turn tracking off
|
||||
if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture()) {
|
||||
if (translate && !isLocationTrackingDisabled() && isDismissLocationTrackingOnGesture() && !isFromLocation) {
|
||||
setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE);
|
||||
}
|
||||
|
||||
@@ -280,38 +276,18 @@ public final class TrackingSettings {
|
||||
/**
|
||||
* Reset the tracking modes as necessary. Animated camera position changes can reset the underlying tracking modes.
|
||||
*
|
||||
* @param cameraPosition the changed camera position
|
||||
* @param currentCameraPosition the current camera position
|
||||
* @param targetCameraPosition the changed camera position
|
||||
* @param isFromLocation true if from location
|
||||
*/
|
||||
void resetTrackingModesIfRequired(CameraPosition cameraPosition) {
|
||||
if (isDismissTrackingModesForCameraPositionChange()) {
|
||||
resetTrackingModesIfRequired(cameraPosition.target != null, false);
|
||||
void resetTrackingModesIfRequired(CameraPosition currentCameraPosition, CameraPosition targetCameraPosition,
|
||||
boolean isFromLocation) {
|
||||
if (currentCameraPosition.target != null) {
|
||||
resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false,
|
||||
isFromLocation);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if a animation allows to dismiss a tracking mode.
|
||||
* <p>
|
||||
* By default this is set to true.
|
||||
* </p>
|
||||
*
|
||||
* @return True if camera animations will allow to dismiss a tracking mode
|
||||
*/
|
||||
public boolean isDismissTrackingModesForCameraPositionChange() {
|
||||
return isResetTrackingWithCameraPositionChange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a flag to allow animated camera position changes to dismiss a tracking mode.
|
||||
* <p>
|
||||
* <p>
|
||||
* </p>
|
||||
*
|
||||
* @param willAllowToDismiss True will allow animated camera changes dismiss a trackig mode
|
||||
*/
|
||||
public void setDismissTrackingModeForCameraPositionChange(boolean willAllowToDismiss) {
|
||||
isResetTrackingWithCameraPositionChange = willAllowToDismiss;
|
||||
}
|
||||
|
||||
Location getMyLocation() {
|
||||
return myLocationView.getLocation();
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, MapboxMap.CancelableCallback callback) {
|
||||
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
|
||||
if (!cameraPosition.equals(this.cameraPosition)) {
|
||||
trackingSettings.resetTrackingModesIfRequired(cameraPosition);
|
||||
trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false);
|
||||
cancelTransitions();
|
||||
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
|
||||
mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom);
|
||||
@@ -107,10 +107,10 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
|
||||
@UiThread
|
||||
final void easeCamera(MapboxMap mapboxMap, CameraUpdate update, int durationMs, boolean easingInterpolator,
|
||||
final MapboxMap.CancelableCallback callback) {
|
||||
final MapboxMap.CancelableCallback callback, boolean isDismissable) {
|
||||
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
|
||||
if (!cameraPosition.equals(this.cameraPosition)) {
|
||||
trackingSettings.resetTrackingModesIfRequired(cameraPosition);
|
||||
trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable);
|
||||
cancelTransitions();
|
||||
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
|
||||
|
||||
@@ -118,7 +118,6 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
cameraCancelableCallback = callback;
|
||||
mapView.addOnMapChangedListener(this);
|
||||
}
|
||||
|
||||
mapView.easeTo(cameraPosition.bearing, cameraPosition.target, durationMs, cameraPosition.tilt,
|
||||
cameraPosition.zoom, easingInterpolator);
|
||||
}
|
||||
@@ -129,7 +128,7 @@ final class Transform implements MapView.OnMapChangedListener {
|
||||
final MapboxMap.CancelableCallback callback) {
|
||||
CameraPosition cameraPosition = update.getCameraPosition(mapboxMap);
|
||||
if (!cameraPosition.equals(this.cameraPosition)) {
|
||||
trackingSettings.resetTrackingModesIfRequired(cameraPosition);
|
||||
trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false);
|
||||
cancelTransitions();
|
||||
cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION);
|
||||
|
||||
|
||||
@@ -485,19 +485,8 @@ public class MyLocationView extends View {
|
||||
if (location != null) {
|
||||
if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) {
|
||||
// center map directly
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false);
|
||||
mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(new LatLng(location)), 0, false /*linear interpolator*/,
|
||||
new MapboxMap.CancelableCallback() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true);
|
||||
}
|
||||
});
|
||||
null, true);
|
||||
} else {
|
||||
// do not use interpolated location from tracking mode
|
||||
latLng = null;
|
||||
@@ -663,19 +652,8 @@ public class MyLocationView extends View {
|
||||
private void rotateCamera(float rotation) {
|
||||
CameraPosition.Builder builder = new CameraPosition.Builder();
|
||||
builder.bearing(rotation);
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false);
|
||||
mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS,
|
||||
false /*linear interpolator*/, new MapboxMap.CancelableCallback() {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true);
|
||||
}
|
||||
});
|
||||
false /*linear interpolator*/, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -750,7 +728,7 @@ public class MyLocationView extends View {
|
||||
abstract void invalidate();
|
||||
}
|
||||
|
||||
private class MyLocationTrackingBehavior extends MyLocationBehavior implements MapboxMap.CancelableCallback {
|
||||
private class MyLocationTrackingBehavior extends MyLocationBehavior {
|
||||
|
||||
@Override
|
||||
void updateLatLng(@NonNull Location location) {
|
||||
@@ -789,10 +767,9 @@ public class MyLocationView extends View {
|
||||
// accuracy
|
||||
updateAccuracy(location);
|
||||
|
||||
// disable dismiss of tracking settings, enabled in #onFinish
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(false);
|
||||
// ease to new camera position with a linear interpolator
|
||||
mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, this);
|
||||
mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null,
|
||||
true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -803,22 +780,6 @@ public class MyLocationView extends View {
|
||||
screenLocation = new PointF(x, y);
|
||||
MyLocationView.this.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
//no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
// Posting to end message queue to avoid race condition #8560
|
||||
post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mapboxMap.getTrackingSettings().setDismissTrackingModeForCameraPositionChange(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class MyLocationShowBehavior extends MyLocationBehavior {
|
||||
|
||||
Reference in New Issue
Block a user