diff options
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps')
23 files changed, 1410 insertions, 3464 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java index 48de768e64..39cd25631e 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java @@ -11,12 +11,13 @@ import android.support.annotation.NonNull; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Toast; + +import com.mapbox.android.telemetry.TelemetryEnabler; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.attribution.Attribution; import com.mapbox.mapboxsdk.attribution.AttributionParser; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.util.ArrayList; import java.util.List; @@ -31,17 +32,16 @@ import java.util.Set; * Additionally an telemetry option item is shown to configure telemetry settings. * </p> */ -class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener { +public class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener { private static final String MAP_FEEDBACK_URL = "https://www.mapbox.com/map-feedback"; private static final String MAP_FEEDBACK_LOCATION_FORMAT = MAP_FEEDBACK_URL + "/#/%f/%f/%d"; private final Context context; private final MapboxMap mapboxMap; - private String[] attributionTitles; private Set<Attribution> attributionSet; - AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) { + public AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) { this.context = context; this.mapboxMap = mapboxMap; } @@ -59,12 +59,11 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface. // check is hosting activity isn't finishing // https://github.com/mapbox/mapbox-gl-native/issues/11238 if (!isActivityFinishing) { - showAttributionDialog(); + showAttributionDialog(getAttributionTitles()); } } - private void showAttributionDialog() { - attributionTitles = getAttributionTitles(); + protected void showAttributionDialog(String[] attributionTitles) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.mapbox_attributionsDialogTitle); builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionTitles), this); @@ -90,7 +89,7 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface. } private boolean isLatestEntry(int attributionKeyIndex) { - return attributionKeyIndex == attributionTitles.length - 1; + return attributionKeyIndex == getAttributionTitles().length - 1; } private void showTelemetryDialog() { @@ -100,7 +99,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface. builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(true); + TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.ENABLED); + Telemetry.obtainTelemetry().enable(); dialog.cancel(); } }); @@ -114,7 +114,8 @@ class AttributionDialogManager implements View.OnClickListener, DialogInterface. builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - MapboxTelemetry.getInstance().setTelemetryEnabled(false); + Telemetry.obtainTelemetry().disable(); + TelemetryEnabler.updateTelemetryState(TelemetryEnabler.State.DISABLED); dialog.cancel(); } }); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java index 07106689be..de9b4fdbc2 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java @@ -5,27 +5,32 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.PointF; -import android.location.Location; +import android.os.Handler; import android.support.annotation.Nullable; -import android.support.v4.view.GestureDetectorCompat; -import android.support.v4.view.ScaleGestureDetectorCompat; -import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.InputDevice; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; - -import com.almeros.android.multitouch.gesturedetectors.RotateGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.ShoveGestureDetector; -import com.almeros.android.multitouch.gesturedetectors.TwoFingerGestureDetector; +import android.view.animation.DecelerateInterpolator; + +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.gestures.Constants; +import com.mapbox.android.gestures.MoveGestureDetector; +import com.mapbox.android.gestures.MultiFingerTapGestureDetector; +import com.mapbox.android.gestures.RotateGestureDetector; +import com.mapbox.android.gestures.ShoveGestureDetector; +import com.mapbox.android.gestures.StandardGestureDetector; +import com.mapbox.android.gestures.StandardScaleGestureDetector; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapState; +import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.services.android.telemetry.MapboxEvent; -import com.mapbox.services.android.telemetry.MapboxTelemetry; -import com.mapbox.services.android.telemetry.utils.MathUtils; -import com.mapbox.services.android.telemetry.utils.TelemetryUtils; +import com.mapbox.mapboxsdk.utils.MathUtils; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION; @@ -33,24 +38,15 @@ import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.RE /** * Manages gestures events on a MapView. - * <p> - * Relies on gesture detection code in almeros.android.multitouch.gesturedetectors. - * </p> */ final class MapGestureDetector { private final Transform transform; private final Projection projection; private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; private final AnnotationManager annotationManager; private final CameraChangeDispatcher cameraChangeDispatcher; - private GestureDetectorCompat gestureDetector; - private ScaleGestureDetector scaleGestureDetector; - private RotateGestureDetector rotateGestureDetector; - private ShoveGestureDetector shoveGestureDetector; - // deprecated map touch API private MapboxMap.OnMapClickListener onMapClickListener; private MapboxMap.OnMapLongClickListener onMapLongClickListener; @@ -70,44 +66,96 @@ final class MapGestureDetector { private final CopyOnWriteArrayList<MapboxMap.OnScrollListener> onScrollListenerList = new CopyOnWriteArrayList<>(); - private PointF focalPoint; + private final CopyOnWriteArrayList<MapboxMap.OnMoveListener> onMoveListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList<MapboxMap.OnRotateListener> onRotateListenerList + = new CopyOnWriteArrayList<>(); + + private final CopyOnWriteArrayList<MapboxMap.OnScaleListener> onScaleListenerList + = new CopyOnWriteArrayList<>(); - private boolean twoTap; - private boolean quickZoom; - private boolean tiltGestureOccurred; - private boolean scrollGestureOccurred; + private final CopyOnWriteArrayList<MapboxMap.OnShoveListener> onShoveListenerList + = new CopyOnWriteArrayList<>(); - private boolean scaleGestureOccurred; - private boolean recentScaleGestureOccurred; - private long scaleBeginTime; + /** + * User-set focal point. + */ + private PointF focalPoint; + + private AndroidGesturesManager gesturesManager; + private boolean executeDoubleTap; - private boolean scaleAnimating; - private boolean rotateAnimating; + private Animator scaleAnimator; + private Animator rotateAnimator; + private final List<Animator> scheduledAnimators = new ArrayList<>(); - private VelocityTracker velocityTracker; - private boolean wasZoomingIn; - private boolean wasClockwiseRotating; - private boolean rotateGestureOccurred; + /** + * Cancels scheduled velocity animations if user doesn't lift fingers within + * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT} + */ + private Handler animationsTimeoutHandler = new Handler(); MapGestureDetector(Context context, Transform transform, Projection projection, UiSettings uiSettings, - TrackingSettings trackingSettings, AnnotationManager annotationManager, - CameraChangeDispatcher cameraChangeDispatcher) { + AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) { this.annotationManager = annotationManager; this.transform = transform; this.projection = projection; this.uiSettings = uiSettings; - this.trackingSettings = trackingSettings; this.cameraChangeDispatcher = cameraChangeDispatcher; - // Touch gesture detectors + // Checking for context != null for testing purposes if (context != null) { - gestureDetector = new GestureDetectorCompat(context, new GestureListener()); - gestureDetector.setIsLongpressEnabled(true); - scaleGestureDetector = new ScaleGestureDetector(context, new ScaleGestureListener()); - ScaleGestureDetectorCompat.setQuickScaleEnabled(scaleGestureDetector, true); - rotateGestureDetector = new RotateGestureDetector(context, new RotateGestureListener()); - shoveGestureDetector = new ShoveGestureDetector(context, new ShoveGestureListener()); + // Initialize gestures manager + AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context); + initializeGesturesManager(androidGesturesManager, true); + + // Initialize gesture listeners + initializeGestureListeners(context, true); + } + } + + private void initializeGestureListeners(Context context, boolean attachDefaultListeners) { + if (attachDefaultListeners) { + StandardGestureListener standardGestureListener = new StandardGestureListener(); + MoveGestureListener moveGestureListener = new MoveGestureListener(); + ScaleGestureListener scaleGestureListener = new ScaleGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity)); + RotateGestureListener rotateGestureListener = new RotateGestureListener( + context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating), + context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity), + context.getResources().getDimension(R.dimen.mapbox_defaultScaleSpanSinceStartThreshold)); + ShoveGestureListener shoveGestureListener = new ShoveGestureListener(); + TapGestureListener tapGestureListener = new TapGestureListener(); + + gesturesManager.setStandardGestureListener(standardGestureListener); + gesturesManager.setMoveGestureListener(moveGestureListener); + gesturesManager.setStandardScaleGestureListener(scaleGestureListener); + gesturesManager.setRotateGestureListener(rotateGestureListener); + gesturesManager.setShoveGestureListener(shoveGestureListener); + gesturesManager.setMultiFingerTapGestureListener(tapGestureListener); + } + } + + private void initializeGesturesManager(AndroidGesturesManager androidGesturesManager, + boolean setDefaultMutuallyExclusives) { + if (setDefaultMutuallyExclusives) { + Set<Integer> shoveScaleSet = new HashSet<>(); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + + Set<Integer> shoveRotateSet = new HashSet<>(); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); + shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE); + + Set<Integer> ScaleLongPressSet = new HashSet<>(); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); + ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS); + + androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet); } + + gesturesManager = androidGesturesManager; } /** @@ -133,8 +181,9 @@ final class MapGestureDetector { /** * Get the current active gesture focal point. * <p> - * This could be either the user provided focal point in {@link UiSettings#setFocalPoint(PointF)} or the focal point - * defined as a result of {@link TrackingSettings#setMyLocationEnabled(boolean)}. + * This could be either the user provided focal point in + * {@link UiSettings#setFocalPoint(PointF)}or <code>null</code>. + * If it's <code>null</code>, gestures will use focal pointed returned by the detector. * </p> * * @return the current active gesture focal point. @@ -145,128 +194,86 @@ final class MapGestureDetector { } /** - * Given coordinates from a gesture, use the current projection to translate it into - * a Location object. - * - * @param x coordinate - * @param y coordinate - * @return location - */ - private Location getLocationFromGesture(float x, float y) { - LatLng latLng = projection.fromScreenLocation(new PointF(x, y)); - return TelemetryUtils.buildLocation(latLng.getLongitude(), latLng.getLatitude()); - } - - /** * Called when user touches the screen, all positions are absolute. * <p> * Forwards event to the related gesture detectors. * </p> * - * @param event the MotionEvent + * @param motionEvent the MotionEvent * @return True if touch event is handled */ - boolean onTouchEvent(MotionEvent event) { - // framework can return null motion events in edge cases #9432 - if (event == null) { + boolean onTouchEvent(MotionEvent motionEvent) { + // Framework can return null motion events in edge cases #9432 + if (motionEvent == null) { return false; } // Check and ignore non touch or left clicks - if ((event.getButtonState() != 0) && (event.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { + if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { return false; } - // Check two finger gestures first - scaleGestureDetector.onTouchEvent(event); - rotateGestureDetector.onTouchEvent(event); - shoveGestureDetector.onTouchEvent(event); + boolean result = gesturesManager.onTouchEvent(motionEvent); - // Handle two finger tap - switch (event.getActionMasked()) { + switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: - if (velocityTracker == null) { - velocityTracker = VelocityTracker.obtain(); - } else { - velocityTracker.clear(); - } - velocityTracker.addMovement(event); - // First pointer down, reset scaleGestureOccurred, used to avoid triggering a fling after a scale gesture #7666 - recentScaleGestureOccurred = false; + cancelAnimators(); transform.setGestureInProgress(true); break; + case MotionEvent.ACTION_UP: + transform.setGestureInProgress(false); - case MotionEvent.ACTION_POINTER_DOWN: - // Second pointer down - twoTap = event.getPointerCount() == 2 - && uiSettings.isZoomGesturesEnabled(); - if (twoTap) { - // Confirmed 2nd Finger Down - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(event.getX(), event.getY()), - MapboxEvent.GESTURE_TWO_FINGER_SINGLETAP, transform)); + // Start all awaiting velocity animations + animationsTimeoutHandler.removeCallbacksAndMessages(null); + for (Animator animator : scheduledAnimators) { + animator.start(); } + scheduledAnimators.clear(); break; - case MotionEvent.ACTION_POINTER_UP: - // Second pointer up + case MotionEvent.ACTION_CANCEL: + scheduledAnimators.clear(); + transform.setGestureInProgress(false); break; + } - case MotionEvent.ACTION_UP: - // First pointer up - long tapInterval = event.getEventTime() - event.getDownTime(); - boolean isTap = tapInterval <= ViewConfiguration.getTapTimeout(); - boolean inProgress = rotateGestureDetector.isInProgress() - || scaleGestureDetector.isInProgress() - || shoveGestureDetector.isInProgress(); - - if (twoTap && isTap && !inProgress) { - if (focalPoint != null) { - transform.zoom(false, focalPoint); - } else { - PointF focalPoint = TwoFingerGestureDetector.determineFocalPoint(event); - transform.zoom(false, focalPoint); - } - twoTap = false; - return true; - } + return result; + } - // Scroll / Pan Has Stopped - if (scrollGestureOccurred) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapDragEndEvent( - getLocationFromGesture(event.getX(), event.getY()), transform)); - scrollGestureOccurred = false; + void cancelAnimators() { + animationsTimeoutHandler.removeCallbacksAndMessages(null); + scheduledAnimators.clear(); - if (!scaleAnimating && !rotateAnimating) { - cameraChangeDispatcher.onCameraIdle(); - } - } + cancelAnimator(scaleAnimator); + cancelAnimator(rotateAnimator); + } - twoTap = false; - transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); - } - velocityTracker = null; - break; + private void cancelAnimator(Animator animator) { + if (animator != null && animator.isStarted()) { + animator.cancel(); + } + } - case MotionEvent.ACTION_CANCEL: - twoTap = false; - transform.setGestureInProgress(false); - if (velocityTracker != null) { - velocityTracker.recycle(); - } - velocityTracker = null; - break; - case MotionEvent.ACTION_MOVE: - if (velocityTracker != null) { - velocityTracker.addMovement(event); - velocityTracker.computeCurrentVelocity(1000); - } - break; + /** + * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed. + */ + private Runnable cancelAnimatorsRunnable = new Runnable() { + @Override + public void run() { + cancelAnimators(); } + }; - return gestureDetector.onTouchEvent(event); + /** + * Schedules a velocity animator to be executed when user lift fingers, + * unless canceled by the {@link #cancelAnimatorsRunnable}. + * + * @param animator animator ot be scheduled + */ + private void scheduleAnimator(Animator animator) { + scheduledAnimators.add(animator); + animationsTimeoutHandler.removeCallbacksAndMessages(null); + animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT); } /** @@ -275,7 +282,7 @@ final class MapGestureDetector { * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. * </p> * - * @param event The MotionEvent occured + * @param event The MotionEvent occurred * @return True is the event is handled */ boolean onGenericMotionEvent(MotionEvent event) { @@ -297,7 +304,7 @@ final class MapGestureDetector { float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); // Scale the map by the appropriate power of two factor - transform.zoomBy(scrollDist, event.getX(), event.getY()); + transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY())); return true; @@ -311,57 +318,14 @@ final class MapGestureDetector { return false; } - /** - * Responsible for handling one finger gestures. - */ - private class GestureListener extends android.view.GestureDetector.SimpleOnGestureListener { - + private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener { @Override - public boolean onDown(MotionEvent event) { - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent e) { - if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) { - return false; - } - - switch (e.getAction()) { - case MotionEvent.ACTION_DOWN: - break; - case MotionEvent.ACTION_MOVE: - break; - case MotionEvent.ACTION_UP: - if (quickZoom) { - cameraChangeDispatcher.onCameraIdle(); - quickZoom = false; - break; - } - - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - // Single finger double tap - if (focalPoint != null) { - // User provided focal point - transform.zoom(true, focalPoint); - } else { - // Zoom in on gesture - transform.zoom(true, new PointF(e.getX(), e.getY())); - } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e.getX(), e.getY()), - MapboxEvent.GESTURE_DOUBLETAP, transform)); - break; - } - + public boolean onDown(MotionEvent motionEvent) { return true; } @Override public boolean onSingleTapUp(MotionEvent motionEvent) { - // Cancel any animation transform.cancelTransitions(); return true; } @@ -380,27 +344,60 @@ final class MapGestureDetector { notifyOnMapClickListeners(tapPoint); } - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(motionEvent.getX(), motionEvent.getY()), - MapboxEvent.GESTURE_SINGLETAP, transform)); + sendTelemetryEvent(Telemetry.SINGLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); return true; } @Override - public void onLongPress(MotionEvent motionEvent) { - PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + public boolean onDoubleTapEvent(MotionEvent motionEvent) { + int action = motionEvent.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + executeDoubleTap = true; + } + if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { + if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled() || !executeDoubleTap) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + PointF zoomFocalPoint; + // Single finger double tap + if (focalPoint != null) { + // User provided focal point + zoomFocalPoint = focalPoint; + } else { + // Zoom in on gesture + zoomFocalPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + } - if (!quickZoom) { - notifyOnMapLongClickListeners(longClickPoint); + zoomInAnimated(zoomFocalPoint, false); + + sendTelemetryEvent(Telemetry.DOUBLE_TAP, new PointF(motionEvent.getX(), motionEvent.getY())); + + return true; } + return super.onDoubleTapEvent(motionEvent); + } + + @Override + public void onLongPress(MotionEvent motionEvent) { + PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); + notifyOnMapLongClickListeners(longClickPoint); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if ((!trackingSettings.isScrollGestureCurrentlyEnabled()) || recentScaleGestureOccurred) { - // don't allow a fling is scroll is disabled - // and ignore when a scale gesture has occurred + if (!uiSettings.isScrollGesturesEnabled()) { + // don't allow a fling if scroll is disabled + return false; + } + + notifyOnFlingListeners(); + + if (!uiSettings.isFlingVelocityAnimationEnabled()) { return false; } @@ -413,11 +410,7 @@ final class MapGestureDetector { return false; } - trackingSettings.resetTrackingModesIfRequired(true, false, false); - - // cancel any animation transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); // tilt results in a bigger translation, limiting input for #5281 @@ -432,487 +425,600 @@ final class MapGestureDetector { // update transformation transform.moveBy(offsetX, offsetY, animationTime); - notifyOnFlingListeners(); return true; } + } - // Called for drags + private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener { @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + public boolean onMoveBegin(MoveGestureDetector detector) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } - if (tiltGestureOccurred) { - return false; - } + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - if (!scrollGestureOccurred) { - scrollGestureOccurred = true; + sendTelemetryEvent(Telemetry.PAN, detector.getFocalPoint()); - // Cancel any animation - if (!scaleGestureOccurred) { - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } + notifyOnMoveBeginListeners(detector); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(e1.getX(), e1.getY()), - MapboxEvent.GESTURE_PAN_START, transform)); - } + return true; + } - // reset tracking if needed - trackingSettings.resetTrackingModesIfRequired(true, false, false); + @Override + public boolean onMove(MoveGestureDetector detector, float distanceX, float distanceY) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); // Scroll the map transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); notifyOnScrollListeners(); + notifyOnMoveListeners(detector); return true; } - } - - void notifyOnMapClickListeners(PointF tapPoint) { - // deprecated API - if (onMapClickListener != null) { - onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); - } - // new API - for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { - listener.onMapClick(projection.fromScreenLocation(tapPoint)); + @Override + public void onMoveEnd(MoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + notifyOnMoveEndListeners(detector); } } - void notifyOnMapLongClickListeners(PointF longClickPoint) { - // deprecated API - if (onMapLongClickListener != null) { - onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); - } + private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { - // new API - for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { - listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); - } - } + private final float minimumVelocity; - void notifyOnFlingListeners() { - // deprecated API - if (onFlingListener != null) { - onFlingListener.onFling(); - } + private PointF scaleFocalPoint; + private boolean quickZoom; - // new API - for (MapboxMap.OnFlingListener listener : onFlingListenerList) { - listener.onFling(); + ScaleGestureListener(float minimumVelocity) { + this.minimumVelocity = minimumVelocity; } - } - void notifyOnScrollListeners() { - //deprecated API - if (onScrollListener != null) { - onScrollListener.onScroll(); - } + @Override + public boolean onScaleBegin(StandardScaleGestureDetector detector) { + if (!uiSettings.isZoomGesturesEnabled()) { + return false; + } - // new API - for (MapboxMap.OnScrollListener listener : onScrollListenerList) { - listener.onScroll(); - } - } + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - /** - * Responsible for handling two finger gestures and double-tap drag gestures. - */ - private class ScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { + quickZoom = detector.getPointersCount() == 1; + if (quickZoom) { + // when quickzoom, dismiss double tap and disable move gesture + executeDoubleTap = false; + gesturesManager.getMoveGestureDetector().setEnabled(false); + } + + if (uiSettings.isIncreaseRotateThresholdWhenScaling()) { + // increase rotate angle threshold when scale is detected first + gesturesManager.getRotateGestureDetector().setAngleThreshold( + Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD + + MapboxConstants.ROTATION_THRESHOLD_INCREASE_WHEN_SCALING + ); + } + + // setting focalPoint in #onScaleBegin() as well, because #onScale() might not get called before #onScaleEnd() + setScaleFocalPoint(detector); - private static final int ANIMATION_TIME_MULTIPLIER = 77; - private static final double ZOOM_DISTANCE_DIVIDER = 5; + sendTelemetryEvent(Telemetry.PINCH, scaleFocalPoint); - private float scaleFactor = 1.0f; - private PointF scalePointBegin; + notifyOnScaleBeginListeners(detector); + + return true; + } - // Called when two fingers first touch the screen @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } + public boolean onScale(StandardScaleGestureDetector detector) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + setScaleFocalPoint(detector); + + float scaleFactor = detector.getScaleFactor(); + double zoomBy = getNewZoom(scaleFactor, quickZoom); + transform.zoomBy(zoomBy, scaleFocalPoint); + + notifyOnScaleListeners(detector); - recentScaleGestureOccurred = true; - scalePointBegin = new PointF(detector.getFocusX(), detector.getFocusY()); - scaleBeginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PINCH_START, transform)); return true; } - // Called each time a finger moves - // Called for pinch zooms and quickzooms/quickscales @Override - public boolean onScale(ScaleGestureDetector detector) { - if (!uiSettings.isZoomGesturesEnabled()) { - return super.onScale(detector); - } + public void onScaleEnd(StandardScaleGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); - wasZoomingIn = (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) > 0; - if (tiltGestureOccurred) { - return false; + if (quickZoom) { + //if quickzoom, re-enabling move gesture detector + gesturesManager.getMoveGestureDetector().setEnabled(true); } - // Ignore short touches in case it is a tap - // Also ignore small scales - long time = detector.getEventTime(); - long interval = time - scaleBeginTime; - if (!scaleGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; + if (uiSettings.isIncreaseRotateThresholdWhenScaling()) { + // resetting default angle threshold + gesturesManager.getRotateGestureDetector().setAngleThreshold( + Constants.DEFAULT_ROTATE_ANGLE_THRESHOLD + ); } - // If scale is large enough ignore a tap - scaleFactor *= detector.getScaleFactor(); - if ((scaleFactor > 1.1f) || (scaleFactor < 0.9f)) { - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - scaleGestureOccurred = true; - } + notifyOnScaleEndListeners(detector); - if (!scaleGestureOccurred) { - return false; + if (!uiSettings.isScaleVelocityAnimationEnabled()) { + return; } - // Gesture is a quickzoom if there aren't two fingers - if (!quickZoom && !twoTap) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); + if (velocityXY > minimumVelocity) { + double zoomAddition = calculateScale(velocityXY, detector.isScalingOut()); + double currentZoom = transform.getRawZoom(); + long animationTime = (long) (Math.abs(zoomAddition) * 1000 / 4); + scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, scaleFocalPoint, animationTime); + scheduleAnimator(scaleAnimator); } - quickZoom = !twoTap; + } - // make an assumption here; if the zoom center is specified by the gesture, it's NOT going - // 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, false); - // Scale the map + private void setScaleFocalPoint(StandardScaleGestureDetector detector) { if (focalPoint != null) { - // arround user provided focal point - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), focalPoint.x, focalPoint.y); + // around user provided focal point + scaleFocalPoint = focalPoint; } else if (quickZoom) { - cameraChangeDispatcher.onCameraMove(); + // around center + scaleFocalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); + } else { + // around gesture + scaleFocalPoint = detector.getFocalPoint(); + } + } + + private double calculateScale(double velocityXY, boolean isScalingOut) { + double zoomAddition = (float) Math.log(velocityXY / 1000 + 1); + if (isScalingOut) { + zoomAddition = -zoomAddition; + } + return zoomAddition; + } + + private double getNewZoom(float scaleFactor, boolean quickZoom) { + double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); + if (quickZoom) { // clamp scale factors we feed to core #7514 - float scaleFactor = detector.getScaleFactor(); - // around center map - double zoomBy = Math.log(scaleFactor) / Math.log(Math.PI / 2); boolean negative = zoomBy < 0; zoomBy = MathUtils.clamp(Math.abs(zoomBy), MapboxConstants.MINIMUM_SCALE_FACTOR_CLAMP, MapboxConstants.MAXIMUM_SCALE_FACTOR_CLAMP); - transform.zoomBy(negative ? -zoomBy : zoomBy, uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - recentScaleGestureOccurred = true; - } else { - // around gesture - transform.zoomBy(Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2), - scalePointBegin.x, scalePointBegin.y); + return negative ? -zoomBy : zoomBy; } + return zoomBy; + } + } + + private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { + private PointF rotateFocalPoint; + private final float minimumScaleSpanWhenRotating; + private final float minimumAngularVelocity; + private final float defaultSpanSinceStartThreshold; + + public RotateGestureListener(float minimumScaleSpanWhenRotating, float minimumAngularVelocity, + float defaultSpanSinceStartThreshold) { + this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating; + this.minimumAngularVelocity = minimumAngularVelocity; + this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold; + } + + @Override + public boolean onRotateBegin(RotateGestureDetector detector) { + if (!uiSettings.isRotateGesturesEnabled()) { + return false; + } + + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); + + if (uiSettings.isIncreaseScaleThresholdWhenRotating()) { + // when rotation starts, interrupting scale and increasing the threshold + // to make rotation without scaling easier + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating); + gesturesManager.getStandardScaleGestureDetector().interrupt(); + } + + // setting in #onRotateBegin() as well, because #onRotate() might not get called before #onRotateEnd() + setRotateFocalPoint(detector); + + sendTelemetryEvent(Telemetry.ROTATION, rotateFocalPoint); + + notifyOnRotateBeginListeners(detector); + + return true; + } + + @Override + public boolean onRotate(RotateGestureDetector detector, float rotationDegreesSinceLast, + float rotationDegreesSinceFirst) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); + + setRotateFocalPoint(detector); + + // Calculate map bearing value + double bearing = transform.getRawBearing() + rotationDegreesSinceLast; + + // Rotate the map + transform.setBearing(bearing, rotateFocalPoint.x, rotateFocalPoint.y); + + notifyOnRotateListeners(detector); + return true; } - // Called when fingers leave screen @Override - public void onScaleEnd(final ScaleGestureDetector detector) { - if (velocityTracker == null) { + public void onRotateEnd(RotateGestureDetector detector, float velocityX, float velocityY, float angularVelocity) { + cameraChangeDispatcher.onCameraIdle(); + + if (uiSettings.isIncreaseScaleThresholdWhenRotating()) { + // resetting default scale threshold values + gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold); + } + + notifyOnRotateEndListeners(detector); + + if (!uiSettings.isRotateVelocityAnimationEnabled()) { return; } - if (rotateGestureOccurred || quickZoom) { - reset(); + if (Math.abs(angularVelocity) < minimumAngularVelocity) { return; } - double velocityXY = Math.abs(velocityTracker.getYVelocity()) + Math.abs(velocityTracker.getXVelocity()); - if (velocityXY > MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING / 2) { - scaleAnimating = true; - double zoomAddition = calculateScale(velocityXY); - double currentZoom = transform.getRawZoom(); - long animationTime = (long) (Math.log(velocityXY) * ANIMATION_TIME_MULTIPLIER); - createScaleAnimator(currentZoom, zoomAddition, animationTime).start(); - } else if (!scaleAnimating) { - reset(); + boolean negative = angularVelocity < 0; + angularVelocity = (float) Math.pow(angularVelocity, 2); + angularVelocity = MathUtils.clamp( + angularVelocity, MapboxConstants.MINIMUM_ANGULAR_VELOCITY, MapboxConstants.MAXIMUM_ANGULAR_VELOCITY); + + long animationTime = (long) (Math.log(angularVelocity + 1) * 500); + + if (negative) { + angularVelocity = -angularVelocity; } - } - private void reset() { - scaleAnimating = false; - scaleGestureOccurred = false; - scaleBeginTime = 0; - scaleFactor = 1.0f; - cameraChangeDispatcher.onCameraIdle(); + rotateAnimator = createRotateAnimator(angularVelocity, animationTime); + scheduleAnimator(rotateAnimator); } - private double calculateScale(double velocityXY) { - double zoomAddition = (float) (Math.log(velocityXY) / ZOOM_DISTANCE_DIVIDER); - if (!wasZoomingIn) { - zoomAddition = -zoomAddition; + private void setRotateFocalPoint(RotateGestureDetector detector) { + if (focalPoint != null) { + // User provided focal point + rotateFocalPoint = focalPoint; + } else { + // around gesture + rotateFocalPoint = detector.getFocalPoint(); } - return zoomAddition; } - private Animator createScaleAnimator(double currentZoom, double zoomAddition, long animationTime) { - ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); + private Animator createRotateAnimator(float angularVelocity, long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f); animator.setDuration(animationTime); - animator.setInterpolator(new FastOutSlowInInterpolator()); + animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override public void onAnimationUpdate(ValueAnimator animation) { - PointF scalePoint = focalPoint != null ? focalPoint : scalePointBegin; - transform.setZoom((Float) animation.getAnimatedValue(), scalePoint, 0, true); + transform.setBearing( + transform.getRawBearing() + (float) animation.getAnimatedValue(), + rotateFocalPoint.x, rotateFocalPoint.y, + 0L + ); } }); + animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); } @Override public void onAnimationCancel(Animator animation) { - reset(); + cameraChangeDispatcher.onCameraIdle(); } @Override public void onAnimationEnd(Animator animation) { - reset(); + cameraChangeDispatcher.onCameraIdle(); } }); + return animator; } } - /** - * Responsible for handling rotation gestures. - */ - private class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - - private static final float ROTATE_INVOKE_ANGLE = 15.30f; - private static final float ROTATE_LIMITATION_ANGLE = 3.35f; - private static final float ROTATE_LIMITATION_DURATION = ROTATE_LIMITATION_ANGLE * 1.85f; - - private long beginTime = 0; - private boolean started = false; - - // Called when two fingers first touch the screen + private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener { @Override - public boolean onRotateBegin(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled()) { + public boolean onShoveBegin(ShoveGestureDetector detector) { + if (!uiSettings.isTiltGesturesEnabled()) { return false; } - // notify camera change listener + transform.cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - beginTime = detector.getEventTime(); + sendTelemetryEvent(Telemetry.PITCH, detector.getFocalPoint()); + + // disabling move gesture during shove + gesturesManager.getMoveGestureDetector().setEnabled(false); + + notifyOnShoveBeginListeners(detector); + return true; } - // Called each time one of the two fingers moves - // Called for rotation @Override - public boolean onRotate(RotateGestureDetector detector) { - if (!trackingSettings.isRotateGestureCurrentlyEnabled() || tiltGestureOccurred) { - return false; - } + public boolean onShove(ShoveGestureDetector detector, float deltaPixelsSinceLast, float deltaPixelsSinceStart) { + // dispatching start even once more if another detector ended, and this one didn't + cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - // If rotate is large enough ignore a tap - // Also is zoom already started, don't rotate - float angle = detector.getRotationDegreesDelta(); - if (Math.abs(angle) >= ROTATE_INVOKE_ANGLE) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_ROTATION_START, transform)); - started = true; - } + // Get tilt value (scale and clamp) + double pitch = transform.getTilt(); + pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast; + pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT); - if (!started) { - return false; - } + // Tilt the map + transform.setTilt(pitch); + + notifyOnShoveListeners(detector); - wasClockwiseRotating = detector.getRotationDegreesDelta() > 0; - if (scaleBeginTime != 0) { - rotateGestureOccurred = true; + return true; + } + + @Override + public void onShoveEnd(ShoveGestureDetector detector, float velocityX, float velocityY) { + cameraChangeDispatcher.onCameraIdle(); + + // re-enabling move gesture + gesturesManager.getMoveGestureDetector().setEnabled(true); + + notifyOnShoveEndListeners(detector); + } + } + + private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener { + @Override + public boolean onMultiFingerTap(MultiFingerTapGestureDetector detector, int pointersCount) { + if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) { + return false; } - // rotation constitutes translation of anything except the center of - // rotation, so cancel both location and bearing tracking if required - trackingSettings.resetTrackingModesIfRequired(true, true, false); + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - // Calculate map bearing value - double bearing = transform.getRawBearing() + angle; + sendTelemetryEvent(Telemetry.TWO_FINGER_TAP, detector.getFocalPoint()); - // Rotate the map + PointF zoomFocalPoint; + // Single finger double tap if (focalPoint != null) { // User provided focal point - transform.setBearing(bearing, focalPoint.x, focalPoint.y); + zoomFocalPoint = focalPoint; } else { - // around gesture - transform.setBearing(bearing, detector.getFocusX(), detector.getFocusY()); + // Zoom in on gesture + zoomFocalPoint = detector.getFocalPoint(); } + + zoomOutAnimated(zoomFocalPoint, false); + return true; } + } - // Called when the fingers leave the screen - @Override - public void onRotateEnd(RotateGestureDetector detector) { - long interval = detector.getEventTime() - beginTime; - if ((!started && (interval <= ViewConfiguration.getTapTimeout())) || scaleAnimating || interval > 500) { - reset(); - return; + private Animator createScaleAnimator(double currentZoom, double zoomAddition, final PointF animationFocalPoint, + long animationTime) { + ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); + animator.setDuration(animationTime); + animator.setInterpolator(new DecelerateInterpolator()); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + + @Override + public void onAnimationUpdate(ValueAnimator animation) { + transform.setZoom((Float) animation.getAnimatedValue(), animationFocalPoint); } + }); + + animator.addListener(new AnimatorListenerAdapter() { - double angularVelocity = calculateVelocityVector(detector); - if (Math.abs(angularVelocity) > 0.001 && rotateGestureOccurred && !rotateAnimating) { - animateRotateVelocity(); - } else if (!rotateAnimating) { - reset(); + @Override + public void onAnimationStart(Animator animation) { + transform.cancelTransitions(); + cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); } - } - private void reset() { - beginTime = 0; - started = false; - rotateAnimating = false; - rotateGestureOccurred = false; + @Override + public void onAnimationCancel(Animator animation) { + transform.cancelTransitions(); + } - if (!twoTap) { + @Override + public void onAnimationEnd(Animator animation) { cameraChangeDispatcher.onCameraIdle(); } + }); + return animator; + } + + /** + * Zoom in by 1. + * + * @param zoomFocalPoint focal point of zoom animation + * @param runImmediately if true, animation will be started right away, otherwise it will wait until + * {@link MotionEvent#ACTION_UP} is registered. + */ + void zoomInAnimated(PointF zoomFocalPoint, boolean runImmediately) { + zoomAnimated(true, zoomFocalPoint, runImmediately); + } + + /** + * Zoom out by 1. + * + * @param zoomFocalPoint focal point of zoom animation + * @param runImmediately if true, animation will be started right away, otherwise it will wait until + * {@link MotionEvent#ACTION_UP} is registered. + */ + void zoomOutAnimated(PointF zoomFocalPoint, boolean runImmediately) { + zoomAnimated(false, zoomFocalPoint, runImmediately); + } + + private void zoomAnimated(boolean zoomIn, PointF zoomFocalPoint, boolean runImmediately) { + //canceling here as well, because when using a button it will not be canceled automatically by onDown() + cancelAnimator(scaleAnimator); + + double currentZoom = transform.getRawZoom(); + scaleAnimator = createScaleAnimator( + currentZoom, + zoomIn ? 1 : -1, + zoomFocalPoint, + MapboxConstants.ANIMATION_DURATION); + if (runImmediately) { + scaleAnimator.start(); + } else { + scheduleAnimator(scaleAnimator); } + } - private void animateRotateVelocity() { - rotateAnimating = true; - double currentRotation = transform.getRawBearing(); - double rotateAdditionDegrees = calculateVelocityInDegrees(); - createAnimator(currentRotation, rotateAdditionDegrees).start(); + private void sendTelemetryEvent(String eventType, PointF focalPoint) { + if (isZoomValid(transform)) { + MapEventFactory mapEventFactory = new MapEventFactory(); + LatLng latLng = projection.fromScreenLocation(focalPoint); + MapState state = new MapState(latLng.getLatitude(), latLng.getLongitude(), transform.getZoom()); + state.setGesture(eventType); + Telemetry.obtainTelemetry().push(mapEventFactory.createMapGestureEvent(Event.Type.MAP_CLICK, state)); } + } - private double calculateVelocityVector(RotateGestureDetector detector) { - return ((detector.getFocusX() * velocityTracker.getYVelocity()) - + (detector.getFocusY() * velocityTracker.getXVelocity())) - / (Math.pow(detector.getFocusX(), 2) + Math.pow(detector.getFocusY(), 2)); + private boolean isZoomValid(Transform transform) { + if (transform == null) { + return false; } + double mapZoom = transform.getZoom(); + return mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM; + } - private double calculateVelocityInDegrees() { - double angleRadians = Math.atan2(velocityTracker.getXVelocity(), velocityTracker.getYVelocity()); - double angle = angleRadians / (Math.PI / 180); - if (angle <= 0) { - angle += 360; - } + void notifyOnMapClickListeners(PointF tapPoint) { + // deprecated API + if (onMapClickListener != null) { + onMapClickListener.onMapClick(projection.fromScreenLocation(tapPoint)); + } - // limit the angle - angle = angle / ROTATE_LIMITATION_ANGLE; + // new API + for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { + listener.onMapClick(projection.fromScreenLocation(tapPoint)); + } + } - // correct direction - if (!wasClockwiseRotating) { - angle = -angle; - } + void notifyOnMapLongClickListeners(PointF longClickPoint) { + // deprecated API + if (onMapLongClickListener != null) { + onMapLongClickListener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); + } - return angle; + // new API + for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { + listener.onMapLongClick(projection.fromScreenLocation(longClickPoint)); } + } - private Animator createAnimator(double currentRotation, double rotateAdditionDegrees) { - ValueAnimator animator = ValueAnimator.ofFloat( - (float) currentRotation, - (float) (currentRotation + rotateAdditionDegrees) - ); - animator.setDuration((long) (Math.abs(rotateAdditionDegrees) * ROTATE_LIMITATION_DURATION)); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - transform.setBearing((Float) animation.getAnimatedValue()); - } - }); - animator.addListener(new AnimatorListenerAdapter() { + void notifyOnFlingListeners() { + // deprecated API + if (onFlingListener != null) { + onFlingListener.onFling(); + } - @Override - public void onAnimationStart(Animator animation) { - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_ANIMATION); - } + // new API + for (MapboxMap.OnFlingListener listener : onFlingListenerList) { + listener.onFling(); + } + } - @Override - public void onAnimationCancel(Animator animation) { - reset(); - } + void notifyOnScrollListeners() { + //deprecated API + if (onScrollListener != null) { + onScrollListener.onScroll(); + } - @Override - public void onAnimationEnd(Animator animation) { - reset(); - } - }); - return animator; + // new API + for (MapboxMap.OnScrollListener listener : onScrollListenerList) { + listener.onScroll(); } } - /** - * Responsible for handling 2 finger shove gestures. - */ - private class ShoveGestureListener implements ShoveGestureDetector.OnShoveGestureListener { + void notifyOnMoveBeginListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveBegin(detector); + } + } - private long beginTime = 0; - private float totalDelta = 0.0f; + void notifyOnMoveListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMove(detector); + } + } - @Override - public boolean onShoveBegin(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } + void notifyOnMoveEndListeners(MoveGestureDetector detector) { + for (MapboxMap.OnMoveListener listener : onMoveListenerList) { + listener.onMoveEnd(detector); + } + } - // notify camera change listener - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - return true; + void notifyOnRotateBeginListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateBegin(detector); } + } - @Override - public void onShoveEnd(ShoveGestureDetector detector) { - beginTime = 0; - totalDelta = 0.0f; - tiltGestureOccurred = false; + void notifyOnRotateListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotate(detector); } + } - @Override - public boolean onShove(ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } + void notifyOnRotateEndListeners(RotateGestureDetector detector) { + for (MapboxMap.OnRotateListener listener : onRotateListenerList) { + listener.onRotateEnd(detector); + } + } - // Ignore short touches in case it is a tap - // Also ignore small tilt - long time = detector.getEventTime(); - long interval = time - beginTime; - if (!tiltGestureOccurred && (interval <= ViewConfiguration.getTapTimeout())) { - return false; - } + void notifyOnScaleBeginListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleBegin(detector); + } + } - // If tilt is large enough ignore a tap - // Also if zoom already started, don't tilt - totalDelta += detector.getShovePixelsDelta(); - if (!tiltGestureOccurred && ((totalDelta > 10.0f) || (totalDelta < -10.0f))) { - tiltGestureOccurred = true; - beginTime = detector.getEventTime(); - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapClickEvent( - getLocationFromGesture(detector.getFocusX(), detector.getFocusY()), - MapboxEvent.GESTURE_PITCH_START, transform)); - } + void notifyOnScaleListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScale(detector); + } + } - if (!tiltGestureOccurred) { - return false; - } + void notifyOnScaleEndListeners(StandardScaleGestureDetector detector) { + for (MapboxMap.OnScaleListener listener : onScaleListenerList) { + listener.onScaleEnd(detector); + } + } - // Get tilt value (scale and clamp) - double pitch = transform.getTilt(); - pitch -= 0.1 * detector.getShovePixelsDelta(); - pitch = Math.max(MapboxConstants.MINIMUM_TILT, Math.min(MapboxConstants.MAXIMUM_TILT, pitch)); + void notifyOnShoveBeginListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveBegin(detector); + } + } - // Tilt the map - transform.setTilt(pitch); - return true; + void notifyOnShoveListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShove(detector); + } + } + + void notifyOnShoveEndListeners(ShoveGestureDetector detector) { + for (MapboxMap.OnShoveListener listener : onShoveListenerList) { + listener.onShoveEnd(detector); } } @@ -963,4 +1069,46 @@ final class MapGestureDetector { void removeOnScrollListener(MapboxMap.OnScrollListener onScrollListener) { onScrollListenerList.remove(onScrollListener); } -} + + void addOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.add(listener); + } + + void removeOnMoveListener(MapboxMap.OnMoveListener listener) { + onMoveListenerList.remove(listener); + } + + void addOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.add(listener); + } + + void removeOnRotateListener(MapboxMap.OnRotateListener listener) { + onRotateListenerList.remove(listener); + } + + void addOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.add(listener); + } + + void removeOnScaleListener(MapboxMap.OnScaleListener listener) { + onScaleListenerList.remove(listener); + } + + void addShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.add(listener); + } + + void removeShoveListener(MapboxMap.OnShoveListener listener) { + onShoveListenerList.remove(listener); + } + + AndroidGesturesManager getGesturesManager() { + return gesturesManager; + } + + void setGesturesManager(Context context, AndroidGesturesManager gesturesManager, boolean attachDefaultListeners, + boolean setDefaultMutuallyExclusives) { + initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives); + initializeGestureListeners(context, attachDefaultListeners); + } +}
\ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java index d1f01a30f7..08110ff326 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java @@ -13,24 +13,22 @@ import android.view.ViewConfiguration; * <p> * <ul> * <li> Uses {@link Transform} to change the map state</li> - * <li> Uses {@link TrackingSettings} to verify validity of the current tracking mode.</li> * <li> Uses {@link UiSettings} to verify validity of user restricted movement.</li> * </ul> * <p> */ final class MapKeyListener { - private final TrackingSettings trackingSettings; private final Transform transform; private final UiSettings uiSettings; + private final MapGestureDetector mapGestureDetector; private TrackballLongPressTimeOut currentTrackballLongPressTimeOut; - MapKeyListener(@NonNull Transform transform, @NonNull TrackingSettings trackingSettings, - @NonNull UiSettings uiSettings) { + MapKeyListener(Transform transform, UiSettings uiSettings, MapGestureDetector mapGestureDetector) { this.transform = transform; - this.trackingSettings = trackingSettings; this.uiSettings = uiSettings; + this.mapGestureDetector = mapGestureDetector; } /** @@ -55,7 +53,7 @@ final class MapKeyListener { return true; case KeyEvent.KEYCODE_DPAD_LEFT: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } @@ -67,7 +65,7 @@ final class MapKeyListener { return true; case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } @@ -79,7 +77,7 @@ final class MapKeyListener { return true; case KeyEvent.KEYCODE_DPAD_UP: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } @@ -91,7 +89,7 @@ final class MapKeyListener { return true; case KeyEvent.KEYCODE_DPAD_DOWN: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } @@ -128,7 +126,7 @@ final class MapKeyListener { // Zoom out PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, focalPoint); + mapGestureDetector.zoomOutAnimated(focalPoint, true); return true; default: @@ -164,7 +162,7 @@ final class MapKeyListener { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + mapGestureDetector.zoomInAnimated(focalPoint, true); return true; } @@ -183,7 +181,7 @@ final class MapKeyListener { switch (event.getActionMasked()) { // The trackball was rotated case MotionEvent.ACTION_MOVE: - if (!trackingSettings.isScrollGestureCurrentlyEnabled()) { + if (!uiSettings.isScrollGesturesEnabled()) { return false; } @@ -219,7 +217,7 @@ final class MapKeyListener { if (currentTrackballLongPressTimeOut != null) { // Zoom in PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(true, focalPoint); + mapGestureDetector.zoomInAnimated(focalPoint, true); } return true; @@ -261,7 +259,7 @@ final class MapKeyListener { if (!cancelled) { // Zoom out PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - transform.zoom(false, pointF); + mapGestureDetector.zoomOutAnimated(pointF, true); // Ensure the up action is not run currentTrackballLongPressTimeOut = null; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java index 3c5b82c3b0..22d5dd8f19 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java @@ -23,6 +23,12 @@ import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ZoomButtonsController; +import com.mapbox.android.gestures.AndroidGesturesManager; +import com.mapbox.android.telemetry.AppUserTurnstile; +import com.mapbox.android.telemetry.Event; +import com.mapbox.android.telemetry.MapEventFactory; +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.mapboxsdk.BuildConfig; import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.MarkerViewManager; @@ -32,11 +38,8 @@ import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; import com.mapbox.mapboxsdk.maps.widgets.CompassView; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; import com.mapbox.mapboxsdk.net.ConnectivityReceiver; import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.services.android.telemetry.MapboxTelemetry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -78,7 +81,6 @@ public class MapView extends FrameLayout { private boolean destroyed; private boolean hasSurface; - private MyLocationView myLocationView; private CompassView compassView; private PointF focalPoint; private ImageView attrView; @@ -126,7 +128,6 @@ public class MapView extends FrameLayout { // inflate view View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); compassView = (CompassView) view.findViewById(R.id.compassView); - myLocationView = (MyLocationView) view.findViewById(R.id.userLocationView); attrView = (ImageView) view.findViewById(R.id.attributionView); logoView = (ImageView) view.findViewById(R.id.logoView); @@ -146,10 +147,7 @@ public class MapView extends FrameLayout { focalPointInvalidator.addListener(createFocalPointChangeListener()); // callback for registering touch listeners - RegisterTouchListener registerTouchListener = new RegisterTouchListener(); - - // callback for zooming in the camera - CameraZoomInvalidator zoomInvalidator = new CameraZoomInvalidator(); + GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener(); // callback for camera change events final CameraChangeDispatcher cameraChangeDispatcher = new CameraChangeDispatcher(); @@ -157,10 +155,6 @@ public class MapView extends FrameLayout { // setup components for MapboxMap creation Projection proj = new Projection(nativeMapView); UiSettings uiSettings = new UiSettings(proj, focalPointInvalidator, compassView, attrView, logoView); - TrackingSettings trackingSettings = new TrackingSettings(myLocationView, uiSettings, focalPointInvalidator, - zoomInvalidator); - MyLocationViewSettings myLocationViewSettings = new MyLocationViewSettings(myLocationView, proj, - focalPointInvalidator); LongSparseArray<Annotation> annotationsArray = new LongSparseArray<>(); MarkerViewManager markerViewManager = new MarkerViewManager((ViewGroup) findViewById(R.id.markerViewContainer)); IconManager iconManager = new IconManager(nativeMapView); @@ -171,31 +165,29 @@ public class MapView extends FrameLayout { ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray); AnnotationManager annotationManager = new AnnotationManager(nativeMapView, this, annotationsArray, markerViewManager, iconManager, annotations, markers, polygons, polylines, shapeAnnotations); - Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), trackingSettings, + Transform transform = new Transform(nativeMapView, annotationManager.getMarkerViewManager(), cameraChangeDispatcher); - mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, trackingSettings, myLocationViewSettings, proj, - registerTouchListener, annotationManager, cameraChangeDispatcher); - focalPointInvalidator.addListener(mapboxMap.createFocalPointChangeListener()); + mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, proj, registerTouchListener, + annotationManager, cameraChangeDispatcher); mapCallback.attachMapboxMap(mapboxMap); // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, trackingSettings, + mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, annotationManager, cameraChangeDispatcher); - mapKeyListener = new MapKeyListener(transform, trackingSettings, uiSettings); + mapKeyListener = new MapKeyListener(transform, uiSettings, mapGestureDetector); // overlain zoom buttons mapZoomButtonController = new MapZoomButtonController(new ZoomButtonsController(this)); - MapZoomControllerListener zoomListener = new MapZoomControllerListener(mapGestureDetector, uiSettings, transform, - cameraChangeDispatcher, getWidth(), getHeight()); + MapZoomControllerListener zoomListener = new MapZoomControllerListener( + mapGestureDetector, cameraChangeDispatcher, getWidth(), getHeight()); mapZoomButtonController.bind(uiSettings, zoomListener); compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraChangeDispatcher)); compassView.setOnClickListener(createCompassClickListener(cameraChangeDispatcher)); // inject widgets with MapboxMap - myLocationView.setMapboxMap(mapboxMap); - attrView.setOnClickListener(new AttributionDialogManager(context, mapboxMap)); + attrView.setOnClickListener(new AttributionClickListener(context, mapboxMap)); // Ensure this view is interactable setClickable(true); @@ -265,7 +257,7 @@ public class MapView extends FrameLayout { /** * <p> * You must call this method from the parent's Activity#onCreate(Bundle)} or - * Fragment#onCreate(Bundle). + * Fragment#onViewCreated(View, Bundle). * </p> * You must set a valid access token with {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} * before you call this method or an exception will be thrown. @@ -276,7 +268,12 @@ public class MapView extends FrameLayout { @UiThread public void onCreate(@Nullable Bundle savedInstanceState) { if (savedInstanceState == null) { - MapboxTelemetry.getInstance().pushEvent(MapboxEventWrapper.buildMapLoadEvent()); + MapboxTelemetry telemetry = Telemetry.obtainTelemetry(); + AppUserTurnstile turnstileEvent = new AppUserTurnstile(BuildConfig.MAPBOX_SDK_IDENTIFIER, + BuildConfig.MAPBOX_SDK_VERSION); + telemetry.push(turnstileEvent); + MapEventFactory mapEventFactory = new MapEventFactory(); + telemetry.push(mapEventFactory.createMapLoadEvent(Event.Type.MAP_LOAD)); } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { this.savedInstanceState = savedInstanceState; } @@ -285,10 +282,12 @@ public class MapView extends FrameLayout { private void initialiseDrawingSurface(MapboxMapOptions options) { if (options.getTextureMode()) { TextureView textureView = new TextureView(getContext()); - mapRenderer = new TextureViewMapRenderer(getContext(), textureView, options.getLocalIdeographFontFamily()) { + String localFontFamily = options.getLocalIdeographFontFamily(); + boolean translucentSurface = options.getTranslucentTextureSurface(); + mapRenderer = new TextureViewMapRenderer(getContext(), textureView, localFontFamily, translucentSurface) { @Override protected void onSurfaceCreated(GL10 gl, EGLConfig config) { - initRenderSurface(); + MapView.this.onSurfaceCreated(); super.onSurfaceCreated(gl, config); } }; @@ -300,7 +299,7 @@ public class MapView extends FrameLayout { mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, options.getLocalIdeographFontFamily()) { @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { - initRenderSurface(); + MapView.this.onSurfaceCreated(); super.onSurfaceCreated(gl, config); } }; @@ -312,14 +311,14 @@ public class MapView extends FrameLayout { nativeMapView.resizeView(getMeasuredWidth(), getMeasuredHeight()); } - private void initRenderSurface() { + private void onSurfaceCreated() { hasSurface = true; post(new Runnable() { @Override public void run() { // Initialise only when not destroyed and only once if (!destroyed && mapboxMap == null) { - initialiseMap(); + MapView.this.initialiseMap(); mapboxMap.onStart(); } } @@ -383,6 +382,7 @@ public class MapView extends FrameLayout { public void onStop() { if (mapboxMap != null) { // map was destroyed before it was started + mapGestureDetector.cancelAnimators(); mapboxMap.onStop(); } @@ -916,7 +916,7 @@ public class MapView extends FrameLayout { } } - private class RegisterTouchListener implements MapboxMap.OnRegisterTouchListener { + private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener { @Override public void onSetMapClickListener(MapboxMap.OnMapClickListener listener) { @@ -977,22 +977,75 @@ public class MapView extends FrameLayout { public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) { mapGestureDetector.removeOnFlingListener(listener); } + + @Override + public void onAddMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.addOnMoveListener(listener); + } + + @Override + public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) { + mapGestureDetector.removeOnMoveListener(listener); + } + + @Override + public void onAddRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.addOnRotateListener(listener); + } + + @Override + public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) { + mapGestureDetector.removeOnRotateListener(listener); + } + + @Override + public void onAddScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.addOnScaleListener(listener); + } + + @Override + public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) { + mapGestureDetector.removeOnScaleListener(listener); + } + + @Override + public void onAddShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.addShoveListener(listener); + } + + @Override + public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) { + mapGestureDetector.removeShoveListener(listener); + } + + @Override + public AndroidGesturesManager getGesturesManager() { + return mapGestureDetector.getGesturesManager(); + } + + @Override + public void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners, + boolean setDefaultMutuallyExclusives) { + mapGestureDetector.setGesturesManager( + getContext(), gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); + } + + @Override + public void cancelAllVelocityAnimations() { + mapGestureDetector.cancelAnimators(); + } } private static class MapZoomControllerListener implements ZoomButtonsController.OnZoomListener { private final MapGestureDetector mapGestureDetector; - private final UiSettings uiSettings; - private final Transform transform; private final CameraChangeDispatcher cameraChangeDispatcher; private final float mapWidth; private final float mapHeight; - MapZoomControllerListener(MapGestureDetector detector, UiSettings uiSettings, Transform transform, - CameraChangeDispatcher dispatcher, float mapWidth, float mapHeight) { + MapZoomControllerListener(MapGestureDetector detector, CameraChangeDispatcher dispatcher, + float mapWidth, float mapHeight) { this.mapGestureDetector = detector; - this.uiSettings = uiSettings; - this.transform = transform; this.cameraChangeDispatcher = dispatcher; this.mapWidth = mapWidth; this.mapHeight = mapHeight; @@ -1012,32 +1065,13 @@ public class MapView extends FrameLayout { } private void onZoom(boolean zoomIn, @Nullable PointF focalPoint) { - if (focalPoint != null) { - transform.zoom(zoomIn, focalPoint); - } else { - PointF centerPoint = new PointF(mapWidth / 2, mapHeight / 2); - transform.zoom(zoomIn, centerPoint); - } - } - } - - private class CameraZoomInvalidator implements TrackingSettings.CameraZoomInvalidator { - - @Override - public void zoomTo(double zoomLevel) { - Transform transform = mapboxMap.getTransform(); - double currentZoomLevel = transform.getCameraPosition().zoom; - if (currentZoomLevel < zoomLevel) { - setZoom(zoomLevel, mapGestureDetector.getFocalPoint(), transform); + if (focalPoint == null) { + focalPoint = new PointF(mapWidth / 2, mapHeight / 2); } - } - - private void setZoom(double zoomLevel, @Nullable PointF focalPoint, @NonNull Transform transform) { - if (focalPoint != null) { - transform.setZoom(zoomLevel, focalPoint); + if (zoomIn) { + mapGestureDetector.zoomInAnimated(focalPoint, true); } else { - PointF centerPoint = new PointF(getMeasuredWidth() / 2, getMeasuredHeight() / 2); - transform.setZoom(zoomLevel, centerPoint); + mapGestureDetector.zoomOutAnimated(focalPoint, true); } } } @@ -1090,4 +1124,28 @@ public class MapView extends FrameLayout { onMapReadyCallbackList.clear(); } } + + /** + * Click event hook for providing a custom attribution dialog manager. + */ + private static class AttributionClickListener implements OnClickListener { + + private final AttributionDialogManager defaultDialogManager; + private UiSettings uiSettings; + + private AttributionClickListener(Context context, MapboxMap mapboxMap) { + this.defaultDialogManager = new AttributionDialogManager(context, mapboxMap); + this.uiSettings = mapboxMap.getUiSettings(); + } + + @Override + public void onClick(View v) { + AttributionDialogManager customDialogManager = uiSettings.getAttributionDialogManager(); + if (customDialogManager != null) { + uiSettings.getAttributionDialogManager().onClick(v); + } else { + defaultDialogManager.onClick(v); + } + } + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java deleted file mode 100644 index 6730278d79..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxEventWrapper.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.location.Location; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.services.android.telemetry.MapboxEvent; - -import java.util.Hashtable; - -/** - * Wrapper class for MapboxEvent - * <p> - * Provides facility methods to use Transform and handle the case that the zoom, required for a telemetry event, - * isn't available yet. - * </p> - */ -class MapboxEventWrapper { - - @Nullable - static Hashtable<String, Object> buildMapClickEvent( - @NonNull Location location, @NonNull String gestureId, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapClickEvent(location, gestureId, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable<String, Object> buildMapDragEndEvent( - @NonNull Location location, Transform transform) { - try { - double mapZoom = transform.getZoom(); - if (mapZoom >= MapboxConstants.MINIMUM_ZOOM && mapZoom <= MapboxConstants.MAXIMUM_ZOOM) { - // validate zoom #8057 - return MapboxEvent.buildMapDragEndEvent(location, transform.getZoom()); - } - } catch (NullPointerException exception) { - // Map/Transform is not ready yet #8650 - // returning null is valid, event is ignored. - } - return null; - } - - @Nullable - static Hashtable<String, Object> buildMapLoadEvent() { - return MapboxEvent.buildMapLoadEvent(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index a6da4c57fb..cfa7143671 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -4,7 +4,6 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; -import android.location.Location; import android.os.Bundle; import android.support.annotation.FloatRange; import android.support.annotation.IntRange; @@ -16,6 +15,13 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +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.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.annotations.Annotation; import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; import com.mapbox.mapboxsdk.annotations.BaseMarkerViewOptions; @@ -31,19 +37,13 @@ import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.constants.MyBearingTracking; -import com.mapbox.mapboxsdk.constants.MyLocationTracking; import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings; -import com.mapbox.mapboxsdk.style.layers.Filter; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.lang.reflect.ParameterizedType; import java.util.HashMap; @@ -66,37 +66,30 @@ public final class MapboxMap { private final NativeMapView nativeMapView; private final UiSettings uiSettings; - private final TrackingSettings trackingSettings; private final Projection projection; private final Transform transform; private final AnnotationManager annotationManager; - private final MyLocationViewSettings myLocationViewSettings; private final CameraChangeDispatcher cameraChangeDispatcher; - private final OnRegisterTouchListener onRegisterTouchListener; + private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; private MapboxMap.OnFpsChangedListener onFpsChangedListener; - private PointF focalPoint; - MapboxMap(NativeMapView map, Transform transform, UiSettings ui, TrackingSettings tracking, - MyLocationViewSettings myLocationView, Projection projection, OnRegisterTouchListener listener, - AnnotationManager annotations, CameraChangeDispatcher cameraChangeDispatcher) { + MapboxMap(NativeMapView map, Transform transform, UiSettings ui, Projection projection, + OnGesturesManagerInteractionListener listener, AnnotationManager annotations, + CameraChangeDispatcher cameraChangeDispatcher) { this.nativeMapView = map; this.uiSettings = ui; - this.trackingSettings = tracking; this.projection = projection; - this.myLocationViewSettings = myLocationView; this.annotationManager = annotations.bind(this); this.transform = transform; - this.onRegisterTouchListener = listener; + this.onGesturesManagerInteractionListener = listener; this.cameraChangeDispatcher = cameraChangeDispatcher; } void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { transform.initialise(this, options); uiSettings.initialise(context, options); - myLocationViewSettings.initialise(options); - trackingSettings.initialise(options); // Map configuration setDebugActive(options.getDebugActive()); @@ -110,7 +103,6 @@ public final class MapboxMap { */ void onStart() { nativeMapView.update(); - trackingSettings.onStart(); if (TextUtils.isEmpty(nativeMapView.getStyleUrl()) && TextUtils.isEmpty(nativeMapView.getStyleJson())) { // if user hasn't loaded a Style yet nativeMapView.setStyleUrl(Style.MAPBOX_STREETS); @@ -121,7 +113,6 @@ public final class MapboxMap { * Called when the hosting Activity/Fragment onStop() method is called. */ void onStop() { - trackingSettings.onStop(); } /** @@ -133,9 +124,7 @@ public final class MapboxMap { outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, nativeMapView.getDebug()); outState.putString(MapboxConstants.STATE_STYLE_URL, nativeMapView.getStyleUrl()); - trackingSettings.onSaveInstanceState(outState); uiSettings.onSaveInstanceState(outState); - myLocationViewSettings.onSaveInstanceState(outState); } /** @@ -146,9 +135,7 @@ public final class MapboxMap { void onRestoreInstanceState(Bundle savedInstanceState) { final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); - myLocationViewSettings.onRestoreInstanceState(savedInstanceState); uiSettings.onRestoreInstanceState(savedInstanceState); - trackingSettings.onRestoreInstanceState(savedInstanceState); if (cameraPosition != null) { moveCamera(CameraUpdateFactory.newCameraPosition( @@ -188,7 +175,6 @@ public final class MapboxMap { * Called when the region is changing or has changed. */ void onUpdateRegionChange() { - trackingSettings.update(); annotationManager.update(); } @@ -556,38 +542,6 @@ public final class MapboxMap { } // - // TrackingSettings - // - - /** - * Gets the tracking interface settings for the map. - * - * @return the TrackingSettings asssociated with this map - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public TrackingSettings getTrackingSettings() { - return trackingSettings; - } - - // - // MyLocationViewSettings - // - - /** - * Gets the settings of the user location for the map. - * - * @return the MyLocationViewSettings associated with this map - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public MyLocationViewSettings getMyLocationViewSettings() { - return myLocationViewSettings; - } - - // // Projection // @@ -602,7 +556,7 @@ public final class MapboxMap { } // - // + // Light // /** @@ -620,47 +574,6 @@ public final class MapboxMap { // /** - * Moves the center of the screen to a latitude and longitude specified by a LatLng object. This centers the - * camera on the LatLng object. - * - * @param latLng Target location to change to - */ - public void setLatLng(@NonNull LatLng latLng) { - nativeMapView.setLatLng(latLng); - } - - /** - * Moves the camera viewpoint to a particular zoom level. - * - * @param zoom Zoom level to change to - */ - public void setZoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { - if (focalPoint == null) { - focalPoint = new PointF(nativeMapView.getWidth() / 2, nativeMapView.getHeight() / 2); - } - nativeMapView.setZoom(zoom, focalPoint, 0); - } - - /** - * Moves the camera viewpoint angle to a particular angle in degrees. - * - * @param tilt Tilt angle to change to - */ - public void setTilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, to = MapboxConstants.MAXIMUM_TILT) double tilt) { - nativeMapView.setPitch(tilt, 0); - } - - /** - * Moves the camera viewpoint direction to a particular angle in degrees. - * - * @param bearing Direction angle to change to - */ - public void setBearing(@FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, to = MapboxConstants.MAXIMUM_DIRECTION) - double bearing) { - nativeMapView.setBearing(bearing); - } - - /** * Cancels ongoing animations. * <p> * This invokes the {@link CancelableCallback} for ongoing camera updates. @@ -787,11 +700,6 @@ public final class MapboxMap { * 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 com.mapbox.mapboxsdk.maps.TrackingSettings#setDismissLocationTrackingOnGesture(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 @@ -813,11 +721,6 @@ public final class MapboxMap { * 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 com.mapbox.mapboxsdk.maps.TrackingSettings#setDismissLocationTrackingOnGesture(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 @@ -1647,20 +1550,9 @@ public final class MapboxMap { * @param padding the padding to apply to the bounds * @return the camera position that fits the bounds and padding */ - public CameraPosition getCameraForLatLngBounds(@Nullable LatLngBounds latLngBounds, int[] padding) { - // calculate and set additional bounds padding - int[] mapPadding = getPadding(); - for (int i = 0; i < padding.length; i++) { - padding[i] = mapPadding[i] + padding[i]; - } - projection.setContentPadding(padding, myLocationViewSettings.getPadding()); - + public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, int[] padding) { // get padded camera position from LatLngBounds - CameraPosition cameraPosition = nativeMapView.getCameraForLatLngBounds(latLngBounds); - - // reset map padding - setPadding(mapPadding); - return cameraPosition; + return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding); } /** @@ -1672,19 +1564,8 @@ public final class MapboxMap { * @return the camera position that fits the bounds and padding */ public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) { - // calculate and set additional bounds padding - int[] mapPadding = getPadding(); - for (int i = 0; i < padding.length; i++) { - padding[i] = mapPadding[i] + padding[i]; - } - projection.setContentPadding(padding, myLocationViewSettings.getPadding()); - - // get padded camera position from LatLngBounds - CameraPosition cameraPosition = nativeMapView.getCameraForGeometry(geometry, bearing); - - // reset map padding - setPadding(mapPadding); - return cameraPosition; + // get padded camera position from Geometry + return nativeMapView.getCameraForGeometry(geometry, bearing, padding); } // @@ -1693,13 +1574,13 @@ public final class MapboxMap { /** * <p> - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. + * Sets the distance from the edges of the map view's frame to the edges of the map + * view's logical viewport. * </p> * <p> * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the + * properties such as 'centerCoordinate' assume a viewport that matches the map + * view's frame. Otherwise, those properties are inset, excluding part of the * frame from the viewport. For instance, if the only the top edge is inset, the * map center is effectively shifted downward. * </p> @@ -1714,7 +1595,7 @@ public final class MapboxMap { } private void setPadding(int[] padding) { - projection.setContentPadding(padding, myLocationViewSettings.getPadding()); + projection.setContentPadding(padding); uiSettings.invalidate(); } @@ -1759,7 +1640,7 @@ public final class MapboxMap { * * @param listener the listener to notify */ - public void addOnCameraIdleListener(@Nullable OnCameraIdleListener listener) { + public void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { cameraChangeDispatcher.addOnCameraIdleListener(listener); } @@ -1768,7 +1649,7 @@ public final class MapboxMap { * * @param listener the listener to remove */ - public void removeOnCameraIdleListener(@Nullable OnCameraIdleListener listener) { + public void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { cameraChangeDispatcher.removeOnCameraIdleListener(listener); } @@ -1789,7 +1670,7 @@ public final class MapboxMap { * * @param listener the listener to notify */ - public void addOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) { + public void addOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) { cameraChangeDispatcher.addOnCameraMoveCancelListener(listener); } @@ -1798,7 +1679,7 @@ public final class MapboxMap { * * @param listener the listener to remove */ - public void removeOnCameraMoveCancelListener(@Nullable OnCameraMoveCanceledListener listener) { + public void removeOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) { cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener); } @@ -1819,7 +1700,7 @@ public final class MapboxMap { * * @param listener the listener to notify */ - public void addOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) { + public void addOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) { cameraChangeDispatcher.addOnCameraMoveStartedListener(listener); } @@ -1828,7 +1709,7 @@ public final class MapboxMap { * * @param listener the listener to remove */ - public void removeOnCameraMoveStartedListener(@Nullable OnCameraMoveStartedListener listener) { + public void removeOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) { cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener); } @@ -1849,7 +1730,7 @@ public final class MapboxMap { * * @param listener the listener to notify */ - public void addOnCameraMoveListener(@Nullable OnCameraMoveListener listener) { + public void addOnCameraMoveListener(@NonNull OnCameraMoveListener listener) { cameraChangeDispatcher.addOnCameraMoveListener(listener); } @@ -1858,7 +1739,7 @@ public final class MapboxMap { * * @param listener the listener to remove */ - public void removeOnCameraMoveListener(@Nullable OnCameraMoveListener listener) { + public void removeOnCameraMoveListener(@NonNull OnCameraMoveListener listener) { cameraChangeDispatcher.removeOnCameraMoveListener(listener); } @@ -1887,27 +1768,25 @@ public final class MapboxMap { */ @Deprecated public void setOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onSetScrollListener(listener); + onGesturesManagerInteractionListener.onSetScrollListener(listener); } /** * Adds a callback that's invoked when the map is scrolled. * * @param listener The callback that's invoked when the map is scrolled. - * To unset the callback, use null. */ - public void addOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onAddScrollListener(listener); + public void addOnScrollListener(@NonNull OnScrollListener listener) { + onGesturesManagerInteractionListener.onAddScrollListener(listener); } /** * Removes a callback that's invoked when the map is scrolled. * * @param listener The callback that's invoked when the map is scrolled. - * To unset the callback, use null. */ - public void removeOnScrollListener(@Nullable OnScrollListener listener) { - onRegisterTouchListener.onRemoveScrollListener(listener); + public void removeOnScrollListener(@NonNull OnScrollListener listener) { + onGesturesManagerInteractionListener.onRemoveScrollListener(listener); } /** @@ -1919,27 +1798,131 @@ public final class MapboxMap { */ @Deprecated public void setOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onSetFlingListener(listener); + onGesturesManagerInteractionListener.onSetFlingListener(listener); } /** * Adds a callback that's invoked when the map is flinged. * * @param listener The callback that's invoked when the map is flinged. - * To unset the callback, use null. */ - public void addOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onAddFlingListener(listener); + public void addOnFlingListener(@NonNull OnFlingListener listener) { + onGesturesManagerInteractionListener.onAddFlingListener(listener); } /** * Removes a callback that's invoked when the map is flinged. * * @param listener The callback that's invoked when the map is flinged. - * To unset the callback, use null. */ - public void removeOnFlingListener(@Nullable OnFlingListener listener) { - onRegisterTouchListener.onRemoveFlingListener(listener); + public void removeOnFlingListener(@NonNull OnFlingListener listener) { + onGesturesManagerInteractionListener.onRemoveFlingListener(listener); + } + + /** + * Adds a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void addOnMoveListener(@NonNull OnMoveListener listener) { + onGesturesManagerInteractionListener.onAddMoveListener(listener); + } + + /** + * Removes a callback that's invoked when the map is moved. + * + * @param listener The callback that's invoked when the map is moved. + */ + public void removeOnMoveListener(@NonNull OnMoveListener listener) { + onGesturesManagerInteractionListener.onRemoveMoveListener(listener); + } + + /** + * Adds a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void addOnRotateListener(@NonNull OnRotateListener listener) { + onGesturesManagerInteractionListener.onAddRotateListener(listener); + } + + /** + * Removes a callback that's invoked when the map is rotated. + * + * @param listener The callback that's invoked when the map is rotated. + */ + public void removeOnRotateListener(@NonNull OnRotateListener listener) { + onGesturesManagerInteractionListener.onRemoveRotateListener(listener); + } + + /** + * Adds a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void addOnScaleListener(@NonNull OnScaleListener listener) { + onGesturesManagerInteractionListener.onAddScaleListener(listener); + } + + /** + * Removes a callback that's invoked when the map is scaled. + * + * @param listener The callback that's invoked when the map is scaled. + */ + public void removeOnScaleListener(@NonNull OnScaleListener listener) { + onGesturesManagerInteractionListener.onRemoveScaleListener(listener); + } + + /** + * Adds a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void addOnShoveListener(@NonNull OnShoveListener listener) { + onGesturesManagerInteractionListener.onAddShoveListener(listener); + } + + /** + * Remove a callback that's invoked when the map is tilted. + * + * @param listener The callback that's invoked when the map is tilted. + */ + public void removeOnShoveListener(@NonNull OnShoveListener listener) { + onGesturesManagerInteractionListener.onRemoveShoveListener(listener); + } + + /** + * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s + * registered by the {@link MapView}. + * + * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events. + * @param attachDefaultListeners If true, pre-defined listeners will be attach + * to change map based on {@link AndroidGesturesManager} callbacks. + * @param setDefaultMutuallyExclusives If true, pre-defined mutually exclusive gesture sets + * will be added to the passed gestures manager. + * @see <a href="https://github.com/mapbox/mapbox-gestures-android">mapbox-gestures-android library</a> + */ + public void setGesturesManager(AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, + boolean setDefaultMutuallyExclusives) { + onGesturesManagerInteractionListener.setGesturesManager( + androidGesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); + } + + /** + * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s + * registered by the {@link MapView} + * + * @return Current gestures manager. + */ + public AndroidGesturesManager getGesturesManager() { + return onGesturesManagerInteractionListener.getGesturesManager(); + } + + /** + * Interrupts any ongoing gesture velocity animations. + */ + public void cancelAllVelocityAnimations() { + onGesturesManagerInteractionListener.cancelAllVelocityAnimations(); } /** @@ -1951,27 +1934,25 @@ public final class MapboxMap { */ @Deprecated public void setOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onSetMapClickListener(listener); + onGesturesManagerInteractionListener.onSetMapClickListener(listener); } /** * Adds a callback that's invoked when the user clicks on the map view. * * @param listener The callback that's invoked when the user clicks on the map view. - * To unset the callback, use null. */ - public void addOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onAddMapClickListener(listener); + public void addOnMapClickListener(@NonNull OnMapClickListener listener) { + onGesturesManagerInteractionListener.onAddMapClickListener(listener); } /** * Removes a callback that's invoked when the user clicks on the map view. * * @param listener The callback that's invoked when the user clicks on the map view. - * To unset the callback, use null. */ - public void removeOnMapClickListener(@Nullable OnMapClickListener listener) { - onRegisterTouchListener.onRemoveMapClickListener(listener); + public void removeOnMapClickListener(@NonNull OnMapClickListener listener) { + onGesturesManagerInteractionListener.onRemoveMapClickListener(listener); } /** @@ -1983,27 +1964,25 @@ public final class MapboxMap { */ @Deprecated public void setOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onSetMapLongClickListener(listener); + onGesturesManagerInteractionListener.onSetMapLongClickListener(listener); } /** * Adds a callback that's invoked when the user long clicks on the map view. * * @param listener The callback that's invoked when the user long clicks on the map view. - * To unset the callback, use null. */ - public void addOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onAddMapLongClickListener(listener); + public void addOnMapLongClickListener(@NonNull OnMapLongClickListener listener) { + onGesturesManagerInteractionListener.onAddMapLongClickListener(listener); } /** * Removes a callback that's invoked when the user long clicks on the map view. * * @param listener The callback that's invoked when the user long clicks on the map view. - * To unset the callback, use null. */ - public void removeOnMapLongClickListener(@Nullable OnMapLongClickListener listener) { - onRegisterTouchListener.onRemoveMapLongClickListener(listener); + public void removeOnMapLongClickListener(@NonNull OnMapLongClickListener listener) { + onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener); } /** @@ -2064,107 +2043,6 @@ public final class MapboxMap { } // - // User location - // - - /** - * Returns the status of the my-location layer. - * - * @return True if the my-location layer is enabled, false otherwise. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public boolean isMyLocationEnabled() { - return trackingSettings.isMyLocationEnabled(); - } - - /** - * <p> - * Enables or disables the my-location layer. - * While enabled, the my-location layer continuously draws an indication of a user's current - * location and bearing. - * </p> - * In order to use the my-location layer feature you need to request permission for either - * android.Manifest.permission#ACCESS_COARSE_LOCATION or android.Manifest.permission#ACCESS_FINE_LOCATION. - * - * @param enabled True to enable; false to disable. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public void setMyLocationEnabled(boolean enabled) { - trackingSettings.setMyLocationEnabled(enabled); - } - - /** - * Returns the currently displayed user location, or null if there is no location data available. - * - * @return The currently displayed user location. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Nullable - @Deprecated - public Location getMyLocation() { - return trackingSettings.getMyLocation(); - } - - /** - * Sets a callback that's invoked when the the My Location view - * (which signifies the user's location) changes location. - * - * @param listener The callback that's invoked when the user clicks on a marker. - * To unset the callback, use null. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public void setOnMyLocationChangeListener(@Nullable MapboxMap.OnMyLocationChangeListener - listener) { - trackingSettings.setOnMyLocationChangeListener(listener); - } - - /** - * Replaces the location source of the my-location layer. - * - * @param locationSource A {@link LocationEngine} location source to use in the my-location layer. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public void setLocationSource(@Nullable LocationEngine locationSource) { - trackingSettings.setLocationSource(locationSource); - } - - /** - * Sets a callback that's invoked when the location tracking mode changes. - * - * @param listener The callback that's invoked when the location tracking mode changes. - * To unset the callback, use null. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public void setOnMyLocationTrackingModeChangeListener( - @Nullable MapboxMap.OnMyLocationTrackingModeChangeListener listener) { - trackingSettings.setOnMyLocationTrackingModeChangeListener(listener); - } - - /** - * Sets a callback that's invoked when the bearing tracking mode changes. - * - * @param listener The callback that's invoked when the bearing tracking mode changes. - * To unset the callback, use null. - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ - @Deprecated - public void setOnMyBearingTrackingModeChangeListener(@Nullable OnMyBearingTrackingModeChangeListener listener) { - trackingSettings.setOnMyBearingTrackingModeChangeListener(listener); - } - - // // Invalidate // @@ -2194,13 +2072,13 @@ public final class MapboxMap { * Queries the map for rendered features * * @param coordinates the point to query - * @param filter filters the returned features + * @param filter filters the returned features with an expression * @param layerIds optionally - only query these layers * @return the list of feature */ @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, - @Nullable Filter.Statement filter, + @Nullable Expression filter, @Nullable String... layerIds) { return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); } @@ -2222,26 +2100,17 @@ public final class MapboxMap { * Queries the map for rendered features * * @param coordinates the box to query - * @param filter filters the returned features + * @param filter filters the returned features with an expression * @param layerIds optionally - only query these layers * @return the list of feature */ @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, - @Nullable Filter.Statement filter, + @Nullable Expression filter, @Nullable String... layerIds) { return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); } - FocalPointChangeListener createFocalPointChangeListener() { - return new FocalPointChangeListener() { - @Override - public void onFocalPointChanged(PointF pointF) { - focalPoint = pointF; - } - }; - } - // // Interfaces // @@ -2262,7 +2131,9 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when the map is scrolled. * * @see MapboxMap#setOnScrollListener(OnScrollListener) + * @deprecated Use {@link OnMoveListener} instead. */ + @Deprecated public interface OnScrollListener { /** * Called when the map is scrolled. @@ -2271,6 +2142,58 @@ public final class MapboxMap { } /** + * Interface definition for a callback to be invoked when the map is moved. + * + * @see MapboxMap#addOnMoveListener(OnMoveListener) + */ + public interface OnMoveListener { + void onMoveBegin(MoveGestureDetector detector); + + void onMove(MoveGestureDetector detector); + + void onMoveEnd(MoveGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is rotated. + * + * @see MapboxMap#addOnRotateListener(OnRotateListener) + */ + public interface OnRotateListener { + void onRotateBegin(RotateGestureDetector detector); + + void onRotate(RotateGestureDetector detector); + + void onRotateEnd(RotateGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is scaled. + * + * @see MapboxMap#addOnScaleListener(OnScaleListener) + */ + public interface OnScaleListener { + void onScaleBegin(StandardScaleGestureDetector detector); + + void onScale(StandardScaleGestureDetector detector); + + void onScaleEnd(StandardScaleGestureDetector detector); + } + + /** + * Interface definition for a callback to be invoked when the map is tilted. + * + * @see MapboxMap#addOnShoveListener(OnShoveListener) + */ + public interface OnShoveListener { + void onShoveBegin(ShoveGestureDetector detector); + + void onShove(ShoveGestureDetector detector); + + void onShoveEnd(ShoveGestureDetector detector); + } + + /** * Interface definition for a callback to be invoked when the camera changes position. * * @deprecated Replaced by {@link MapboxMap.OnCameraMoveStartedListener}, {@link MapboxMap.OnCameraMoveListener} and @@ -2372,7 +2295,7 @@ public final class MapboxMap { * Interface definition for a callback to be invoked when a user registers an listener that is * related to touch and click events. */ - interface OnRegisterTouchListener { + interface OnGesturesManagerInteractionListener { void onSetMapClickListener(OnMapClickListener listener); void onAddMapClickListener(OnMapClickListener listener); @@ -2396,6 +2319,29 @@ public final class MapboxMap { void onAddFlingListener(OnFlingListener listener); void onRemoveFlingListener(OnFlingListener listener); + + void onAddMoveListener(OnMoveListener listener); + + void onRemoveMoveListener(OnMoveListener listener); + + void onAddRotateListener(OnRotateListener listener); + + void onRemoveRotateListener(OnRotateListener listener); + + void onAddScaleListener(OnScaleListener listener); + + void onRemoveScaleListener(OnScaleListener listener); + + void onAddShoveListener(OnShoveListener listener); + + void onRemoveShoveListener(OnShoveListener listener); + + AndroidGesturesManager getGesturesManager(); + + void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners, + boolean setDefaultMutuallyExclusives); + + void cancelAllVelocityAnimations(); } /** @@ -2668,51 +2614,6 @@ public final class MapboxMap { } /** - * Interface definition for a callback to be invoked when the the My Location view changes location. - * - * @see MapboxMap#setOnMyLocationChangeListener(OnMyLocationChangeListener) - */ - public interface OnMyLocationChangeListener { - /** - * Called when the location of the My Location view has changed - * (be it latitude/longitude, bearing or accuracy). - * - * @param location The current location of the My Location view The type of map change event. - */ - void onMyLocationChange(@Nullable Location location); - } - - /** - * Interface definition for a callback to be invoked when the the My Location tracking mode changes. - * - * @see TrackingSettings#setMyLocationTrackingMode(int) - */ - public interface OnMyLocationTrackingModeChangeListener { - - /** - * Called when the tracking mode of My Location tracking has changed - * - * @param myLocationTrackingMode the current active location tracking mode - */ - void onMyLocationTrackingModeChange(@MyLocationTracking.Mode int myLocationTrackingMode); - } - - /** - * Interface definition for a callback to be invoked when the the My Location tracking mode changes. - * - * @see TrackingSettings#setMyLocationTrackingMode(int) - */ - public interface OnMyBearingTrackingModeChangeListener { - - /** - * Called when the tracking mode of My Bearing tracking has changed - * - * @param myBearingTrackingMode the current active bearing tracking mode - */ - void onMyBearingTrackingModeChange(@MyBearingTracking.Mode int myBearingTrackingMode); - } - - /** * Interface definition for a callback to be invoked when a task is complete or cancelled. */ public interface CancelableCallback { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java index 46dba28b98..bb4e2f9212 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java @@ -8,10 +8,8 @@ import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; import android.support.v4.content.res.ResourcesCompat; import android.util.AttributeSet; import android.view.Gravity; @@ -20,7 +18,6 @@ import com.mapbox.mapboxsdk.R; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.utils.BitmapUtils; -import com.mapbox.mapboxsdk.utils.ColorUtils; import java.util.Arrays; @@ -68,18 +65,6 @@ public class MapboxMapOptions implements Parcelable { private boolean zoomControlsEnabled = false; private boolean doubleTapGesturesEnabled = true; - private boolean myLocationEnabled; - private Drawable myLocationForegroundDrawable; - private Drawable myLocationForegroundBearingDrawable; - private Drawable myLocationBackgroundDrawable; - @ColorInt - private int myLocationForegroundTintColor = UNDEFINED_COLOR; - @ColorInt - private int myLocationBackgroundTintColor = UNDEFINED_COLOR; - private int[] myLocationBackgroundPadding; - private int myLocationAccuracyTintColor; - private int myLocationAccuracyAlpha; - private float myLocationAccuracyThreshold; private boolean prefetchesTiles = true; private boolean zMediaOverlay = false; private String localIdeographFontFamily; @@ -87,6 +72,7 @@ public class MapboxMapOptions implements Parcelable { private String apiBaseUrl; private boolean textureMode; + private boolean translucentTextureSurface; private String style; @@ -129,33 +115,10 @@ public class MapboxMapOptions implements Parcelable { zoomGesturesEnabled = in.readByte() != 0; doubleTapGesturesEnabled = in.readByte() != 0; - myLocationEnabled = in.readByte() != 0; - - Bitmap foregroundBitmap = in.readParcelable(getClass().getClassLoader()); - if (foregroundBitmap != null) { - myLocationForegroundDrawable = new BitmapDrawable(foregroundBitmap); - } - - Bitmap foregroundBearingBitmap = in.readParcelable(getClass().getClassLoader()); - if (foregroundBearingBitmap != null) { - myLocationForegroundBearingDrawable = new BitmapDrawable(foregroundBearingBitmap); - } - - Bitmap backgroundBitmap = in.readParcelable(getClass().getClassLoader()); - if (backgroundBitmap != null) { - myLocationBackgroundDrawable = new BitmapDrawable(backgroundBitmap); - } - - myLocationForegroundTintColor = in.readInt(); - myLocationBackgroundTintColor = in.readInt(); - myLocationBackgroundPadding = in.createIntArray(); - myLocationAccuracyAlpha = in.readInt(); - myLocationAccuracyTintColor = in.readInt(); - myLocationAccuracyThreshold = in.readFloat(); - style = in.readString(); apiBaseUrl = in.readString(); textureMode = in.readByte() != 0; + translucentTextureSurface = in.readByte() != 0; prefetchesTiles = in.readByte() != 0; zMediaOverlay = in.readByte() != 0; localIdeographFontFamily = in.readString(); @@ -245,50 +208,10 @@ public class MapboxMapOptions implements Parcelable { (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom, FOUR_DP * pxlRatio))}); - mapboxMapOptions.locationEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_myLocation, false)); - mapboxMapOptions.myLocationForegroundTintColor( - typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationTintColor, UNDEFINED_COLOR)); - mapboxMapOptions.myLocationBackgroundTintColor( - typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundTintColor, UNDEFINED_COLOR)); - - Drawable foregroundDrawable = typedArray.getDrawable(R.styleable.mapbox_MapView_mapbox_myLocationDrawable); - if (foregroundDrawable == null) { - foregroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_default); - } - - Drawable foregroundBearingDrawable = typedArray.getDrawable( - R.styleable.mapbox_MapView_mapbox_myLocationBearingDrawable); - if (foregroundBearingDrawable == null) { - foregroundBearingDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_icon_bearing); - } - - Drawable backgroundDrawable = typedArray.getDrawable( - R.styleable.mapbox_MapView_mapbox_myLocationBackgroundDrawable); - if (backgroundDrawable == null) { - backgroundDrawable = ContextCompat.getDrawable(context, R.drawable.mapbox_mylocation_bg_shape); - } - - mapboxMapOptions.myLocationForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - mapboxMapOptions.myLocationBackgroundDrawable(backgroundDrawable); - mapboxMapOptions.myLocationBackgroundPadding(new int[] { - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginLeft, - 0) * pxlRatio), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginTop, - 0) * pxlRatio), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginRight, - 0) * pxlRatio), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_myLocationBackgroundMarginBottom, - 0) * pxlRatio) - }); - mapboxMapOptions.myLocationAccuracyAlpha( - typedArray.getInt(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyAlpha, 100)); - mapboxMapOptions.myLocationAccuracyTint( - typedArray.getColor(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyTintColor, - ColorUtils.getPrimaryColor(context))); - mapboxMapOptions.myLocationAccuracyThreshold( - typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_myLocationAccuracyThreshold, 0)); mapboxMapOptions.textureMode( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); + mapboxMapOptions.translucentTextureSurface( + typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureTranslucentSurface, false)); mapboxMapOptions.setPrefetchesTiles( typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); mapboxMapOptions.renderSurfaceOnTop( @@ -572,128 +495,6 @@ public class MapboxMapOptions implements Parcelable { } /** - * Specifies if the user location view is enabled for a map view. - * - * @param locationEnabled True and gesture will be enabled - * @return This - */ - public MapboxMapOptions locationEnabled(boolean locationEnabled) { - this.myLocationEnabled = locationEnabled; - return this; - } - - /** - * Set the foreground drawables of the MyLocationView. - * - * @param myLocationForegroundDrawable the drawable to show as foreground without bearing - * @param myLocationBearingDrawable the drawable to show as foreground when bearing is disabled - * @return This - */ - public MapboxMapOptions myLocationForegroundDrawables(Drawable myLocationForegroundDrawable, - Drawable myLocationBearingDrawable) { - this.myLocationForegroundDrawable = myLocationForegroundDrawable; - this.myLocationForegroundBearingDrawable = myLocationBearingDrawable; - return this; - } - - /** - * Set the foreground drawable of the MyLocationView. - * <p> - * The same drawable will be used for both bearing as non bearing modes. - * </p> - * - * @param myLocationForegroundDrawable the drawable to show as foreground - * @return This - */ - public MapboxMapOptions myLocationForegroundDrawable(Drawable myLocationForegroundDrawable) { - this.myLocationForegroundDrawable = myLocationForegroundDrawable; - return this; - } - - /** - * Set the background drawable of MyLocationView. - * <p> - * Padding can be added to provide an offset to the background. - * </p> - * - * @param myLocationBackgroundDrawable the drawable to show as background - * @return This - */ - public MapboxMapOptions myLocationBackgroundDrawable(Drawable myLocationBackgroundDrawable) { - this.myLocationBackgroundDrawable = myLocationBackgroundDrawable; - return this; - } - - /** - * Set the foreground tint color of MyLocationView. - * <p> - * The color will tint both the foreground and the bearing foreground drawable. - * </p> - * - * @param myLocationForegroundTintColor the color to tint the foreground drawable - * @return This - */ - public MapboxMapOptions myLocationForegroundTintColor(@ColorInt int myLocationForegroundTintColor) { - this.myLocationForegroundTintColor = myLocationForegroundTintColor; - return this; - } - - /** - * Set the background tint color of MyLocationView. - * - * @param myLocationBackgroundTintColor the color to tint the background drawable - * @return This - */ - public MapboxMapOptions myLocationBackgroundTintColor(@ColorInt int myLocationBackgroundTintColor) { - this.myLocationBackgroundTintColor = myLocationBackgroundTintColor; - return this; - } - - /** - * Set the MyLocationView padding. - * - * @param myLocationBackgroundPadding the color to tint the background - * @return This - */ - public MapboxMapOptions myLocationBackgroundPadding(int[] myLocationBackgroundPadding) { - this.myLocationBackgroundPadding = myLocationBackgroundPadding; - return this; - } - - /** - * Set the MyLocationView accuracy circle tint color. - * - * @param myLocationAccuracyTintColor the color to tint the accuracy circle - * @return This - */ - public MapboxMapOptions myLocationAccuracyTint(@ColorInt int myLocationAccuracyTintColor) { - this.myLocationAccuracyTintColor = myLocationAccuracyTintColor; - return this; - } - - /** - * Set the MyLocationView accuracy alpha value. - * - * @param alpha the alpha value - * @return This - */ - public MapboxMapOptions myLocationAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { - this.myLocationAccuracyAlpha = alpha; - return this; - } - - /** - * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. - * - * @param myLocationAccuracyThreshold Value of accuracy (in meters), below which circle won't be displayed - * @return This - */ - public MapboxMapOptions myLocationAccuracyThreshold(float myLocationAccuracyThreshold) { - this.myLocationAccuracyThreshold = myLocationAccuracyThreshold; - return this; - } - - /** * Enable {@link android.view.TextureView} as rendered surface. * <p> * Since the 5.2.0 release we replaced our TextureView with an {@link android.opengl.GLSurfaceView} @@ -711,6 +512,11 @@ public class MapboxMapOptions implements Parcelable { return this; } + public MapboxMapOptions translucentTextureSurface(boolean translucentTextureSurface) { + this.translucentTextureSurface = translucentTextureSurface; + return this; + } + /** * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render * a low resolution preview while more detailed tiles are loaded. @@ -725,11 +531,11 @@ public class MapboxMapOptions implements Parcelable { } /** - * Set the font family for generating glyphs locally for ideographs in the ‘CJK Unified Ideographs’ - * and ‘Hangul Syllables’ ranges. - * + * Set the font family for generating glyphs locally for ideographs in the 'CJK Unified Ideographs' + * and 'Hangul Syllables' ranges. + * <p> * The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}. - * Default system fonts are defined in '/system/etc/fonts.xml' + * Default system fonts are defined in '/system/etc/fonts.xml' * * @param fontFamily font family for local ideograph generation. * @return This @@ -976,98 +782,6 @@ public class MapboxMapOptions implements Parcelable { } /** - * Get the current configured user location view state for a map view. - * - * @return True and user location will be shown - */ - public boolean getLocationEnabled() { - return myLocationEnabled; - } - - /** - * Get the current configured MyLocationView foreground drawable. - * - * @return the drawable used as foreground - */ - public Drawable getMyLocationForegroundDrawable() { - return myLocationForegroundDrawable; - } - - /** - * Get the current configured MyLocationView foreground bearing drawable. - * - * @return the drawable used as foreground when bearing is enabled - */ - public Drawable getMyLocationForegroundBearingDrawable() { - return myLocationForegroundBearingDrawable; - } - - /** - * Get the current configured MyLocationView background drawable. - * - * @return the drawable used as background - */ - public Drawable getMyLocationBackgroundDrawable() { - return myLocationBackgroundDrawable; - } - - /** - * Get the current configured MyLocationView foreground tint color. - * - * @return the tint color - */ - @ColorInt - public int getMyLocationForegroundTintColor() { - return myLocationForegroundTintColor; - } - - /** - * Get the current configured MyLocationView background tint color. - * - * @return the tint color - */ - @ColorInt - public int getMyLocationBackgroundTintColor() { - return myLocationBackgroundTintColor; - } - - /** - * Get the current configured MyLocationView background padding. - * - * @return an array describing the padding in a LTRB manner - */ - public int[] getMyLocationBackgroundPadding() { - return myLocationBackgroundPadding; - } - - /** - * Get the current configured MyLocationView accuracy circle color tint value. - * - * @return the tint color - */ - public int getMyLocationAccuracyTintColor() { - return myLocationAccuracyTintColor; - } - - /** - * Get the current configured MyLocationView accuracy circle alpha value. - * - * @return the alpha value - */ - public int getMyLocationAccuracyAlpha() { - return myLocationAccuracyAlpha; - } - - /** - * Returns current accuracy threshold value (in meters). - * - * @return Value of accuracy threshold (in meters), below which circle won't be displayed - */ - public float getMyLocationAccuracyThreshold() { - return myLocationAccuracyThreshold; - } - - /** * Get the current configured debug state for a map view. * * @return True indicates debug is enabled. @@ -1085,9 +799,13 @@ public class MapboxMapOptions implements Parcelable { return textureMode; } + public boolean getTranslucentTextureSurface() { + return translucentTextureSurface; + } + /** * Returns the font-family for locally overriding generation of glyphs in the - * ‘CJK Unified Ideographs’ and ‘Hangul Syllables’ ranges. + * 'CJK Unified Ideographs' and 'Hangul Syllables' ranges. * * @return Local ideograph font family name. */ @@ -1141,24 +859,10 @@ public class MapboxMapOptions implements Parcelable { dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0)); dest.writeByte((byte) (doubleTapGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (myLocationEnabled ? 1 : 0)); - - dest.writeParcelable(myLocationForegroundDrawable != null - ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundDrawable) : null, flags); - dest.writeParcelable(myLocationForegroundBearingDrawable != null - ? BitmapUtils.getBitmapFromDrawable(myLocationForegroundBearingDrawable) : null, flags); - dest.writeParcelable(myLocationBackgroundDrawable != null - ? BitmapUtils.getBitmapFromDrawable(myLocationBackgroundDrawable) : null, flags); - dest.writeInt(myLocationForegroundTintColor); - dest.writeInt(myLocationBackgroundTintColor); - dest.writeIntArray(myLocationBackgroundPadding); - dest.writeInt(myLocationAccuracyAlpha); - dest.writeInt(myLocationAccuracyTintColor); - dest.writeFloat(myLocationAccuracyThreshold); - dest.writeString(style); dest.writeString(apiBaseUrl); dest.writeByte((byte) (textureMode ? 1 : 0)); + dest.writeByte((byte) (translucentTextureSurface ? 1 : 0)); dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); dest.writeString(localIdeographFontFamily); @@ -1231,24 +935,6 @@ public class MapboxMapOptions implements Parcelable { if (doubleTapGesturesEnabled != options.doubleTapGesturesEnabled) { return false; } - if (myLocationEnabled != options.myLocationEnabled) { - return false; - } - if (myLocationForegroundTintColor != options.myLocationForegroundTintColor) { - return false; - } - if (myLocationBackgroundTintColor != options.myLocationBackgroundTintColor) { - return false; - } - if (myLocationAccuracyTintColor != options.myLocationAccuracyTintColor) { - return false; - } - if (myLocationAccuracyAlpha != options.myLocationAccuracyAlpha) { - return false; - } - if (myLocationAccuracyThreshold != options.myLocationAccuracyThreshold) { - return false; - } if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) { return false; } @@ -1261,24 +947,6 @@ public class MapboxMapOptions implements Parcelable { if (!Arrays.equals(attributionMargins, options.attributionMargins)) { return false; } - if (myLocationForegroundDrawable != null - ? !myLocationForegroundDrawable.equals(options.myLocationForegroundDrawable) - : options.myLocationForegroundDrawable != null) { - return false; - } - if (myLocationForegroundBearingDrawable != null - ? !myLocationForegroundBearingDrawable.equals(options.myLocationForegroundBearingDrawable) - : options.myLocationForegroundBearingDrawable != null) { - return false; - } - if (myLocationBackgroundDrawable != null - ? !myLocationBackgroundDrawable.equals(options.myLocationBackgroundDrawable) - : options.myLocationBackgroundDrawable != null) { - return false; - } - if (!Arrays.equals(myLocationBackgroundPadding, options.myLocationBackgroundPadding)) { - return false; - } if (style != null ? !style.equals(options.style) : options.style != null) { return false; } @@ -1326,20 +994,9 @@ public class MapboxMapOptions implements Parcelable { result = 31 * result + (zoomGesturesEnabled ? 1 : 0); result = 31 * result + (zoomControlsEnabled ? 1 : 0); result = 31 * result + (doubleTapGesturesEnabled ? 1 : 0); - result = 31 * result + (myLocationEnabled ? 1 : 0); - result = 31 * result + (myLocationForegroundDrawable != null ? myLocationForegroundDrawable.hashCode() : 0); - result = 31 * result + (myLocationForegroundBearingDrawable != null - ? myLocationForegroundBearingDrawable.hashCode() : 0); - result = 31 * result + (myLocationBackgroundDrawable != null ? myLocationBackgroundDrawable.hashCode() : 0); - result = 31 * result + myLocationForegroundTintColor; - result = 31 * result + myLocationBackgroundTintColor; - result = 31 * result + Arrays.hashCode(myLocationBackgroundPadding); - result = 31 * result + myLocationAccuracyTintColor; - result = 31 * result + myLocationAccuracyAlpha; - result = 31 * result + (myLocationAccuracyThreshold != +0.0f - ? Float.floatToIntBits(myLocationAccuracyThreshold) : 0); result = 31 * result + (apiBaseUrl != null ? apiBaseUrl.hashCode() : 0); result = 31 * result + (textureMode ? 1 : 0); + result = 31 * result + (translucentTextureSurface ? 1 : 0); result = 31 * result + (style != null ? style.hashCode() : 0); result = 31 * result + (prefetchesTiles ? 1 : 0); result = 31 * result + (zMediaOverlay ? 1 : 0); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index e8eb7e8718..0e77910c3d 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -11,6 +11,8 @@ import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.DisplayMetrics; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.Geometry; import com.mapbox.mapboxsdk.LibraryLoader; import com.mapbox.mapboxsdk.annotations.Icon; import com.mapbox.mapboxsdk.annotations.Marker; @@ -22,15 +24,13 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.style.expressions.Expression; import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException; -import com.mapbox.mapboxsdk.style.layers.Filter; import com.mapbox.mapboxsdk.style.layers.Layer; import com.mapbox.mapboxsdk.style.light.Light; import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.utils.BitmapUtils; -import com.mapbox.services.commons.geojson.Feature; -import com.mapbox.services.commons.geojson.Geometry; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -229,18 +229,28 @@ final class NativeMapView { return nativeGetLatLng().wrap(); } - public CameraPosition getCameraForLatLngBounds(LatLngBounds latLngBounds) { + public CameraPosition getCameraForLatLngBounds(LatLngBounds latLngBounds, int[] padding) { if (isDestroyedOn("getCameraForLatLngBounds")) { return null; } - return nativeGetCameraForLatLngBounds(latLngBounds); + return nativeGetCameraForLatLngBounds( + latLngBounds, + padding[1] / pixelRatio, + padding[0] / pixelRatio, + padding[3] / pixelRatio, + padding[2] / pixelRatio); } - public CameraPosition getCameraForGeometry(Geometry geometry, double bearing) { + public CameraPosition getCameraForGeometry(Geometry geometry, double bearing, int[] padding) { if (isDestroyedOn("getCameraForGeometry")) { return null; } - return nativeGetCameraForGeometry(geometry, bearing); + return nativeGetCameraForGeometry( + geometry, bearing, + padding[1] / pixelRatio, + padding[0] / pixelRatio, + padding[3] / pixelRatio, + padding[2] / pixelRatio); } public void resetPosition() { @@ -733,7 +743,7 @@ final class NativeMapView { if (isDestroyedOn("addSource")) { return; } - nativeAddSource(source.getNativePtr()); + nativeAddSource(source, source.getNativePtr()); } @Nullable @@ -741,14 +751,19 @@ final class NativeMapView { if (isDestroyedOn("removeSource")) { return null; } - return nativeRemoveSourceById(sourceId); + Source source = getSource(sourceId); + if (source != null) { + return removeSource(source); + } + return null; } + @Nullable public Source removeSource(@NonNull Source source) { if (isDestroyedOn("removeSource")) { return null; } - nativeRemoveSource(source.getNativePtr()); + nativeRemoveSource(source, source.getNativePtr()); return source; } @@ -757,20 +772,8 @@ final class NativeMapView { return; } - // Check/correct config - if (image.getConfig() != Bitmap.Config.ARGB_8888) { - image = image.copy(Bitmap.Config.ARGB_8888, false); - } - - // Get pixels - ByteBuffer buffer = ByteBuffer.allocate(image.getByteCount()); - image.copyPixelsToBuffer(buffer); - // Determine pixel ratio - float density = image.getDensity() == Bitmap.DENSITY_NONE ? Bitmap.DENSITY_NONE : image.getDensity(); - float pixelRatio = density / DisplayMetrics.DENSITY_DEFAULT; - - nativeAddImage(name, image.getWidth(), image.getHeight(), pixelRatio, buffer.array()); + nativeAddImage(name, image, image.getDensity() / DisplayMetrics.DENSITY_DEFAULT); } public void addImages(@NonNull HashMap<String, Bitmap> bitmapHashMap) { @@ -800,7 +803,7 @@ final class NativeMapView { @NonNull public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String[] layerIds, - @Nullable Filter.Statement filter) { + @Nullable Expression filter) { if (isDestroyedOn("queryRenderedFeatures")) { return new ArrayList<>(); } @@ -812,7 +815,7 @@ final class NativeMapView { @NonNull public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String[] layerIds, - @Nullable Filter.Statement filter) { + @Nullable Expression filter) { if (isDestroyedOn("queryRenderedFeatures")) { return new ArrayList<>(); } @@ -907,9 +910,11 @@ final class NativeMapView { private native LatLng nativeGetLatLng(); - private native CameraPosition nativeGetCameraForLatLngBounds(LatLngBounds latLngBounds); + private native CameraPosition nativeGetCameraForLatLngBounds( + LatLngBounds latLngBounds, double top, double left, double bottom, double right); - private native CameraPosition nativeGetCameraForGeometry(Geometry geometry, double bearing); + private native CameraPosition nativeGetCameraForGeometry( + Geometry geometry, double bearing, double top, double left, double bottom, double right); private native void nativeResetPosition(); @@ -1027,14 +1032,11 @@ final class NativeMapView { private native Source nativeGetSource(String sourceId); - private native void nativeAddSource(long nativeSourcePtr) throws CannotAddSourceException; - - private native Source nativeRemoveSourceById(String sourceId); + private native void nativeAddSource(Source source, long sourcePtr) throws CannotAddSourceException; - private native void nativeRemoveSource(long sourcePtr); + private native void nativeRemoveSource(Source source, long sourcePtr); - private native void nativeAddImage(String name, int width, int height, float pixelRatio, - byte[] array); + private native void nativeAddImage(String name, Bitmap bitmap, float pixelRatio); private native void nativeAddImages(Image[] images); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index ae559189ad..f35355533d 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -9,6 +9,9 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.geometry.ProjectedMeters; import com.mapbox.mapboxsdk.geometry.VisibleRegion; +import java.util.ArrayList; +import java.util.List; + /** * A projection is used to translate between on screen location and geographic coordinates on * the surface of the Earth. Screen location is in screen pixels (not display pixels) @@ -24,25 +27,17 @@ public class Projection { this.contentPadding = new int[] {0, 0, 0, 0}; } - void setContentPadding(int[] contentPadding, int[] userLocationViewPadding) { + void setContentPadding(int[] contentPadding) { this.contentPadding = contentPadding; - - int[] padding = new int[] { - contentPadding[0] + userLocationViewPadding[0], - contentPadding[1] + userLocationViewPadding[1], - contentPadding[2] + userLocationViewPadding[2], - contentPadding[3] + userLocationViewPadding[3] - }; - - nativeMapView.setContentPadding(padding); + nativeMapView.setContentPadding(contentPadding); } int[] getContentPadding() { return contentPadding; } - public void invalidateContentPadding(int[] userLocationViewPadding) { - setContentPadding(contentPadding, userLocationViewPadding); + public void invalidateContentPadding() { + setContentPadding(contentPadding); } /** @@ -103,14 +98,49 @@ public class Projection { LatLng bottomRight = fromScreenLocation(new PointF(right, bottom)); LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); - return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - new LatLngBounds.Builder() - .include(topRight) - .include(bottomLeft) - .include(bottomRight) - .include(topLeft) - .build() - ); + // Map can be rotated, find correct LatLngBounds that encompasses the visible region (that might be rotated) + List<LatLng> boundsPoints = new ArrayList<>(); + boundsPoints.add(topLeft); + boundsPoints.add(topRight); + boundsPoints.add(bottomRight); + boundsPoints.add(bottomLeft); + + // order so that two most northern point are put first + while ((boundsPoints.get(0).getLatitude() < boundsPoints.get(3).getLatitude()) + || (boundsPoints.get(1).getLatitude() < boundsPoints.get(2).getLatitude())) { + LatLng first = boundsPoints.remove(0); + boundsPoints.add(first); + } + + double north = boundsPoints.get(0).getLatitude(); + if (north < boundsPoints.get(1).getLatitude()) { + north = boundsPoints.get(1).getLatitude(); + } + + double south = boundsPoints.get(2).getLatitude(); + if (south > boundsPoints.get(3).getLatitude()) { + south = boundsPoints.get(3).getLatitude(); + } + + double firstLon = boundsPoints.get(0).getLongitude(); + double secondLon = boundsPoints.get(1).getLongitude(); + double thridLon = boundsPoints.get(2).getLongitude(); + double fourthLon = boundsPoints.get(3).getLongitude(); + + // if it does not go over the date line + if (secondLon > fourthLon && firstLon < thridLon) { + return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, + LatLngBounds.from(north, + secondLon > thridLon ? secondLon : thridLon, + south, + firstLon < fourthLon ? firstLon : fourthLon)); + } else { + return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, + LatLngBounds.from(north, + secondLon < thridLon ? secondLon : thridLon, + south, + firstLon > fourthLon ? firstLon : fourthLon)); + } } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java new file mode 100644 index 0000000000..e6b93e4b91 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Telemetry.java @@ -0,0 +1,48 @@ +package com.mapbox.mapboxsdk.maps; + + +import com.mapbox.android.telemetry.MapboxTelemetry; +import com.mapbox.android.telemetry.SessionInterval; +import com.mapbox.android.telemetry.TelemetryEnabler; +import com.mapbox.mapboxsdk.BuildConfig; +import com.mapbox.mapboxsdk.Mapbox; + +public class Telemetry { + static final String TWO_FINGER_TAP = "TwoFingerTap"; + static final String DOUBLE_TAP = "DoubleTap"; + static final String SINGLE_TAP = "SingleTap"; + static final String PAN = "Pan"; + static final String PINCH = "Pinch"; + static final String ROTATION = "Rotation"; + static final String PITCH = "Pitch"; + private MapboxTelemetry telemetry; + + private Telemetry() { + telemetry = new MapboxTelemetry(Mapbox.getApplicationContext(), Mapbox.getAccessToken(), + BuildConfig.MAPBOX_EVENTS_USER_AGENT); + TelemetryEnabler.State telemetryState = TelemetryEnabler.retrieveTelemetryStateFromPreferences(); + if (TelemetryEnabler.State.ENABLED.equals(telemetryState)) { + telemetry.enable(); + } + } + + public static void initialize() { + obtainTelemetry(); + } + + public static void updateDebugLoggingEnabled(boolean debugLoggingEnabled) { + TelemetryHolder.INSTANCE.telemetry.updateDebugLoggingEnabled(debugLoggingEnabled); + } + + public static boolean updateSessionIdRotationInterval(SessionInterval interval) { + return TelemetryHolder.INSTANCE.telemetry.updateSessionIdRotationInterval(interval); + } + + private static class TelemetryHolder { + private static final Telemetry INSTANCE = new Telemetry(); + } + + static MapboxTelemetry obtainTelemetry() { + return TelemetryHolder.INSTANCE.telemetry; + } +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java deleted file mode 100644 index 81fd091c90..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TrackingSettings.java +++ /dev/null @@ -1,419 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.location.Location; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; - -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.constants.MyBearingTracking; -import com.mapbox.mapboxsdk.constants.MyLocationTracking; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.permissions.PermissionsManager; - -import timber.log.Timber; - -/** - * Settings for the user location and bearing tracking of a MapboxMap. - * - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ -@Deprecated -public final class TrackingSettings { - - private final MyLocationView myLocationView; - private final UiSettings uiSettings; - private final FocalPointChangeListener focalPointChangedListener; - private final CameraZoomInvalidator zoomInvalidator; - private LocationEngine locationSource; - private LocationEngineListener myLocationListener; - private boolean locationChangeAnimationEnabled = true; - private boolean isCustomLocationSource; - - private boolean myLocationEnabled; - private boolean dismissLocationTrackingOnGesture = true; - private boolean dismissBearingTrackingOnGesture = true; - - private MapboxMap.OnMyLocationTrackingModeChangeListener onMyLocationTrackingModeChangeListener; - private MapboxMap.OnMyBearingTrackingModeChangeListener onMyBearingTrackingModeChangeListener; - - TrackingSettings(@NonNull MyLocationView myLocationView, UiSettings uiSettings, - FocalPointChangeListener focalPointChangedListener, CameraZoomInvalidator zoomInvalidator) { - this.myLocationView = myLocationView; - this.focalPointChangedListener = focalPointChangedListener; - this.uiSettings = uiSettings; - this.zoomInvalidator = zoomInvalidator; - } - - void initialise(MapboxMapOptions options) { - locationSource = Mapbox.getLocationEngine(); - setMyLocationEnabled(options.getLocationEnabled()); - } - - void onSaveInstanceState(Bundle outState) { - outState.putInt(MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, getMyLocationTrackingMode()); - outState.putInt(MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, getMyBearingTrackingMode()); - 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_LOCATION_CHANGE_ANIMATION_ENABLED, isLocationChangeAnimationEnabled()); - outState.putBoolean(MapboxConstants.STATE_USING_CUSTOM_LOCATION_SOURCE, isCustomLocationSource()); - } - - void onRestoreInstanceState(Bundle savedInstanceState) { - try { - setMyLocationEnabled( - savedInstanceState.getBoolean(MapboxConstants.STATE_MY_LOCATION_ENABLED), - savedInstanceState.getBoolean(MapboxConstants.STATE_USING_CUSTOM_LOCATION_SOURCE) - ); - } catch (SecurityException ignore) { - // User did not accept location permissions - } - // noinspection ResourceType - setMyLocationTrackingMode(savedInstanceState.getInt( - MapboxConstants.STATE_MY_LOCATION_TRACKING_MODE, MyLocationTracking.TRACKING_NONE)); - // noinspection ResourceType - setMyBearingTrackingMode(savedInstanceState.getInt( - MapboxConstants.STATE_MY_BEARING_TRACKING_MODE, MyBearingTracking.NONE)); - setDismissLocationTrackingOnGesture(savedInstanceState.getBoolean( - MapboxConstants.STATE_MY_LOCATION_TRACKING_DISMISS, true)); - setDismissBearingTrackingOnGesture(savedInstanceState.getBoolean( - MapboxConstants.STATE_MY_BEARING_TRACKING_DISMISS, true)); - setLocationChangeAnimationEnabled(savedInstanceState.getBoolean( - MapboxConstants.STATE_LOCATION_CHANGE_ANIMATION_ENABLED, true)); - } - - /** - * <p> - * Set the current my location tracking mode. - * </p> - * <p> - * Will enable my location if not active. - * </p> - * See {@link MyLocationTracking} for different values. - * - * @param myLocationTrackingMode The location tracking mode to be used. - * @throws SecurityException if no suitable permission is present - * @see MyLocationTracking - */ - @UiThread - public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { - myLocationView.setLocationChangeAnimationEnabled(isLocationChangeAnimationEnabled()); - myLocationView.setMyLocationTrackingMode(myLocationTrackingMode); - - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - zoomInvalidator.zoomTo(2.0); - focalPointChangedListener.onFocalPointChanged(myLocationView.getCenter()); - } else { - focalPointChangedListener.onFocalPointChanged(null); - } - - if (onMyLocationTrackingModeChangeListener != null) { - onMyLocationTrackingModeChangeListener.onMyLocationTrackingModeChange(myLocationTrackingMode); - } - } - - /** - * Returns the current user location tracking mode. - * - * @return The current user location tracking mode. - * One of the values from {@link MyLocationTracking.Mode}. - * @see MyLocationTracking.Mode - */ - @UiThread - @MyLocationTracking.Mode - public int getMyLocationTrackingMode() { - return myLocationView.getMyLocationTrackingMode(); - } - - /** - * <p> - * Set the current my bearing tracking mode. - * </p> - * Shows the direction the user is heading. - * <p> - * When location tracking is disabled the direction of {@link MyLocationView} is rotated. When - * location tracking is enabled the {@link MapView} is rotated based on the bearing value. - * </p> - * See {@link MyBearingTracking} for different values. - * - * @param myBearingTrackingMode The bearing tracking mode to be used. - * @throws SecurityException if no suitable permission is present - * @see MyBearingTracking - */ - @UiThread - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - myLocationView.setMyBearingTrackingMode(myBearingTrackingMode); - if (onMyBearingTrackingModeChangeListener != null) { - onMyBearingTrackingModeChangeListener.onMyBearingTrackingModeChange(myBearingTrackingMode); - } - } - - /** - * Returns the current user bearing tracking mode. - * See {@link MyBearingTracking} for possible return values. - * - * @return the current user bearing tracking mode. - * @see MyBearingTracking - */ - @UiThread - @MyBearingTracking.Mode - public int getMyBearingTrackingMode() { - return myLocationView.getMyBearingTrackingMode(); - } - - /** - * Returns if all tracking modes will be dismissed when a gesture occurs. - * - * @return True to indicate that location and bearing tracking will be dismissed. - */ - public boolean isAllDismissTrackingOnGesture() { - return dismissLocationTrackingOnGesture && dismissBearingTrackingOnGesture; - } - - /** - * Set the dismissal of the tracking modes if a gesture occurs. - * - * @param dismissTrackingOnGesture True to dismiss all the tracking modes. - */ - public void setDismissAllTrackingOnGesture(boolean dismissTrackingOnGesture) { - dismissLocationTrackingOnGesture = dismissTrackingOnGesture; - dismissBearingTrackingOnGesture = dismissTrackingOnGesture; - } - - /** - * Set the dismissal of the tracking modes if a gesture occurs. - * - * @param dismissLocationTrackingOnGesture True to dismiss the location tracking mode. - */ - public void setDismissLocationTrackingOnGesture(boolean dismissLocationTrackingOnGesture) { - this.dismissLocationTrackingOnGesture = dismissLocationTrackingOnGesture; - } - - /** - * Returns if the location tracking will be disabled when a gesture occurs - * - * @return True if location tracking will be disabled. - */ - public boolean isDismissLocationTrackingOnGesture() { - return dismissLocationTrackingOnGesture; - } - - /** - * Set the dismissal of the bearing tracking modes if a gesture occurs. - * - * @param dismissBearingTrackingOnGesture True to dimsiss the bearinf tracking mode - */ - public void setDismissBearingTrackingOnGesture(boolean dismissBearingTrackingOnGesture) { - this.dismissBearingTrackingOnGesture = dismissBearingTrackingOnGesture; - } - - /** - * Returns if bearing will disabled when a gesture occurs - * - * @return True if bearing tracking will be disabled - */ - public boolean isDismissBearingTrackingOnGesture() { - return dismissBearingTrackingOnGesture; - } - - /** - * Returns if location tracking is disabled - * - * @return True if location tracking is disabled. - */ - public boolean isLocationTrackingDisabled() { - return myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE; - } - - /** - * Returns if bearing tracking disabled - * - * @return True if bearing tracking is disabled. - */ - public boolean isBearingTrackingDisabled() { - return myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE; - } - - /** - * Returns if rotate gesture are currently enabled. - * - * @return True if rotate gestures are currently enabled. - */ - public boolean isRotateGestureCurrentlyEnabled() { - // rotate gestures are recognised if: - // The user settings are enabled AND; - // EITHER bearing tracking is dismissed on gesture OR there is no bearing tracking - return uiSettings.isRotateGesturesEnabled() - && (dismissBearingTrackingOnGesture - || myLocationView.getMyBearingTrackingMode() == MyBearingTracking.NONE - || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); - } - - /** - * Returns if scroll gesture are currently enabled. - * - * @return True if scroll gestures are currently enabled. - */ - public boolean isScrollGestureCurrentlyEnabled() { - return uiSettings.isScrollGesturesEnabled() - && (dismissLocationTrackingOnGesture - || myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE); - } - - /** - * Returns whether location change animation is applied for {@link MyLocationTracking#TRACKING_FOLLOW}. - * - * @return True if animation is applied, false otherwise. - */ - public boolean isLocationChangeAnimationEnabled() { - return locationChangeAnimationEnabled; - } - - /** - * Set whether location change animation should be applied for {@link MyLocationTracking#TRACKING_FOLLOW}. - * - * @param locationChangeAnimationEnabled True if animation should be applied, false otherwise. - */ - public void setLocationChangeAnimationEnabled(boolean locationChangeAnimationEnabled) { - this.locationChangeAnimationEnabled = locationChangeAnimationEnabled; - - myLocationView.setLocationChangeAnimationEnabled(locationChangeAnimationEnabled); - } - - /** - * 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 isFromLocation true if from location - */ - 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() && !isFromLocation) { - setMyLocationTrackingMode(MyLocationTracking.TRACKING_NONE); - } - - // reset bearing tracking only on rotate - if (rotate && !isBearingTrackingDisabled() && isDismissBearingTrackingOnGesture()) { - setMyBearingTrackingMode(MyBearingTracking.NONE); - } - } - - /** - * Reset the tracking modes as necessary. Animated camera position changes can reset the underlying tracking modes. - * - * @param currentCameraPosition the current camera position - * @param targetCameraPosition the changed camera position - * @param isFromLocation true if from location - */ - void resetTrackingModesIfRequired(CameraPosition currentCameraPosition, CameraPosition targetCameraPosition, - boolean isFromLocation) { - if (currentCameraPosition.target != null) { - resetTrackingModesIfRequired(!currentCameraPosition.target.equals(targetCameraPosition.target), false, - isFromLocation); - } - } - - Location getMyLocation() { - return myLocationView.getLocation(); - } - - void setOnMyLocationChangeListener(@Nullable final MapboxMap.OnMyLocationChangeListener listener) { - if (listener != null) { - myLocationListener = new LocationEngineListener() { - @Override - public void onConnected() { - // Nothing - } - - @Override - public void onLocationChanged(Location location) { - if (listener != null) { - listener.onMyLocationChange(location); - } - } - }; - locationSource.addLocationEngineListener(myLocationListener); - } else { - locationSource.removeLocationEngineListener(myLocationListener); - myLocationListener = null; - } - } - - public boolean isCustomLocationSource() { - return isCustomLocationSource; - } - - void setOnMyLocationTrackingModeChangeListener(MapboxMap.OnMyLocationTrackingModeChangeListener listener) { - this.onMyLocationTrackingModeChangeListener = listener; - } - - void setOnMyBearingTrackingModeChangeListener(MapboxMap.OnMyBearingTrackingModeChangeListener listener) { - this.onMyBearingTrackingModeChangeListener = listener; - } - - MyLocationView getMyLocationView() { - return myLocationView; - } - - - boolean isMyLocationEnabled() { - return myLocationEnabled; - } - - void setMyLocationEnabled(boolean locationEnabled) { - setMyLocationEnabled(locationEnabled, isCustomLocationSource()); - } - - private void setMyLocationEnabled(boolean locationEnabled, boolean isCustomLocationSource) { - if (locationEnabled && !PermissionsManager.areLocationPermissionsGranted(myLocationView.getContext())) { - Timber.e("Could not activate user location tracking: " - + "user did not accept the permission or permissions were not requested."); - return; - } - myLocationEnabled = locationEnabled; - this.isCustomLocationSource = isCustomLocationSource; - myLocationView.setEnabled(locationEnabled, isCustomLocationSource); - } - - void setLocationSource(LocationEngine locationSource) { - if (this.locationSource != null && this.locationSource.equals(locationSource)) { - // this source is already active - return; - } - - this.isCustomLocationSource = locationSource != null; - if (locationSource == null) { - locationSource = Mapbox.getLocationEngine(); - } - this.locationSource = locationSource; - myLocationView.setLocationSource(locationSource); - } - - void update() { - if (!myLocationView.isEnabled()) { - return; - } - myLocationView.update(); - } - - void onStart() { - myLocationView.onStart(); - } - - void onStop() { - myLocationView.onStop(); - } - - interface CameraZoomInvalidator { - void zoomTo(double zoomLevel); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java index 84a601039f..f76e54984b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java @@ -12,7 +12,6 @@ import com.mapbox.mapboxsdk.camera.CameraUpdate; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.widgets.MyLocationView; import timber.log.Timber; @@ -30,8 +29,6 @@ final class Transform implements MapView.OnMapChangedListener { private final NativeMapView mapView; private final MarkerViewManager markerViewManager; - private final TrackingSettings trackingSettings; - private final MyLocationView myLocationView; private final Handler handler = new Handler(); private CameraPosition cameraPosition; @@ -41,12 +38,10 @@ final class Transform implements MapView.OnMapChangedListener { private CameraChangeDispatcher cameraChangeDispatcher; - Transform(NativeMapView mapView, MarkerViewManager markerViewManager, TrackingSettings trackingSettings, + Transform(NativeMapView mapView, MarkerViewManager markerViewManager, CameraChangeDispatcher cameraChangeDispatcher) { this.mapView = mapView; this.markerViewManager = markerViewManager; - this.trackingSettings = trackingSettings; - this.myLocationView = trackingSettings.getMyLocationView(); this.cameraChangeDispatcher = cameraChangeDispatcher; } @@ -73,9 +68,6 @@ final class Transform implements MapView.OnMapChangedListener { @UiThread void updateCameraPosition(@NonNull CameraPosition position) { - if (myLocationView != null) { - myLocationView.setCameraPosition(position); - } markerViewManager.setTilt((float) position.tilt); } @@ -103,7 +95,6 @@ final class Transform implements MapView.OnMapChangedListener { final void moveCamera(MapboxMap mapboxMap, CameraUpdate update, final MapboxMap.CancelableCallback callback) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (isValidCameraPosition(cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); mapView.jumpTo(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, cameraPosition.zoom); @@ -125,7 +116,6 @@ final class Transform implements MapView.OnMapChangedListener { final MapboxMap.CancelableCallback callback, boolean isDismissable) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (isValidCameraPosition(cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, isDismissable); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); @@ -143,7 +133,6 @@ final class Transform implements MapView.OnMapChangedListener { final MapboxMap.CancelableCallback callback) { CameraPosition cameraPosition = update.getCameraPosition(mapboxMap); if (isValidCameraPosition(cameraPosition)) { - trackingSettings.resetTrackingModesIfRequired(this.cameraPosition, cameraPosition, false); cancelTransitions(); cameraChangeDispatcher.onCameraMoveStarted(OnCameraMoveStartedListener.REASON_API_ANIMATION); @@ -205,6 +194,8 @@ final class Transform implements MapView.OnMapChangedListener { // cancel ongoing transitions mapView.cancelTransitions(); + + cameraChangeDispatcher.onCameraIdle(); } @UiThread @@ -235,39 +226,21 @@ final class Transform implements MapView.OnMapChangedListener { return mapView.getZoom(); } - void zoom(boolean zoomIn, @NonNull PointF focalPoint) { - CameraPosition cameraPosition = invalidateCameraPosition(); - if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + (zoomIn ? 1 : -1)); - setZoom(newZoom, focalPoint, MapboxConstants.ANIMATION_DURATION, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); - } - } - - void zoom(double zoomAddition, @NonNull PointF focalPoint, long duration) { - CameraPosition cameraPosition = invalidateCameraPosition(); - if (cameraPosition != null) { - int newZoom = (int) Math.round(cameraPosition.zoom + zoomAddition); - setZoom(newZoom, focalPoint, duration, false); - } else { - // we are not transforming, notify about being idle - cameraChangeDispatcher.onCameraIdle(); - } + void zoomBy(double zoomAddition, @NonNull PointF focalPoint) { + setZoom(mapView.getZoom() + zoomAddition, focalPoint); } void setZoom(double zoom, @NonNull PointF focalPoint) { - setZoom(zoom, focalPoint, 0, false); + setZoom(zoom, focalPoint, 0); } - void setZoom(double zoom, @NonNull PointF focalPoint, long duration, final boolean isAnimator) { + void setZoom(double zoom, @NonNull PointF focalPoint, final long duration) { if (mapView != null) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { @Override public void onMapChanged(int change) { if (change == MapView.REGION_DID_CHANGE_ANIMATED) { - if (!isAnimator) { + if (duration > 0) { cameraChangeDispatcher.onCameraIdle(); } mapView.removeOnMapChangedListener(this); @@ -297,23 +270,14 @@ final class Transform implements MapView.OnMapChangedListener { } void setBearing(double bearing) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } mapView.setBearing(bearing); } void setBearing(double bearing, float focalX, float focalY) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } mapView.setBearing(bearing, focalX, focalY); } void setBearing(double bearing, float focalX, float focalY, long duration) { - if (myLocationView != null) { - myLocationView.setBearing(bearing); - } mapView.setBearing(bearing, focalX, focalY, duration); } @@ -335,9 +299,6 @@ final class Transform implements MapView.OnMapChangedListener { } void setTilt(Double pitch) { - if (myLocationView != null) { - myLocationView.setTilt(pitch); - } markerViewManager.setTilt(pitch.floatValue()); mapView.setPitch(pitch, 0); } @@ -361,10 +322,6 @@ final class Transform implements MapView.OnMapChangedListener { } } - void zoomBy(double z, float x, float y) { - mapView.setZoom(mapView.getZoom() + z, new PointF(x, y), 0); - } - void moveBy(double offsetX, double offsetY, long duration) { if (duration > 0) { mapView.addOnMapChangedListener(new MapView.OnMapChangedListener() { diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java index 44f106bdd5..c1daebbe52 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java @@ -37,6 +37,7 @@ public final class UiSettings { private final ImageView attributionsView; private final int[] attributionsMargins = new int[4]; + private AttributionDialogManager attributionDialogManager; private final View logoView; private final int[] logoMargins = new int[4]; @@ -44,21 +45,23 @@ public final class UiSettings { private float pixelRatio; private boolean rotateGesturesEnabled = true; - private boolean rotateGestureChangeAllowed = true; private boolean tiltGesturesEnabled = true; - private boolean tiltGestureChangeAllowed = true; private boolean zoomGesturesEnabled = true; - private boolean zoomGestureChangeAllowed = true; private boolean scrollGesturesEnabled = true; - private boolean scrollGestureChangeAllowed = true; private boolean zoomControlsEnabled; private boolean doubleTapGesturesEnabled = true; - private boolean doubleTapGestureChangeAllowed = true; + + private boolean scaleVelocityAnimationEnabled = true; + private boolean rotateVelocityAnimationEnabled = true; + private boolean flingVelocityAnimationEnabled = true; + + private boolean increaseRotateThresholdWhenScaling = true; + private boolean increaseScaleThresholdWhenRotating = true; private boolean deselectMarkersOnTap = true; @@ -107,42 +110,39 @@ public final class UiSettings { private void initialiseGestures(MapboxMapOptions options) { setZoomGesturesEnabled(options.getZoomGesturesEnabled()); - setZoomGestureChangeAllowed(options.getZoomGesturesEnabled()); setScrollGesturesEnabled(options.getScrollGesturesEnabled()); - setScrollGestureChangeAllowed(options.getScrollGesturesEnabled()); setRotateGesturesEnabled(options.getRotateGesturesEnabled()); - setRotateGestureChangeAllowed(options.getRotateGesturesEnabled()); setTiltGesturesEnabled(options.getTiltGesturesEnabled()); - setTiltGestureChangeAllowed(options.getTiltGesturesEnabled()); setZoomControlsEnabled(options.getZoomControlsEnabled()); setDoubleTapGesturesEnabled(options.getDoubleTapGesturesEnabled()); - setDoubleTapGestureChangeAllowed(options.getDoubleTapGesturesEnabled()); } private void saveGestures(Bundle outState) { outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED, isZoomGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE, isZoomGestureChangeAllowed()); outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED, isScrollGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE, isScrollGestureChangeAllowed()); outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED, isRotateGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE, isRotateGestureChangeAllowed()); outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED, isTiltGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE, isTiltGestureChangeAllowed()); outState.putBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED, isDoubleTapGesturesEnabled()); - outState.putBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED_CHANGE, isDoubleTapGestureChangeAllowed()); + outState.putBoolean(MapboxConstants.STATE_SCALE_ANIMATION_ENABLED, isScaleVelocityAnimationEnabled()); + outState.putBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED, isRotateVelocityAnimationEnabled()); + outState.putBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED, isFlingVelocityAnimationEnabled()); + outState.putBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD, isIncreaseRotateThresholdWhenScaling()); + outState.putBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD, isIncreaseScaleThresholdWhenRotating()); } private void restoreGestures(Bundle savedInstanceState) { setZoomGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED)); - setZoomGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ZOOM_ENABLED_CHANGE)); setScrollGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED)); - setScrollGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_SCROLL_ENABLED_CHANGE)); setRotateGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED)); - setRotateGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ENABLED_CHANGE)); setTiltGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED)); - setTiltGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_TILT_ENABLED_CHANGE)); setDoubleTapGesturesEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED)); - setDoubleTapGestureChangeAllowed(savedInstanceState.getBoolean(MapboxConstants.STATE_DOUBLE_TAP_ENABLED_CHANGE)); + setScaleVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_SCALE_ANIMATION_ENABLED)); + setRotateVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_ROTATE_ANIMATION_ENABLED)); + setFlingVelocityAnimationEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_FLING_ANIMATION_ENABLED)); + setIncreaseRotateThresholdWhenScaling( + savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_ROTATE_THRESHOLD)); + setIncreaseScaleThresholdWhenRotating( + savedInstanceState.getBoolean(MapboxConstants.STATE_INCREASE_SCALE_THRESHOLD)); } private void initialiseCompass(MapboxMapOptions options, Resources resources) { @@ -535,6 +535,28 @@ public final class UiSettings { return attributionsView.getVisibility() == View.VISIBLE; } + + /** + * Set a custom attribution dialog manager. + * <p> + * Set to null to reset to default behaviour. + * </p> + * + * @param attributionDialogManager the manager class used for showing attribution + */ + public void setAttributionDialogManager(AttributionDialogManager attributionDialogManager) { + this.attributionDialogManager = attributionDialogManager; + } + + /** + * Get the custom attribution dialog manager. + * + * @return the active manager class used for showing attribution + */ + public AttributionDialogManager getAttributionDialogManager() { + return attributionDialogManager; + } + /** * <p> * Sets the gravity of the attribution. @@ -635,9 +657,7 @@ public final class UiSettings { * @param rotateGesturesEnabled If true, rotating is enabled. */ public void setRotateGesturesEnabled(boolean rotateGesturesEnabled) { - if (rotateGestureChangeAllowed) { - this.rotateGesturesEnabled = rotateGesturesEnabled; - } + this.rotateGesturesEnabled = rotateGesturesEnabled; } /** @@ -649,14 +669,6 @@ public final class UiSettings { return rotateGesturesEnabled; } - void setRotateGestureChangeAllowed(boolean rotateGestureChangeAllowed) { - this.rotateGestureChangeAllowed = rotateGestureChangeAllowed; - } - - boolean isRotateGestureChangeAllowed() { - return rotateGestureChangeAllowed; - } - /** * <p> * Changes whether the user may tilt the map. @@ -670,9 +682,8 @@ public final class UiSettings { * @param tiltGesturesEnabled If true, tilting is enabled. */ public void setTiltGesturesEnabled(boolean tiltGesturesEnabled) { - if (tiltGestureChangeAllowed) { - this.tiltGesturesEnabled = tiltGesturesEnabled; - } + this.tiltGesturesEnabled = tiltGesturesEnabled; + } /** @@ -684,14 +695,6 @@ public final class UiSettings { return tiltGesturesEnabled; } - void setTiltGestureChangeAllowed(boolean tiltGestureChangeAllowed) { - this.tiltGestureChangeAllowed = tiltGestureChangeAllowed; - } - - boolean isTiltGestureChangeAllowed() { - return tiltGestureChangeAllowed; - } - /** * <p> * Changes whether the user may zoom the map. @@ -705,9 +708,7 @@ public final class UiSettings { * @param zoomGesturesEnabled If true, zooming is enabled. */ public void setZoomGesturesEnabled(boolean zoomGesturesEnabled) { - if (zoomGestureChangeAllowed) { - this.zoomGesturesEnabled = zoomGesturesEnabled; - } + this.zoomGesturesEnabled = zoomGesturesEnabled; } /** @@ -719,14 +720,6 @@ public final class UiSettings { return zoomGesturesEnabled; } - void setZoomGestureChangeAllowed(boolean zoomGestureChangeAllowed) { - this.zoomGestureChangeAllowed = zoomGestureChangeAllowed; - } - - boolean isZoomGestureChangeAllowed() { - return zoomGestureChangeAllowed; - } - /** * <p> * Sets whether the zoom controls are enabled. @@ -765,9 +758,7 @@ public final class UiSettings { * @param doubleTapGesturesEnabled If true, zooming with a double tap is enabled. */ public void setDoubleTapGesturesEnabled(boolean doubleTapGesturesEnabled) { - if (doubleTapGestureChangeAllowed) { - this.doubleTapGesturesEnabled = doubleTapGesturesEnabled; - } + this.doubleTapGesturesEnabled = doubleTapGesturesEnabled; } /** @@ -779,14 +770,6 @@ public final class UiSettings { return doubleTapGesturesEnabled; } - void setDoubleTapGestureChangeAllowed(boolean doubleTapGestureChangeAllowed) { - this.doubleTapGestureChangeAllowed = doubleTapGestureChangeAllowed; - } - - boolean isDoubleTapGestureChangeAllowed() { - return doubleTapGestureChangeAllowed; - } - private void restoreDeselectMarkersOnTap(Bundle savedInstanceState) { setDeselectMarkersOnTap(savedInstanceState.getBoolean(MapboxConstants.STATE_DESELECT_MARKER_ON_TAP)); } @@ -828,9 +811,7 @@ public final class UiSettings { * @param scrollGesturesEnabled If true, scrolling is enabled. */ public void setScrollGesturesEnabled(boolean scrollGesturesEnabled) { - if (scrollGestureChangeAllowed) { - this.scrollGesturesEnabled = scrollGesturesEnabled; - } + this.scrollGesturesEnabled = scrollGesturesEnabled; } /** @@ -842,12 +823,105 @@ public final class UiSettings { return scrollGesturesEnabled; } - void setScrollGestureChangeAllowed(boolean scrollGestureChangeAllowed) { - this.scrollGestureChangeAllowed = scrollGestureChangeAllowed; + /** + * Returns whether scale velocity animation should execute after users finishes a gesture. + * + * @return If true, scale velocity animation is enabled. + */ + public boolean isScaleVelocityAnimationEnabled() { + return scaleVelocityAnimationEnabled; + } + + /** + * Set whether scale velocity animation should execute after users finishes a gesture. True by default. + * + * @param scaleVelocityAnimationEnabled If true, scale velocity animation will be enabled. + */ + public void setScaleVelocityAnimationEnabled(boolean scaleVelocityAnimationEnabled) { + this.scaleVelocityAnimationEnabled = scaleVelocityAnimationEnabled; + } + + /** + * Returns whether rotate velocity animation should execute after users finishes a gesture. + * + * @return If true, rotate velocity animation is enabled. + */ + public boolean isRotateVelocityAnimationEnabled() { + return rotateVelocityAnimationEnabled; + } + + /** + * Set whether rotate velocity animation should execute after users finishes a gesture. True by default. + * + * @param rotateVelocityAnimationEnabled If true, rotate velocity animation will be enabled. + */ + public void setRotateVelocityAnimationEnabled(boolean rotateVelocityAnimationEnabled) { + this.rotateVelocityAnimationEnabled = rotateVelocityAnimationEnabled; + } + + /** + * Returns whether fling velocity animation should execute after users finishes a gesture. + * + * @return If true, fling velocity animation is enabled. + */ + public boolean isFlingVelocityAnimationEnabled() { + return flingVelocityAnimationEnabled; + } + + /** + * Set whether fling velocity animation should execute after users finishes a gesture. True by default. + * + * @param flingVelocityAnimationEnabled If true, fling velocity animation will be enabled. + */ + public void setFlingVelocityAnimationEnabled(boolean flingVelocityAnimationEnabled) { + this.flingVelocityAnimationEnabled = flingVelocityAnimationEnabled; + } + + /** + * Set whether all velocity animations should execute after users finishes a gesture. + * + * @param allVelocityAnimationsEnabled If true, all velocity animations will be enabled. + */ + public void setAllVelocityAnimationsEnabled(boolean allVelocityAnimationsEnabled) { + setScaleVelocityAnimationEnabled(allVelocityAnimationsEnabled); + setRotateVelocityAnimationEnabled(allVelocityAnimationsEnabled); + setFlingVelocityAnimationEnabled(allVelocityAnimationsEnabled); + } + + /** + * Returns whether rotation threshold should be increase whenever scale is detected. + * + * @return If true, rotation threshold will be increased. + */ + public boolean isIncreaseRotateThresholdWhenScaling() { + return increaseRotateThresholdWhenScaling; + } + + /** + * Set whether rotation threshold should be increase whenever scale is detected. + * + * @param increaseRotateThresholdWhenScaling If true, rotation threshold will be increased. + */ + public void setIncreaseRotateThresholdWhenScaling(boolean increaseRotateThresholdWhenScaling) { + this.increaseRotateThresholdWhenScaling = increaseRotateThresholdWhenScaling; } - boolean isScrollGestureChangeAllowed() { - return scrollGestureChangeAllowed; + /** + * Returns whether scale threshold should be increase whenever rotation is detected. + * + * @return If true, scale threshold will be increased. + */ + public boolean isIncreaseScaleThresholdWhenRotating() { + return increaseScaleThresholdWhenRotating; + } + + /** + * set whether scale threshold should be increase whenever rotation is detected. + * + * @param increaseScaleThresholdWhenRotating If true, scale threshold will be increased. + */ + public void setIncreaseScaleThresholdWhenRotating(boolean increaseScaleThresholdWhenRotating) { + this.increaseScaleThresholdWhenRotating = increaseScaleThresholdWhenRotating; } /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java index 247ffea906..46238ee789 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java @@ -54,6 +54,17 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser { @SuppressWarnings("JavadocReference") private static final int EGL_OPENGL_ES2_BIT = 0x0004; + private boolean translucentSurface; + + public EGLConfigChooser() { + this(false); + } + + public EGLConfigChooser(boolean translucentSurface) { + super(); + this.translucentSurface = translucentSurface; + } + @Override public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { int[] configAttribs = getConfigAttributes(); @@ -274,7 +285,7 @@ public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser { EGL_RED_SIZE, 5, EGL_GREEN_SIZE, 6, EGL_BLUE_SIZE, 5, - EGL_ALPHA_SIZE, 0, + EGL_ALPHA_SIZE, translucentSurface ? 8 : 0, EGL_DEPTH_SIZE, 16, EGL_STENCIL_SIZE, 8, (emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java new file mode 100644 index 0000000000..b14b302652 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android EGL API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.egl; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java new file mode 100644 index 0000000000..aefcffef42 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android GLSurfaceView API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.glsurfaceview; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java new file mode 100644 index 0000000000..f5d8021ea1 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android Renderer API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java index dcc95217ff..ad25dea0d3 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java @@ -17,17 +17,22 @@ import javax.microedition.khronos.opengles.GL10; */ public class TextureViewMapRenderer extends MapRenderer { private TextureViewRenderThread renderThread; + private boolean translucentSurface; /** * Create a {@link MapRenderer} for the given {@link TextureView} * - * @param context the current Context - * @param textureView the TextureView + * @param context the current Context + * @param textureView the TextureView + * @param localIdeographFontFamily the local font family + * @param translucentSurface the translucency flag */ public TextureViewMapRenderer(@NonNull Context context, @NonNull TextureView textureView, - String localIdeographFontFamily) { + String localIdeographFontFamily, + boolean translucentSurface) { super(context, localIdeographFontFamily); + this.translucentSurface = translucentSurface; renderThread = new TextureViewRenderThread(textureView, this); renderThread.start(); } @@ -95,4 +100,8 @@ public class TextureViewMapRenderer extends MapRenderer { public void onDestroy() { renderThread.onDestroy(); } + + public boolean isTranslucentSurface() { + return translucentSurface; + } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java index 1e76ffe3fb..4bba160993 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java @@ -53,9 +53,10 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu */ @UiThread TextureViewRenderThread(@NonNull TextureView textureView, @NonNull TextureViewMapRenderer mapRenderer) { + textureView.setOpaque(!mapRenderer.isTranslucentSurface()); textureView.setSurfaceTextureListener(this); this.mapRenderer = mapRenderer; - this.eglHolder = new EGLHolder(new WeakReference<>(textureView)); + this.eglHolder = new EGLHolder(new WeakReference<>(textureView), mapRenderer.isTranslucentSurface()); } // SurfaceTextureListener methods @@ -324,6 +325,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu private static class EGLHolder { private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; private final WeakReference<TextureView> textureViewWeakRef; + private boolean translucentSurface; private EGL10 egl; private EGLConfig eglConfig; @@ -331,8 +333,9 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu private EGLContext eglContext = EGL10.EGL_NO_CONTEXT; private EGLSurface eglSurface = EGL10.EGL_NO_SURFACE; - EGLHolder(WeakReference<TextureView> textureViewWeakRef) { + EGLHolder(WeakReference<TextureView> textureViewWeakRef, boolean translucentSurface) { this.textureViewWeakRef = textureViewWeakRef; + this.translucentSurface = translucentSurface; } void prepare() { @@ -357,7 +360,7 @@ class TextureViewRenderThread extends Thread implements TextureView.SurfaceTextu eglConfig = null; eglContext = EGL10.EGL_NO_CONTEXT; } else if (eglContext == EGL10.EGL_NO_CONTEXT) { - eglConfig = new EGLConfigChooser().chooseConfig(egl, eglDisplay); + eglConfig = new EGLConfigChooser(translucentSurface).chooseConfig(egl, eglDisplay); int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE}; eglContext = egl.eglCreateContext(eglDisplay, eglConfig, EGL10.EGL_NO_CONTEXT, attrib_list); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java new file mode 100644 index 0000000000..d3585d41f9 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains the Mapbox Maps Android TextureView API classes. + */ +package com.mapbox.mapboxsdk.maps.renderer.textureview; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java index 1e604c9bef..fb2c70cb73 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.maps.widgets; +import android.annotation.SuppressLint; import android.content.Context; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; @@ -22,6 +23,7 @@ import com.mapbox.mapboxsdk.maps.MapboxMap; * use {@link com.mapbox.mapboxsdk.maps.UiSettings}. * </p> */ +@SuppressLint("AppCompatCustomView") public final class CompassView extends ImageView implements Runnable { public static final long TIME_WAIT_IDLE = 500; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java deleted file mode 100644 index 1cdd91028d..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationView.java +++ /dev/null @@ -1,1104 +0,0 @@ -package com.mapbox.mapboxsdk.maps.widgets; - -import android.animation.ValueAnimator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Camera; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.PointF; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.location.Location; -import android.os.Bundle; -import android.os.Parcelable; -import android.os.SystemClock; -import android.support.annotation.ColorInt; -import android.support.annotation.FloatRange; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; - -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.MyBearingTracking; -import com.mapbox.mapboxsdk.constants.MyLocationTracking; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.services.android.telemetry.location.LocationEngine; -import com.mapbox.services.android.telemetry.location.LocationEngineListener; -import com.mapbox.services.android.telemetry.location.LocationEnginePriority; - -import java.lang.ref.WeakReference; - -import timber.log.Timber; - -/** - * UI element overlaid on a map to show the user's location. - * <p> - * Use {@link MyLocationViewSettings} to manipulate the state of this view. - * </p> - * - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ -@Deprecated -public class MyLocationView extends View { - - private static final int UNDEFINED_TINT_COLOR = -1; - private MyLocationBehavior myLocationBehavior; - private MapboxMap mapboxMap; - - private Projection projection; - private float[] projectedCoordinate = new float[2]; - private float projectedX; - private float projectedY; - - private float contentPaddingX; - private float contentPaddingY; - - private LatLng latLng; - private Location location; - private LocationEngine locationEngine; - private long locationUpdateTimestamp; - private float previousDirection; - - private float accuracy; - private Paint accuracyPaint; - private float accuracyThreshold; - - private ValueAnimator locationChangeAnimator; - private ValueAnimator accuracyAnimator; - private ValueAnimator directionAnimator; - private boolean locationChangeAnimationEnabled = true; - - private ValueAnimator.AnimatorUpdateListener invalidateSelfOnUpdateListener = - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - invalidate(); - } - }; - - private Drawable foregroundDrawable; - private Drawable foregroundBearingDrawable; - private Drawable backgroundDrawable; - - private Rect foregroundBounds; - private Rect backgroundBounds; - - private int backgroundOffsetLeft; - private int backgroundOffsetTop; - private int backgroundOffsetRight; - private int backgroundOffsetBottom; - - private Matrix matrix; - private Camera camera; - private PointF screenLocation; - - // camera vars - private double tilt; - private double bearing; - private float magneticHeading; - - // Controls the compass update rate in milliseconds - private static final int COMPASS_UPDATE_RATE_MS = 500; - - @MyLocationTracking.Mode - private int myLocationTrackingMode; - - @MyBearingTracking.Mode - private int myBearingTrackingMode; - - private GpsLocationListener userLocationListener; - private CompassListener compassListener; - - public MyLocationView(Context context) { - super(context); - init(context); - } - - public MyLocationView(Context context, AttributeSet attrs) { - super(context, attrs); - init(context); - } - - public MyLocationView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(context); - } - - private void init(Context context) { - if (isInEditMode()) { - return; - } - - setEnabled(false); - - // setup LayoutParams - ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT); - setLayoutParams(lp); - - matrix = new Matrix(); - camera = new Camera(); - camera.setLocation(0, 0, -1000); - accuracyPaint = new Paint(); - - myLocationBehavior = new MyLocationBehaviorFactory().getBehavioralModel(MyLocationTracking.TRACKING_NONE); - compassListener = new CompassListener(context); - } - - @Deprecated - public void init(LocationEngine locationSource) { - this.locationEngine = locationSource; - } - - /** - * Set the foreground drawable, for internal use only. - * - * @param defaultDrawable The drawable shown when showing this view - * @param bearingDrawable The drawable shown when tracking of bearing is enabled - */ - public final void setForegroundDrawables(Drawable defaultDrawable, Drawable bearingDrawable) { - if (defaultDrawable == null) { - return; - } - - if (bearingDrawable == null) { - // if user only provided one resource - // use same for bearing mode - bearingDrawable = defaultDrawable.getConstantState().newDrawable(); - } - - if (backgroundDrawable == null) { - // if the user didn't provide a background resource we will use the foreground resource instead, - // we need to create a new drawable to handle tinting correctly - backgroundDrawable = defaultDrawable.getConstantState().newDrawable(); - } - - if (defaultDrawable.getIntrinsicWidth() != bearingDrawable.getIntrinsicWidth() - || defaultDrawable.getIntrinsicHeight() != bearingDrawable.getIntrinsicHeight()) { - throw new RuntimeException("The dimensions from location and bearing drawables should be match"); - } - - foregroundDrawable = defaultDrawable; - foregroundBearingDrawable = bearingDrawable; - - invalidateBounds(); - } - - /** - * Set the foreground drawable tint, for internal use only. - * - * @param color The color to tint the drawable with - */ - public final void setForegroundDrawableTint(@ColorInt int color) { - applyDrawableTint(foregroundDrawable, color); - applyDrawableTint(foregroundBearingDrawable, color); - invalidate(); - } - - /** - * Set the shadow drawable, for internal use only. - * - * @param drawable The drawable shown as shadow - */ - public final void setShadowDrawable(Drawable drawable) { - setShadowDrawable(drawable, 0, 0, 0, 0); - } - - /** - * Set the shadow drawable with some additional offset. - * - * @param drawable The drawable shown as shadow - * @param left The left offset margin - * @param top The top offset margin - * @param right The right offset margin - * @param bottom The bottom offset margin - */ - public final void setShadowDrawable(Drawable drawable, int left, int top, int right, int bottom) { - if (drawable != null) { - backgroundDrawable = drawable; - } - - backgroundOffsetLeft = left; - backgroundOffsetTop = top; - backgroundOffsetRight = right; - backgroundOffsetBottom = bottom; - - invalidateBounds(); - } - - /** - * Set the shadow drawable tint color, for internal use only. - * - * @param color The tint color to apply - */ - public final void setShadowDrawableTint(@ColorInt int color) { - if (backgroundDrawable == null) { - return; - } - applyDrawableTint(backgroundDrawable, color); - invalidate(); - } - - /** - * Set the accuracy tint color, for internal use only. - * - * @param color The tint color to apply - */ - public final void setAccuracyTint(@ColorInt int color) { - int alpha = accuracyPaint.getAlpha(); - accuracyPaint.setColor(color); - accuracyPaint.setAlpha(alpha); - invalidate(); - } - - /** - * Set the accuracy alpha value, for internal use only. - * - * @param alpha The alpha accuracy value to apply - */ - public final void setAccuracyAlpha(@IntRange(from = 0, to = 255) int alpha) { - accuracyPaint.setAlpha(alpha); - invalidate(); - } - - private void invalidateBounds() { - if (backgroundDrawable == null || foregroundDrawable == null || foregroundBearingDrawable == null) { - return; - } - - int backgroundWidth = backgroundDrawable.getIntrinsicWidth(); - int backgroundHeight = backgroundDrawable.getIntrinsicHeight(); - int horizontalOffset = backgroundOffsetLeft - backgroundOffsetRight; - int verticalOffset = backgroundOffsetTop - backgroundOffsetBottom; - backgroundBounds = new Rect(-backgroundWidth / 2 + horizontalOffset, - -backgroundHeight / 2 + verticalOffset, backgroundWidth / 2 + horizontalOffset, backgroundHeight / 2 - + verticalOffset); - backgroundDrawable.setBounds(backgroundBounds); - - int foregroundWidth = foregroundDrawable.getIntrinsicWidth(); - int foregroundHeight = foregroundDrawable.getIntrinsicHeight(); - foregroundBounds = new Rect(-foregroundWidth / 2, -foregroundHeight / 2, foregroundWidth / 2, foregroundHeight / 2); - foregroundDrawable.setBounds(foregroundBounds); - foregroundBearingDrawable.setBounds(foregroundBounds); - - // invoke a new draw - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - if (location == null || foregroundBounds == null || backgroundBounds == null || accuracyAnimator == null - || screenLocation == null) { - // Not ready yet - return; - } - - final PointF pointF = screenLocation; - float metersPerPixel = (float) projection.getMetersPerPixelAtLatitude(location.getLatitude()); - float accuracyPixels = (Float) accuracyAnimator.getAnimatedValue() / metersPerPixel; - - // reset - matrix.reset(); - projectedCoordinate[0] = 0; - projectedCoordinate[1] = 0; - - // put camera in position - camera.save(); - camera.rotate((float) tilt, 0, 0); - camera.getMatrix(matrix); - - if (myBearingTrackingMode != MyBearingTracking.NONE && directionAnimator != null) { - matrix.preRotate((Float) directionAnimator.getAnimatedValue()); - } - - matrix.preTranslate(0, contentPaddingY); - matrix.postTranslate(pointF.x, pointF.y - contentPaddingY); - - // concat our matrix on canvas - canvas.concat(matrix); - - // calculate focal point - matrix.mapPoints(projectedCoordinate); - projectedX = pointF.x - projectedCoordinate[0]; - projectedY = pointF.y - projectedCoordinate[1]; - - // restore orientation from camera - camera.restore(); - - // draw circle - canvas.drawCircle(0, 0, accuracyPixels, accuracyPaint); - - // draw shadow - if (backgroundDrawable != null) { - backgroundDrawable.draw(canvas); - } - - // draw foreground - if (myBearingTrackingMode == MyBearingTracking.NONE) { - if (foregroundDrawable != null) { - foregroundDrawable.draw(canvas); - } - } else if (foregroundBearingDrawable != null && foregroundBounds != null) { - if (myBearingTrackingMode == MyBearingTracking.GPS - || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING - || compassListener.isSensorAvailable()) { - foregroundBearingDrawable.draw(canvas); - } else { - // We are tracking MyBearingTracking.COMPASS, but sensor is not available. - foregroundDrawable.draw(canvas); - } - } - } - - /** - * Set the tilt value, for internal use only. - * - * @param tilt The tilt to apply - */ - public void setTilt(@FloatRange(from = 0, to = 60.0f) double tilt) { - this.tilt = tilt; - invalidate(); - } - - /** - * Set the bearing value, for internal use only. - * - * @param bearing The bearing to apply - */ - public void setBearing(double bearing) { - this.bearing = bearing; - if (myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - if (myBearingTrackingMode == MyBearingTracking.GPS - || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { - if (location != null) { - setCompass(location.getBearing() - bearing); - } - } else if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { - setCompass(magneticHeading - bearing); - } - } - } - - /** - * Set the bearing and tilt from a camera position, for internal use only. - * - * @param position The camera position to extract bearing and tilt from - */ - public void setCameraPosition(CameraPosition position) { - if (position != null) { - setBearing(position.bearing); - setTilt(position.tilt); - } - } - - /** - * Called when the hosting activity is starting, for internal use only. - */ - public void onStart() { - if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { - compassListener.onResume(); - } - if (isEnabled()) { - toggleGps(true); - } - } - - /** - * Called when the hosting activity is stopping, for internal use only. - */ - public void onStop() { - compassListener.onPause(); - toggleGps(false); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - // cleanup to prevent memory leaks - if (locationChangeAnimator != null) { - locationChangeAnimator.cancel(); - locationChangeAnimator = null; - } - - if (accuracyAnimator != null) { - accuracyAnimator.cancel(); - accuracyAnimator = null; - } - - if (directionAnimator != null) { - directionAnimator.cancel(); - directionAnimator = null; - } - - if (userLocationListener != null) { - locationEngine.removeLocationEngineListener(userLocationListener); - locationEngine = null; - userLocationListener = null; - } - } - - /** - * Update current locationstate. - */ - public void update() { - if (isEnabled()) { - myLocationBehavior.invalidate(); - } else { - setVisibility(View.INVISIBLE); - } - } - - // TODO refactor MapboxMap out - public void setMapboxMap(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - this.projection = mapboxMap.getProjection(); - } - - /** - * Set the enabled state, for internal use only. - * - * @param enabled The value to set the state to - */ - @Override - public void setEnabled(boolean enabled) { - setEnabled(enabled, false); - } - - /** - * Set the enabled state, for internal use only. - * - * @param enabled The value to set the state to - * @param isCustomLocationEngine Flag handling for handling user provided custom location engine - */ - public void setEnabled(boolean enabled, boolean isCustomLocationEngine) { - super.setEnabled(enabled); - setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); - toggleGps(enabled, isCustomLocationEngine); - } - - /** - * Save the view instance state, for internal use only. - * - * @return the marshaled representation of the view state - */ - @Override - protected Parcelable onSaveInstanceState() { - Bundle bundle = new Bundle(); - bundle.putParcelable("superState", super.onSaveInstanceState()); - bundle.putDouble("tilt", tilt); - return bundle; - } - - /** - * Restore the view instance state, for internal use only. - * - * @param state the marshalled representation of the state to restore - */ - @Override - public void onRestoreInstanceState(Parcelable state) { - if (state instanceof Bundle) { - Bundle bundle = (Bundle) state; - tilt = bundle.getDouble("tilt"); - state = bundle.getParcelable("superState"); - } - super.onRestoreInstanceState(state); - } - - private void toggleGps(boolean enableGps) { - toggleGps(enableGps, mapboxMap != null - && mapboxMap.getTrackingSettings().isCustomLocationSource()); - } - - /** - * Enabled / Disable GPS location updates along with updating the UI, for internal use only. - * - * @param enableGps true if GPS is to be enabled, false if GPS is to be disabled - */ - private void toggleGps(boolean enableGps, boolean isCustomLocationEngine) { - if (enableGps) { - if (locationEngine == null) { - if (!isCustomLocationEngine) { - locationEngine = Mapbox.getLocationEngine(); - } else { - return; - } - } - - if (userLocationListener == null) { - userLocationListener = new GpsLocationListener(this, locationEngine); - } - - locationEngine.addLocationEngineListener(userLocationListener); - locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); - locationEngine.activate(); - } else { - if (locationEngine == null) { - return; - } - // Disable location and user dot - location = null; - locationEngine.removeLocationEngineListener(userLocationListener); - locationEngine.removeLocationUpdates(); - locationEngine.deactivate(); - restoreLocationEngine(); - } - } - - /** - * Get the current location. - * - * @return the current location - */ - public Location getLocation() { - return location; - } - - /** - * Set the current location, for internal use only. - * - * @param location The current location - */ - public void setLocation(Location location) { - if (location == null) { - this.location = null; - return; - } - - this.location = location; - myLocationBehavior.updateLatLng(location); - - if (mapboxMap != null && (myBearingTrackingMode == MyBearingTracking.GPS - || myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) - && myLocationTrackingMode == MyLocationTracking.TRACKING_NONE) { - setBearing(mapboxMap.getCameraPosition().bearing); - } - } - - /** - * Set location change animation enabled, for internal use only. - * - * @param locationChangeAnimationEnabled True if location changes are animated - */ - public void setLocationChangeAnimationEnabled(boolean locationChangeAnimationEnabled) { - this.locationChangeAnimationEnabled = locationChangeAnimationEnabled; - } - - /** - * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. - * For internal use only. - * - * @param accuracyThreshold Value below which circle won't be displayed - */ - public void setAccuracyThreshold(float accuracyThreshold) { - this.accuracyThreshold = accuracyThreshold; - } - - /** - * Set the bearing tracking mode, for internal use only. - * - * @param myBearingTrackingMode The bearing tracking mode - */ - public void setMyBearingTrackingMode(@MyBearingTracking.Mode int myBearingTrackingMode) { - this.myBearingTrackingMode = myBearingTrackingMode; - if (myBearingTrackingMode == MyBearingTracking.COMPASS && compassListener.isSensorAvailable()) { - compassListener.onResume(); - } else { - compassListener.onPause(); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW - && myBearingTrackingMode == MyBearingTracking.GPS) { - // always face north - setCompass(0); - } else { - myLocationBehavior.invalidate(); - } - } - invalidate(); - } - - /** - * Set the location tracking mode, for internla use only. - * - * @param myLocationTrackingMode The location tracking mode - */ - public void setMyLocationTrackingMode(@MyLocationTracking.Mode int myLocationTrackingMode) { - MyLocationBehaviorFactory factory = new MyLocationBehaviorFactory(); - myLocationBehavior = factory.getBehavioralModel(myLocationTrackingMode); - - if (location != null) { - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // center map directly - mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(location))); - } else { - // do not use interpolated location from tracking mode - latLng = null; - } - myLocationBehavior.updateLatLng(location); - } - - this.myLocationTrackingMode = myLocationTrackingMode; - invalidate(); - } - - /** - * Get the location tracking mode, for internal use only. - * - * @return The location tracking mode - */ - @MyLocationTracking.Mode - public int getMyLocationTrackingMode() { - return myLocationTrackingMode; - } - - - /** - * Get the bearing tracking mode, for internal use only. - * - * @return the bearing tracking mode - */ - @MyBearingTracking.Mode - public int getMyBearingTrackingMode() { - return myBearingTrackingMode; - } - - /** - * Set the compass bearing value, for internal use only. - * - * @param bearing The compas bearing value - */ - private void setCompass(double bearing) { - setCompass(bearing, 0 /* no animation */); - } - - private void setCompass(double bearing, long duration) { - float oldDir = previousDirection; - if (directionAnimator != null) { - oldDir = (Float) directionAnimator.getAnimatedValue(); - directionAnimator.end(); - directionAnimator = null; - } - - float newDir = (float) bearing; - float diff = oldDir - newDir; - if (diff > 180.0f) { - newDir += 360.0f; - } else if (diff < -180.0f) { - newDir -= 360.f; - } - previousDirection = newDir; - - directionAnimator = ValueAnimator.ofFloat(oldDir, newDir); - directionAnimator.setDuration(duration); - directionAnimator.addUpdateListener(invalidateSelfOnUpdateListener); - directionAnimator.start(); - } - - /** - * Get the center of this view in screen coordinates. - * - * @return the center of the view - */ - public PointF getCenter() { - return new PointF(getCenterX(), getCenterY()); - } - - /** - * Get the x value of the center of this view. - * - * @return the x value of the center of the view - */ - private float getCenterX() { - return (getX() + getMeasuredWidth()) / 2 + contentPaddingX - projectedX; - } - - /** - * Get the y value of the center of this view. - * - * @return the y value of the center of the view - */ - private float getCenterY() { - return (getY() + getMeasuredHeight()) / 2 + contentPaddingY - projectedY; - } - - public void setContentPadding(int[] padding) { - contentPaddingX = (padding[0] - padding[2]) / 2; - contentPaddingY = (padding[1] - padding[3]) / 2; - } - - /** - * Set the location source from which location updates are received, for internal use only. - * - * @param locationEngine The location engine to receive updates from - */ - public void setLocationSource(LocationEngine locationEngine) { - toggleGps(false); - this.locationEngine = locationEngine; - this.userLocationListener = null; - setEnabled(isEnabled(), locationEngine != null); - } - - private void applyDrawableTint(Drawable drawable, @ColorInt int color) { - if (color == UNDEFINED_TINT_COLOR) { - removeTintColorFilter(drawable); - } else { - applyTintColorFilter(drawable, color); - } - } - - private void removeTintColorFilter(Drawable drawable) { - if (drawable != null) { - drawable.mutate().setColorFilter(null); - } - } - - private void applyTintColorFilter(Drawable drawable, @ColorInt int color) { - if (drawable != null) { - drawable.mutate().setColorFilter(color, PorterDuff.Mode.SRC_IN); - } - } - - private void restoreLocationEngine() { - locationEngine.setPriority(LocationEnginePriority.LOW_POWER); - locationEngine.activate(); - } - - private static class GpsLocationListener implements LocationEngineListener { - - private WeakReference<MyLocationView> userLocationView; - private WeakReference<LocationEngine> locationSource; - - GpsLocationListener(MyLocationView myLocationView, LocationEngine locationEngine) { - userLocationView = new WeakReference<>(myLocationView); - locationSource = new WeakReference<>(locationEngine); - } - - @SuppressLint("MissingPermission") - @Override - public void onConnected() { - MyLocationView locationView = userLocationView.get(); - LocationEngine locationEngine = locationSource.get(); - if (locationView != null && locationEngine != null) { - Location lastKnownLocation = locationEngine.getLastLocation(); - if (lastKnownLocation != null) { - locationView.setLocation(lastKnownLocation); - } - locationEngine.requestLocationUpdates(); - } - } - - /** - * Callback method for receiving location updates from LocationServices. - * - * @param location The new Location data - */ - @Override - public void onLocationChanged(Location location) { - MyLocationView locationView = userLocationView.get(); - if (locationView != null) { - locationView.setLocation(location); - } - } - } - - private class CompassListener implements SensorEventListener { - - private final SensorManager sensorManager; - - private Sensor rotationVectorSensor; - private float[] matrix = new float[9]; - private float[] rotationVectorValue; - private float[] truncatedRotationVectorValue = new float[4]; - - private float[] orientation = new float[3]; - private boolean reportMissingSensor = true; - // Compass data - private long compassUpdateNextTimestamp = 0; - - CompassListener(Context context) { - sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); - } - - public void onResume() { - sensorManager.registerListener(this, rotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); - } - - public void onPause() { - sensorManager.unregisterListener(this, rotationVectorSensor); - } - - public boolean isSensorAvailable() { - if (rotationVectorSensor == null && reportMissingSensor) { - reportMissingSensor = false; - Timber.e("Sensor.TYPE_ROTATION_VECTOR is missing from this device. Unable to use MyBearingTracking.COMPASS."); - } - return rotationVectorSensor != null; - } - - @Override - public void onSensorChanged(SensorEvent event) { - - // check when the last time the compass was updated, return if too soon. - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime < compassUpdateNextTimestamp) { - return; - } - - if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - rotationVectorValue = getRotationVectorFromSensorEvent(event); - SensorManager.getRotationMatrixFromVector(matrix, rotationVectorValue); - SensorManager.getOrientation(matrix, orientation); - - magneticHeading = (float) Math.toDegrees(SensorManager.getOrientation(matrix, orientation)[0]); - if (myLocationTrackingMode == MyLocationTracking.TRACKING_FOLLOW) { - // Change the user location view orientation to reflect the device orientation - rotateCamera(magneticHeading); - setCompass(0, COMPASS_UPDATE_RATE_MS); - } else { - // Change compass direction - setCompass(magneticHeading - bearing, COMPASS_UPDATE_RATE_MS); - } - - compassUpdateNextTimestamp = currentTime + COMPASS_UPDATE_RATE_MS; - } - } - - /** - * Pulls out the rotation vector from a SensorEvent, with a maximum length - * vector of four elements to avoid potential compatibility issues. - * - * @param event the sensor event - * @return the events rotation vector, potentially truncated - */ - @NonNull - float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { - if (event.values.length > 4) { - // On some Samsung devices SensorManager.getRotationMatrixFromVector - // appears to throw an exception if rotation vector has length > 4. - // For the purposes of this class the first 4 values of the - // rotation vector are sufficient (see crbug.com/335298 for details). - // Only affects Android 4.3 - System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4); - return truncatedRotationVectorValue; - } else { - return event.values; - } - } - - private void rotateCamera(float rotation) { - CameraPosition.Builder builder = new CameraPosition.Builder(); - builder.bearing(rotation); - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), COMPASS_UPDATE_RATE_MS, - false /*linear interpolator*/, null); - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - } - - } - - private class MarkerCoordinateAnimatorListener implements ValueAnimator.AnimatorUpdateListener { - - private MyLocationBehavior behavior; - private double fromLat; - private double fromLng; - private double toLat; - private double toLng; - - private MarkerCoordinateAnimatorListener(MyLocationBehavior myLocationBehavior, LatLng from, LatLng to) { - behavior = myLocationBehavior; - fromLat = from.getLatitude(); - fromLng = from.getLongitude(); - toLat = to.getLatitude(); - toLng = to.getLongitude(); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - float frac = animation.getAnimatedFraction(); - double latitude = fromLat + (toLat - fromLat) * frac; - double longitude = fromLng + (toLng - fromLng) * frac; - behavior.updateLatLng(latitude, longitude); - update(); - } - } - - private class MyLocationBehaviorFactory { - - MyLocationBehavior getBehavioralModel(@MyLocationTracking.Mode int mode) { - if (mode == MyLocationTracking.TRACKING_NONE) { - return new MyLocationShowBehavior(); - } else { - return new MyLocationTrackingBehavior(); - } - } - } - - private abstract class MyLocationBehavior { - - MyLocationBehavior() { - if (latLng != null) { - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - } - - void updateLatLng(@NonNull Location newLocation) { - location = newLocation; - } - - void updateLatLng(double lat, double lon) { - if (latLng != null) { - latLng.setLatitude(lat); - latLng.setLongitude(lon); - } - } - - void updateAccuracy(@NonNull Location location) { - if (accuracyAnimator != null && accuracyAnimator.isRunning()) { - // use current accuracy as a starting point - accuracy = (Float) accuracyAnimator.getAnimatedValue(); - accuracyAnimator.end(); - } - - float newAccuracy = location.getAccuracy() >= accuracyThreshold ? location.getAccuracy() : 0f; - accuracyAnimator = ValueAnimator.ofFloat(accuracy, newAccuracy); - accuracyAnimator.setDuration(750); - accuracyAnimator.start(); - accuracy = newAccuracy; - } - - abstract void invalidate(); - } - - private class MyLocationTrackingBehavior extends MyLocationBehavior { - - @Override - void updateLatLng(@NonNull Location location) { - super.updateLatLng(location); - if (latLng == null) { - // first location fix - latLng = new LatLng(location); - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - - // updateLatLng timestamp - float previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - - // calculate animation duration - int animationDuration; - if (previousUpdateTimeStamp == 0) { - animationDuration = 0; - } else { - animationDuration = (int) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.1f) - /*make animation slightly longer*/; - } - - // calculate interpolated location - latLng = new LatLng(location); - CameraPosition.Builder builder = new CameraPosition.Builder().target(latLng); - - // add direction - if (myBearingTrackingMode == MyBearingTracking.GPS) { - if (location.hasBearing()) { - builder.bearing(location.getBearing()); - } - setCompass(0, COMPASS_UPDATE_RATE_MS); - } - - if (myBearingTrackingMode == MyBearingTracking.GPS_NORTH_FACING) { - builder.bearing(0); - if (location.hasBearing()) { - setCompass(location.getBearing(), COMPASS_UPDATE_RATE_MS); - } - } - - // accuracy - updateAccuracy(location); - - if (locationChangeAnimationEnabled && animationDuration > 0) { - // ease to new camera position with a linear interpolator - mapboxMap.easeCamera(CameraUpdateFactory.newCameraPosition(builder.build()), animationDuration, false, null, - true); - } else { - mapboxMap.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build())); - } - } - - @Override - void invalidate() { - int[] mapPadding = mapboxMap.getPadding(); - float x = (getWidth() + mapPadding[0] - mapPadding[2]) / 2 + contentPaddingX; - float y = (getHeight() - mapPadding[3] + mapPadding[1]) / 2 + contentPaddingY; - screenLocation = new PointF(x, y); - MyLocationView.this.invalidate(); - } - } - - private class MyLocationShowBehavior extends MyLocationBehavior { - - @Override - void updateLatLng(@NonNull final Location location) { - super.updateLatLng(location); - if (latLng == null) { - // first location update - latLng = new LatLng(location); - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - } - - // update LatLng location - LatLng newLocation = new LatLng(location); - - // update LatLng accuracy - updateAccuracy(location); - - // calculate updateLatLng time + add some extra offset to improve animation - long previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - long locationUpdateDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * 1.2f); - - // animate changes - if (locationChangeAnimator != null) { - locationChangeAnimator.end(); - locationChangeAnimator = null; - } - - locationChangeAnimator = ValueAnimator.ofFloat(0.0f, 1.0f); - if (locationChangeAnimationEnabled) { - locationChangeAnimator.setDuration(locationUpdateDuration); - } else { - locationChangeAnimator.setDuration(0); - } - locationChangeAnimator.addUpdateListener(new MarkerCoordinateAnimatorListener(this, - latLng, newLocation - )); - locationChangeAnimator.start(); - latLng = newLocation; - } - - @Override - void invalidate() { - if (latLng != null) { - screenLocation = projection.toScreenLocation(latLng); - } - MyLocationView.this.invalidate(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java deleted file mode 100644 index ec7c53e1d0..0000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/MyLocationViewSettings.java +++ /dev/null @@ -1,389 +0,0 @@ -package com.mapbox.mapboxsdk.maps.widgets; - -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.constants.MyLocationTracking; -import com.mapbox.mapboxsdk.maps.FocalPointChangeListener; -import com.mapbox.mapboxsdk.maps.MapboxMapOptions; -import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -/** - * Settings to configure the visual appearance of the MyLocationView. - * - * @deprecated use location layer plugin from - * https://github.com/mapbox/mapbox-plugins-android/tree/master/plugins/locationlayer instead. - */ -@Deprecated -public class MyLocationViewSettings { - - private Projection projection; - private MyLocationView myLocationView; - private FocalPointChangeListener focalPointChangeListener; - - // - // State - // - - private boolean enabled; - - // - // Foreground - // - - private Drawable foregroundDrawable; - private Drawable foregroundBearingDrawable; - - @ColorInt - private int foregroundTintColor; - - // - // Background - // - - private Drawable backgroundDrawable; - private int[] backgroundOffset = new int[4]; - - @ColorInt - private int backgroundTintColor; - - // - // Accuracy - // - - private int accuracyAlpha; - private float accuracyThreshold = 0f; - - @ColorInt - private int accuracyTintColor; - - // - // Padding - // - - private int[] padding = new int[4]; - - /** - * Creates an instance of MyLocationViewSettings - * <p> - * - * @param myLocationView the MyLocationView to apply the settings to - * @param projection the MapView projection - * @param focalPointChangedListener the interface to be invoked when focal points changes - * @see MyLocationView - */ - public MyLocationViewSettings(MyLocationView myLocationView, Projection projection, FocalPointChangeListener - focalPointChangedListener) { - this.myLocationView = myLocationView; - this.projection = projection; - this.focalPointChangeListener = focalPointChangedListener; - } - - /** - * Initialise this with MapboxMapOptions. - * - * @param options the options to initialise this class from - */ - public void initialise(@NonNull MapboxMapOptions options) { - CameraPosition position = options.getCamera(); - if (position != null && !position.equals(CameraPosition.DEFAULT)) { - setTilt(position.tilt); - } - setForegroundDrawable(options.getMyLocationForegroundDrawable(), options.getMyLocationForegroundBearingDrawable()); - setForegroundTintColor(options.getMyLocationForegroundTintColor()); - setBackgroundDrawable(options.getMyLocationBackgroundDrawable(), options.getMyLocationBackgroundPadding()); - setBackgroundTintColor(options.getMyLocationBackgroundTintColor()); - setAccuracyAlpha(options.getMyLocationAccuracyAlpha()); - setAccuracyTintColor(options.getMyLocationAccuracyTintColor()); - setAccuracyThreshold(options.getMyLocationAccuracyThreshold()); - } - - public void onSaveInstanceState(Bundle outState) { - outState.putBoolean(MapboxConstants.STATE_LOCATION_VIEW_ENABLED, isEnabled()); - outState.putByteArray( - MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_DRAWABLE, - BitmapUtils.getByteArrayFromDrawable(getForegroundDrawable()) - ); - outState.putByteArray( - MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_BEARING_DRAWABLE, - BitmapUtils.getByteArrayFromDrawable(getForegroundBearingDrawable()) - ); - outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_TINT_COLOR, getForegroundTintColor()); - outState.putByteArray( - MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_DRAWABLE, - BitmapUtils.getByteArrayFromDrawable(getBackgroundDrawable()) - ); - outState.putIntArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_OFFSET, getBackgroundOffset()); - outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_TINT_COLOR, getBackgroundTintColor()); - outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_ALPHA, getAccuracyAlpha()); - outState.putInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_TINT_COLOR, getAccuracyTintColor()); - outState.putFloat(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_THRESHOLD, getAccuracyThreshold()); - outState.putIntArray(MapboxConstants.STATE_LOCATION_VIEW_PADDING, getPadding()); - } - - public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - setEnabled(savedInstanceState.getBoolean(MapboxConstants.STATE_LOCATION_VIEW_ENABLED)); - setForegroundDrawable( - BitmapUtils.getDrawableFromByteArray( - myLocationView.getContext(), - savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_DRAWABLE) - ), - BitmapUtils.getDrawableFromByteArray( - myLocationView.getContext(), - savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_BEARING_DRAWABLE) - ) - ); - setForegroundTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_FOREGROUND_TINT_COLOR)); - setBackgroundDrawable( - BitmapUtils.getDrawableFromByteArray( - myLocationView.getContext(), - savedInstanceState.getByteArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_DRAWABLE) - ), - savedInstanceState.getIntArray(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_OFFSET) - ); - setBackgroundTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_BACKGROUND_TINT_COLOR)); - setAccuracyAlpha(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_ALPHA)); - setAccuracyTintColor(savedInstanceState.getInt(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_TINT_COLOR)); - setAccuracyThreshold(savedInstanceState.getFloat(MapboxConstants.STATE_LOCATION_VIEW_ACCURACY_THRESHOLD)); - setPadding(savedInstanceState.getIntArray(MapboxConstants.STATE_LOCATION_VIEW_PADDING)); - } - - /** - * Returns if the MyLocationView is enabled - * - * @return true if MyLocationView is enabled, - */ - public boolean isEnabled() { - return enabled; - } - - /** - * Set the enabled state of MyLocationView - * - * @param enabled true shows the MyLocationView on the map - */ - public void setEnabled(boolean enabled) { - this.enabled = enabled; - myLocationView.setEnabled(enabled); - } - - /** - * Set the foreground drawable of the MyLocationView - * <p> - * The foreground drawable is the image visible on screen - * </p> - * It's linked with the foreground tint color - * - * @param foregroundDrawable the drawable to show as foreground without bearing - * @param foregroundBearingDrawable the drawable to show as foreground when bearing is enabled - */ - public void setForegroundDrawable(Drawable foregroundDrawable, Drawable foregroundBearingDrawable) { - this.foregroundDrawable = foregroundDrawable; - this.foregroundBearingDrawable = foregroundBearingDrawable; - myLocationView.setForegroundDrawables(foregroundDrawable, foregroundBearingDrawable); - myLocationView.setForegroundDrawableTint(foregroundTintColor); - } - - /** - * Get the foreground drawable when bearing is disabled. - * - * @return the drawable used as foreground - */ - public Drawable getForegroundDrawable() { - return foregroundDrawable; - } - - /** - * Get the foreground drawable when bearing is enabled. - * - * @return the bearing drawable used as foreground - */ - public Drawable getForegroundBearingDrawable() { - return foregroundBearingDrawable; - } - - /** - * Set the foreground tint color. - * <p> - * The color will tint both the foreground and the bearing foreground drawable. - * </p> - * - * @param foregroundTintColor the color to tint the foreground drawable or -1 (undefined color) to remove the - * existing foreground tint color - */ - public void setForegroundTintColor(@ColorInt int foregroundTintColor) { - this.foregroundTintColor = foregroundTintColor; - myLocationView.setForegroundDrawableTint(foregroundTintColor); - } - - /** - * Get the foreground tint color. - * - * @return the foreground tint color - */ - public int getForegroundTintColor() { - return foregroundTintColor; - } - - /** - * Set the background drawable of MyLocationView - * <p> - * Padding can be added to provide an offset to the background - * </p> - * It's linked with the background tint color - * - * @param backgroundDrawable the drawable to show as background - * @param padding the padding added to the background - */ - public void setBackgroundDrawable(Drawable backgroundDrawable, int[] padding) { - this.backgroundDrawable = backgroundDrawable; - this.backgroundOffset = padding; - if (padding != null && padding.length == 4) { - myLocationView.setShadowDrawable(backgroundDrawable, padding[0], padding[1], padding[2], padding[3]); - } else { - myLocationView.setShadowDrawable(backgroundDrawable); - } - myLocationView.setShadowDrawableTint(backgroundTintColor); - } - - /** - * Get the background drawable of MyLocationView. - * - * @return the drawable used as background - */ - public Drawable getBackgroundDrawable() { - return backgroundDrawable; - } - - /** - * Set the background tint color. - * - * @param backgroundTintColor the color to tint the background drawable or -1 (undefined color) to remove the - * existing background tint color - */ - public void setBackgroundTintColor(@ColorInt int backgroundTintColor) { - this.backgroundTintColor = backgroundTintColor; - myLocationView.setShadowDrawableTint(backgroundTintColor); - } - - /** - * Get the background tint color. - * - * @return the background tint color - */ - public int getBackgroundTintColor() { - return backgroundTintColor; - } - - /** - * Get the background offset. - * - * @return the background offset - */ - public int[] getBackgroundOffset() { - return backgroundOffset; - } - - /** - * Set the MyLocationView padding. - * - * @param left the padding left of MyLocationView - * @param top the padding top of MyLocationView - * @param right the padding right of MyLocationView - * @param bottom the padding bottom of MyLocaionView - */ - public void setPadding(int left, int top, int right, int bottom) { - padding = new int[] {left, top, right, bottom}; - setPadding(padding); - } - - private void setPadding(int[] padding) { - myLocationView.setContentPadding(padding); - projection.invalidateContentPadding(padding); - invalidateFocalPointForTracking(myLocationView); - } - - /** - * Get the MyLocationView padding. - * - * @return an array describing the padding in a LTRB manner - */ - public int[] getPadding() { - return padding; - } - - /** - * Get the alpha value of the accuracy circle of MyLocationView - * - * @return the alpha value - */ - public int getAccuracyAlpha() { - return accuracyAlpha; - } - - /** - * Set the alpha value of the accuracy circle of MyLocationView - * - * @param accuracyAlpha the alpha value to set - */ - public void setAccuracyAlpha(@IntRange(from = 0, to = 255) int accuracyAlpha) { - this.accuracyAlpha = accuracyAlpha; - myLocationView.setAccuracyAlpha(accuracyAlpha); - } - - /** - * Get the accuracy tint color of MyLocationView. - * - * @return the tint color used for accuracy - */ - public int getAccuracyTintColor() { - return accuracyTintColor; - } - - /** - * Set the accuracy tint color of MyLocationView. - * - * @param accuracyTintColor the accuracy tint color - */ - public void setAccuracyTintColor(@ColorInt int accuracyTintColor) { - this.accuracyTintColor = accuracyTintColor; - myLocationView.setAccuracyTint(accuracyTintColor); - } - - /** - * Returns current accuracy threshold value (in meters). - * - * @return Value of accuracy threshold (in meters), below which circle won't be displayed - */ - public float getAccuracyThreshold() { - return accuracyThreshold; - } - - /** - * Set accuracy circle threshold. Circle won't be displayed if accuracy is below set value. - * - * @param accuracyThreshold Value of accuracy (in meters), below which circle won't be displayed - */ - public void setAccuracyThreshold(float accuracyThreshold) { - this.accuracyThreshold = accuracyThreshold; - myLocationView.setAccuracyThreshold(accuracyThreshold); - } - - public void setTilt(double tilt) { - myLocationView.setTilt(tilt); - } - - private void invalidateFocalPointForTracking(MyLocationView myLocationView) { - if (!(myLocationView.getMyLocationTrackingMode() == MyLocationTracking.TRACKING_NONE)) { - focalPointChangeListener.onFocalPointChanged(myLocationView.getCenter()); - } else { - focalPointChangeListener.onFocalPointChanged(null); - } - } -} |