summaryrefslogtreecommitdiff
path: root/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java15
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java48
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java27
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java320
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java86
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java13
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java30
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PulsingLocationCircleAnimator.java42
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/PulseMode.java50
11 files changed, 641 insertions, 8 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java
index cac513c2f9..1ff3583b5c 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java
@@ -27,6 +27,7 @@ import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PULSING_CIRCLE_LAYER;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER;
import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
@@ -108,4 +109,18 @@ class LayerSourceProvider {
circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP)
);
}
+
+ /**
+ * Adds a {@link CircleLayer} to the map to support the {@link LocationComponent} pulsing UI functionality.
+ *
+ * @return a {@link CircleLayer} with the correct data-driven styling. Tilting the map will keep the pulsing
+ * layer aligned with the map plane.
+ */
+ @NonNull
+ Layer generatePulsingCircleLayer() {
+ return new CircleLayer(PULSING_CIRCLE_LAYER, LOCATION_SOURCE)
+ .withProperties(
+ circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP)
+ );
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
index 5aad038a28..a522fbaaa1 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java
@@ -30,6 +30,7 @@ import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURA
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING;
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING;
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG;
+import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_PULSING_CIRCLE;
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT;
import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM;
import static com.mapbox.mapboxsdk.location.Utils.immediateAnimation;
@@ -51,6 +52,7 @@ final class LocationAnimatorCoordinator {
private final MapboxAnimatorSetProvider animatorSetProvider;
private boolean compassAnimationEnabled;
private boolean accuracyAnimationEnabled;
+ private LocationComponentOptions locationComponentOptions;
@VisibleForTesting
int maxAnimationFps = Integer.MAX_VALUE;
@@ -59,10 +61,12 @@ final class LocationAnimatorCoordinator {
final SparseArray<MapboxAnimator.AnimationsValueChangeListener> listeners = new SparseArray<>();
LocationAnimatorCoordinator(@NonNull Projection projection, @NonNull MapboxAnimatorSetProvider animatorSetProvider,
- @NonNull MapboxAnimatorProvider animatorProvider) {
+ @NonNull MapboxAnimatorProvider animatorProvider,
+ @NonNull LocationComponentOptions locationComponentOptions) {
this.projection = projection;
this.animatorProvider = animatorProvider;
this.animatorSetProvider = animatorSetProvider;
+ this.locationComponentOptions = locationComponentOptions;
}
void updateAnimatorListenerHolders(@NonNull Set<AnimatorListenerHolder> listenerHolders) {
@@ -150,6 +154,29 @@ final class LocationAnimatorCoordinator {
this.previousAccuracyRadius = targetAccuracyRadius;
}
+ /**
+ * Initializes the {@link PulsingLocationCircleAnimator}, which is a type of {@link MapboxAnimator}.
+ * This method also adds the animator to this class' animator array.
+ *
+ * @param options the {@link LocationComponentOptions} passed to this class upstream from the
+ * {@link LocationComponent}.
+ */
+ void startLocationComponentCirclePulsing(LocationComponentOptions options) {
+ cancelAnimator(ANIMATOR_PULSING_CIRCLE);
+ MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(ANIMATOR_PULSING_CIRCLE);
+ locationComponentOptions = options;
+ PulsingLocationCircleAnimator pulsingLocationCircleAnimator = animatorProvider.pulsingCircleAnimator(
+ listener,
+ maxAnimationFps,
+ locationComponentOptions.pulseSingleDuration(),
+ locationComponentOptions.pulseMaxRadius(),
+ locationComponentOptions.pulseInterpolator());
+ if (listener != null) {
+ animatorArray.put(ANIMATOR_PULSING_CIRCLE, pulsingLocationCircleAnimator);
+ }
+ playPulsingAnimator();
+ }
+
void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration,
@Nullable MapboxMap.CancelableCallback callback) {
updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback);
@@ -292,6 +319,18 @@ final class LocationAnimatorCoordinator {
animatorSetProvider.startAnimation(animators, new LinearInterpolator(), duration);
}
+ /**
+ * Starts the {@link PulsingLocationCircleAnimator} in the animator array. This method is separate
+ * from {@link #playAnimators(long, int...)} because the MapboxAnimatorSetProvider has many more
+ * customizable animation parameters than the other {@link MapboxAnimator}s.
+ */
+ private void playPulsingAnimator() {
+ Animator animator = animatorArray.get(ANIMATOR_PULSING_CIRCLE);
+ if (animator != null) {
+ animatorSetProvider.startSingleAnimation(animator);
+ }
+ }
+
void resetAllCameraAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
resetCameraCompassAnimation(currentCameraPosition);
boolean snap = resetCameraLocationAnimations(currentCameraPosition, isGpsNorth);
@@ -383,6 +422,13 @@ final class LocationAnimatorCoordinator {
cancelAnimator(ANIMATOR_TILT);
}
+ /**
+ * Cancel's the pulsing circle location animator.
+ */
+ void cancelPulsingCircleAnimation() {
+ cancelAnimator(ANIMATOR_PULSING_CIRCLE);
+ }
+
void cancelAllAnimations() {
for (int i = 0; i < animatorArray.size(); i++) {
@MapboxAnimator.Type int animatorType = animatorArray.keyAt(i);
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
index 8b014b0e9c..5bd2268e43 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java
@@ -710,17 +710,26 @@ public final class LocationComponent {
LocationComponent.this.options = options;
if (mapboxMap.getStyle() != null) {
locationLayerController.applyStyle(options);
- locationCameraController.initializeOptions(options);
staleStateManager.setEnabled(options.enableStaleState());
staleStateManager.setDelayTime(options.staleStateTimeout());
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options.trackingAnimationDurationMultiplier());
locationAnimatorCoordinator.setCompassAnimationEnabled(options.compassAnimationEnabled());
locationAnimatorCoordinator.setAccuracyAnimationEnabled(options.accuracyAnimationEnabled());
+ if (options.pulseEnabled()) {
+ startPulsingLocationCircle();
+ }
updateMapWithOptions(options);
}
}
/**
+ * Starts the LocationComponent's pulsing circle UI.
+ */
+ public void startPulsingLocationCircle() {
+ locationAnimatorCoordinator.startLocationComponentCirclePulsing(options);
+ }
+
+ /**
* Zooms to the desired zoom level.
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
* If you are not using any of {@link CameraMode} modes,
@@ -1177,6 +1186,14 @@ public final class LocationComponent {
}
}
+ /**
+ * Available to cancel the specific pulsing circle animation.
+ */
+ public void cancelPulsingLocationCircle() {
+ locationAnimatorCoordinator.cancelPulsingCircleAnimation();
+ locationLayerController.adjustPulsingCircleLayerVisibility(false);
+ }
+
@SuppressLint("MissingPermission")
private void onLocationLayerStart() {
if (!isComponentInitialized || !isComponentStarted || mapboxMap.getStyle() == null) {
@@ -1202,6 +1219,9 @@ public final class LocationComponent {
}
}
setCameraMode(locationCameraController.getCameraMode());
+ if (options.pulseEnabled()) {
+ startPulsingLocationCircle();
+ }
setLastLocation();
updateCompassListenerState(true);
setLastCompassHeading();
@@ -1254,7 +1274,7 @@ public final class LocationComponent {
locationAnimatorCoordinator = new LocationAnimatorCoordinator(
mapboxMap.getProjection(),
MapboxAnimatorSetProvider.getInstance(),
- MapboxAnimatorProvider.getInstance()
+ MapboxAnimatorProvider.getInstance(), options
);
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options
.trackingAnimationDurationMultiplier());
@@ -1369,6 +1389,9 @@ public final class LocationComponent {
boolean isLocationLayerHidden = locationLayerController.isHidden();
if (isEnabled && isComponentStarted && isLocationLayerHidden) {
locationLayerController.show();
+ if (options.pulseEnabled()) {
+ locationLayerController.adjustPulsingCircleLayerVisibility(true);
+ }
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java
index f2158584c7..baba5eaca4 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java
@@ -52,6 +52,8 @@ public final class LocationComponentConstants {
static final String PROPERTY_FOREGROUND_STALE_ICON = "mapbox-property-foreground-stale-icon";
static final String PROPERTY_BACKGROUND_STALE_ICON = "mapbox-property-background-stale-icon";
static final String PROPERTY_BEARING_ICON = "mapbox-property-shadow-icon";
+ static final String PROPERTY_PULSING_RADIUS = "mapbox-property-pulsing-circle-radius";
+ static final String PROPERTY_PULSING_OPACITY = "mapbox-property-pulsing-circle-opacity";
// Layers
@@ -80,6 +82,11 @@ public final class LocationComponentConstants {
*/
public static final String BEARING_LAYER = "mapbox-location-bearing-layer";
+ /**
+ * Layer ID of the location pulsing circle.
+ */
+ public static final String PULSING_CIRCLE_LAYER = "mapbox-location-pulsing-circle-layer";
+
// Icons
static final String FOREGROUND_ICON = "mapbox-location-icon";
static final String BACKGROUND_ICON = "mapbox-location-stroke-icon";
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
index 48c89622cd..dfadd9f0ae 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java
@@ -14,6 +14,7 @@ import android.support.annotation.StyleRes;
import com.mapbox.android.gestures.AndroidGesturesManager;
import com.mapbox.mapboxsdk.R;
+import com.mapbox.mapboxsdk.location.modes.PulseMode;
import com.mapbox.mapboxsdk.style.layers.Layer;
import java.util.Arrays;
@@ -69,6 +70,21 @@ public class LocationComponentOptions implements Parcelable {
*/
private static final float TRACKING_ANIMATION_DURATION_MULTIPLIER_DEFAULT = 1.1f;
+ /**
+ * Default duration of a single LocationComponent circle pulse.
+ */
+ private static final long CIRCLE_PULSING_DURATION_DEFAULT_MS = 2300;
+
+ /**
+ * Default opacity of the LocationComponent circle when it ends a single pulse.
+ */
+ private static final float CIRCLE_PULSING_ALPHA_DEFAULT = 1f;
+
+ /**
+ * Default maximum radius of the LocationComponent circle when it's pulsing.
+ */
+ public static final float CIRCLE_PULSING_MAX_RADIUS_DEFAULT = 35f;
+
private float accuracyAlpha;
private int accuracyColor;
private int backgroundDrawableStale;
@@ -114,6 +130,13 @@ public class LocationComponentOptions implements Parcelable {
private float trackingAnimationDurationMultiplier;
private boolean compassAnimationEnabled;
private boolean accuracyAnimationEnabled;
+ private Boolean pulsingCircleEnabled;
+ private Boolean pulsingCircleFadeEnabled;
+ private Integer pulseColor;
+ private float pulseSingleDuration;
+ private float pulsingCircleMaxRadius;
+ private float pulseAlpha;
+ private String pulseInterpolator;
public LocationComponentOptions(
float accuracyAlpha,
@@ -148,7 +171,14 @@ public class LocationComponentOptions implements Parcelable {
String layerBelow,
float trackingAnimationDurationMultiplier,
boolean compassAnimationEnabled,
- boolean accuracyAnimationEnabled) {
+ boolean accuracyAnimationEnabled,
+ Boolean pulsingCircleEnabled,
+ Boolean pulsingCircleFadeEnabled,
+ Integer pulsingCircleColor,
+ float pulsingCircleDuration,
+ float pulsingCircleMaxRadius,
+ float pulsingCircleAlpha,
+ String pulsingCircleInterpolator) {
this.accuracyAlpha = accuracyAlpha;
this.accuracyColor = accuracyColor;
this.backgroundDrawableStale = backgroundDrawableStale;
@@ -185,6 +215,13 @@ public class LocationComponentOptions implements Parcelable {
this.trackingAnimationDurationMultiplier = trackingAnimationDurationMultiplier;
this.compassAnimationEnabled = compassAnimationEnabled;
this.accuracyAnimationEnabled = accuracyAnimationEnabled;
+ this.pulsingCircleEnabled = pulsingCircleEnabled;
+ this.pulsingCircleFadeEnabled = pulsingCircleFadeEnabled;
+ this.pulseColor = pulsingCircleColor;
+ this.pulseSingleDuration = pulsingCircleDuration;
+ this.pulsingCircleMaxRadius = pulsingCircleMaxRadius;
+ this.pulseAlpha = pulsingCircleAlpha;
+ this.pulseInterpolator = pulsingCircleInterpolator;
}
/**
@@ -302,6 +339,35 @@ public class LocationComponentOptions implements Parcelable {
R.styleable.mapbox_LocationComponent_mapbox_accuracyAnimationEnabled, true
);
+ builder.pulsingCircleEnabled = typedArray.getBoolean(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleEnabled, false
+ );
+
+ builder.pulsingCircleFadeEnabled = typedArray.getBoolean(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleFadeEnabled, true
+ );
+
+ if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleColor)) {
+ builder.pulsingCircleColor(typedArray.getColor(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleColor,
+ -1));
+ }
+
+ builder.pulsingCircleDuration = typedArray.getFloat(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleDuration, CIRCLE_PULSING_DURATION_DEFAULT_MS
+ );
+
+ builder.pulsingCircleMaxRadius = typedArray.getFloat(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleRadius, CIRCLE_PULSING_MAX_RADIUS_DEFAULT
+ );
+
+ builder.pulsingCircleAlpha = typedArray.getFloat(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleAlpha, CIRCLE_PULSING_ALPHA_DEFAULT
+ );
+
+ builder.pulsingCircleInterpolator = typedArray.getString(
+ R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleInterpolator);
+
typedArray.recycle();
return builder.build();
@@ -740,6 +806,70 @@ public class LocationComponentOptions implements Parcelable {
return accuracyAnimationEnabled;
}
+ /**
+ * Enable or disable the LocationComponent's pulsing circle.
+ *
+ * @return whether the LocationComponent's pulsing circle is enabled
+ */
+ public Boolean pulseEnabled() {
+ return pulsingCircleEnabled;
+ }
+
+ /**
+ * Enable or disable fading of the LocationComponent's pulsing circle. If it fades, the circle's
+ * opacity decreases as its radius increases.
+ *
+ * @return whether fading of the LocationComponent's pulsing circle is enabled
+ */
+ public Boolean pulsingCircleFadeEnabled() {
+ return pulsingCircleFadeEnabled;
+ }
+
+ /**
+ * Color of the LocationComponent's pulsing circle as it pulses.
+ *
+ * @return the current set color of the circle
+ */
+ public Integer pulseColor() {
+ return pulseColor;
+ }
+
+ /**
+ * The number of milliseconds it takes for a single pulse of the LocationComponent's pulsing circle.
+ *
+ * @return the current set length of time for a single pulse
+ */
+ public float pulseSingleDuration() {
+ return pulseSingleDuration;
+ }
+
+ /**
+ * The maximum radius that a single pulse should expand the LocationComponent's pulsing circle to.
+ *
+ * @return the maximum radius that the pulsing circle will expand to.
+ */
+ public float pulseMaxRadius() {
+ return pulsingCircleMaxRadius;
+ }
+
+ /**
+ * The opacity of the LocationComponent's circle as it pulses.
+ *
+ * @return the current set opacity of the LocationComponent's circle
+ */
+ public float pulseAlpha() {
+ return pulseAlpha;
+ }
+
+ /**
+ * The interpolator type of animation for the movement of the LocationComponent's circle
+ *
+ * @return the current set type of animation interpolator for the pulsing circle
+ */
+ public String pulseInterpolator() {
+ return pulseInterpolator;
+ }
+
@NonNull
@Override
public String toString() {
@@ -775,6 +905,13 @@ public class LocationComponentOptions implements Parcelable {
+ "layerAbove=" + layerAbove
+ "layerBelow=" + layerBelow
+ "trackingAnimationDurationMultiplier=" + trackingAnimationDurationMultiplier
+ + "pulsingCircleEnabled=" + pulsingCircleEnabled
+ + "pulsingCircleFadeEnabled=" + pulsingCircleFadeEnabled
+ + "pulseColor=" + pulseColor
+ + "pulseSingleDuration=" + pulseSingleDuration
+ + "pulsingCircleMaxRadius=" + pulsingCircleMaxRadius
+ + "pulseAlpha=" + pulseAlpha
+ + "pulseInterpolator=" + pulseInterpolator
+ "}";
}
@@ -892,6 +1029,36 @@ public class LocationComponentOptions implements Parcelable {
if (layerAbove != null ? !layerAbove.equals(options.layerAbove) : options.layerAbove != null) {
return false;
}
+
+ if (pulsingCircleEnabled != options.pulsingCircleEnabled) {
+ return false;
+ }
+
+ if (pulsingCircleFadeEnabled != options.pulsingCircleFadeEnabled) {
+ return false;
+ }
+
+ if (pulseColor != null ? !pulseColor.equals(options.pulseColor) :
+ options.pulseColor() != null) {
+ return false;
+ }
+ if (Float.compare(options.pulseSingleDuration, pulseSingleDuration) != 0) {
+ return false;
+ }
+
+ if (Float.compare(options.pulsingCircleMaxRadius, pulsingCircleMaxRadius) != 0) {
+ return false;
+ }
+
+ if (Float.compare(options.pulseAlpha, pulseAlpha) != 0) {
+ return false;
+ }
+
+ if (pulseInterpolator != null ? !pulseInterpolator.equals(options.pulseInterpolator)
+ : options.pulseInterpolator != null) {
+ return false;
+ }
+
return layerBelow != null ? layerBelow.equals(options.layerBelow) : options.layerBelow == null;
}
@@ -933,6 +1100,13 @@ public class LocationComponentOptions implements Parcelable {
? Float.floatToIntBits(trackingAnimationDurationMultiplier) : 0);
result = 31 * result + (compassAnimationEnabled ? 1 : 0);
result = 31 * result + (accuracyAnimationEnabled ? 1 : 0);
+ result = 31 * result + (pulsingCircleEnabled ? 1 : 0);
+ result = 31 * result + (pulsingCircleFadeEnabled ? 1 : 0);
+ result = 31 * result + (pulseColor != null ? pulseColor.hashCode() : 0);
+ result = 31 * result + (pulseSingleDuration != +0.0f ? Float.floatToIntBits(pulseSingleDuration) : 0);
+ result = 31 * result + (pulsingCircleMaxRadius != +0.0f ? Float.floatToIntBits(pulsingCircleMaxRadius) : 0);
+ result = 31 * result + (pulseAlpha != +0.0f ? Float.floatToIntBits(pulseAlpha) : 0);
+ result = 31 * result + (pulseInterpolator != null ? pulseInterpolator.hashCode() : 0);
return result;
}
@@ -973,7 +1147,14 @@ public class LocationComponentOptions implements Parcelable {
in.readString(),
in.readFloat(),
in.readInt() == 1,
- in.readInt() == 1
+ in.readInt() == 1,
+ in.readInt() == 1,
+ in.readInt() == 1,
+ in.readInt() == 0 ? in.readInt() : null,
+ in.readFloat(),
+ in.readFloat(),
+ in.readFloat(),
+ in.readString()
);
}
@@ -1073,6 +1254,18 @@ public class LocationComponentOptions implements Parcelable {
dest.writeFloat(trackingAnimationDurationMultiplier);
dest.writeInt(compassAnimationEnabled() ? 1 : 0);
dest.writeInt(accuracyAnimationEnabled() ? 1 : 0);
+ dest.writeInt(pulseEnabled() ? 1 : 0);
+ dest.writeInt(pulsingCircleFadeEnabled() ? 1 : 0);
+ if (pulseColor() == null) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ dest.writeInt(pulseColor());
+ }
+ dest.writeFloat(pulseSingleDuration());
+ dest.writeFloat(pulseMaxRadius());
+ dest.writeFloat(pulseAlpha());
+ dest.writeString(pulseInterpolator());
}
@Override
@@ -1108,6 +1301,32 @@ public class LocationComponentOptions implements Parcelable {
+ "Choose one or the other.");
}
+ if (locationComponentOptions.pulseEnabled() == null) {
+ String pulsingSetupError = "";
+ if (locationComponentOptions.pulsingCircleFadeEnabled() != null) {
+ pulsingSetupError += " pulsingCircleFadeEnabled";
+ }
+ if (locationComponentOptions.pulseColor() != null) {
+ pulsingSetupError += " pulsingCircleColor";
+ }
+ if (locationComponentOptions.pulseSingleDuration() > 0) {
+ pulsingSetupError += " pulsingCircleDuration";
+ }
+ if (locationComponentOptions.pulseMaxRadius() > 0) {
+ pulsingSetupError += " pulsingCircleMaxRadius";
+ }
+ if (locationComponentOptions.pulseAlpha() >= 0 && locationComponentOptions.pulseAlpha() <= 1) {
+ pulsingSetupError += " pulsingCircleAlpha";
+ }
+ if (locationComponentOptions.pulseInterpolator() != null) {
+ pulsingSetupError += " pulsingCircleInterpolator";
+ }
+ if (!pulsingSetupError.isEmpty()) {
+ throw new IllegalStateException("You've set up the following pulsing circle options but have not enabled"
+ + " the pulsing circle via the LocationComponentOptions builder:" + pulsingSetupError
+ + ". Enable the pulsing circle if you're going to set pulsing options.");
+ }
+ }
return locationComponentOptions;
}
@@ -1156,6 +1375,13 @@ public class LocationComponentOptions implements Parcelable {
private Float trackingAnimationDurationMultiplier;
private Boolean compassAnimationEnabled;
private Boolean accuracyAnimationEnabled;
+ private Boolean pulsingCircleEnabled;
+ private Boolean pulsingCircleFadeEnabled;
+ private int pulsingCircleColor;
+ private float pulsingCircleDuration;
+ private float pulsingCircleMaxRadius;
+ private float pulsingCircleAlpha;
+ private String pulsingCircleInterpolator;
Builder() {
}
@@ -1194,6 +1420,13 @@ public class LocationComponentOptions implements Parcelable {
this.trackingAnimationDurationMultiplier = source.trackingAnimationDurationMultiplier();
this.compassAnimationEnabled = source.compassAnimationEnabled();
this.accuracyAnimationEnabled = source.accuracyAnimationEnabled();
+ this.pulsingCircleEnabled = source.pulsingCircleEnabled;
+ this.pulsingCircleFadeEnabled = source.pulsingCircleFadeEnabled;
+ this.pulsingCircleColor = source.pulseColor;
+ this.pulsingCircleDuration = source.pulseSingleDuration;
+ this.pulsingCircleMaxRadius = source.pulsingCircleMaxRadius;
+ this.pulsingCircleAlpha = source.pulseAlpha;
+ this.pulsingCircleInterpolator = source.pulseInterpolator;
}
/**
@@ -1674,11 +1907,83 @@ public class LocationComponentOptions implements Parcelable {
*
* @return whether smooth animation of the accuracy circle is enabled
*/
- public Builder accuracyAnimationEnabled(Boolean accuracyAnimationEnabled) {
+ public LocationComponentOptions.Builder accuracyAnimationEnabled(Boolean accuracyAnimationEnabled) {
this.accuracyAnimationEnabled = accuracyAnimationEnabled;
return this;
}
+ /**
+ * Enable or disable the LocationComponent's pulsing circle.
+ *
+ * @return whether the LocationComponent's pulsing circle is enabled
+ */
+ public LocationComponentOptions.Builder pulsingCircleEnabled(Boolean pulsingCircleEnabled) {
+ this.pulsingCircleEnabled = pulsingCircleEnabled;
+ return this;
+ }
+
+ /**
+ * Enable or disable fading of the LocationComponent's pulsing circle. If it fades, the circle's
+ * opacity decreases as its radius increases.
+ *
+ * @return whether fading of the LocationComponent's pulsing circle is enabled
+ */
+ public LocationComponentOptions.Builder pulsingCircleFadeEnabled(Boolean pulsingCircleFadeEnabled) {
+ this.pulsingCircleFadeEnabled = pulsingCircleFadeEnabled;
+ return this;
+ }
+
+ /**
+ * Sets the color of the LocationComponent's pulsing circle.
+ *
+ * @return the current set color of the circle
+ */
+ public LocationComponentOptions.Builder pulsingCircleColor(int pulsingCircleColor) {
+ this.pulsingCircleColor = pulsingCircleColor;
+ return this;
+ }
+
+ /**
+ * Sets the number of milliseconds it takes for a single pulse of the LocationComponent's pulsing circle.
+ *
+ * @return the current set length of time for a single pulse
+ */
+ public LocationComponentOptions.Builder pulsingCircleDuration(float pulsingCircleDuration) {
+ this.pulsingCircleDuration = pulsingCircleDuration;
+ return this;
+ }
+
+ /**
+ * The maximum radius that a single pulse should expand the LocationComponent's pulsing circle to.
+ *
+ * @return the maximum radius that the pulsing circle will expand to.
+ */
+ public LocationComponentOptions.Builder pulsingCircleMaxRadius(float pulsingCircleMaxRadius) {
+ this.pulsingCircleMaxRadius = pulsingCircleMaxRadius;
+ return this;
+ }
+
+ /**
+ * Sets the opacity of the LocationComponent's pulsing circle.
+ *
+ * @return the current set opacity of the LocationComponent's circle
+ */
+ public LocationComponentOptions.Builder pulsingCircleAlpha(float pulsingCircleAlpha) {
+ this.pulsingCircleAlpha = pulsingCircleAlpha;
+ return this;
+ }
+
+ /**
+ * Sets the pulsing circle's interpolator animation. Pass through a mode constant via the
+ * {@link PulseMode} class.
+ *
+ * @return a String which represents the interpolator animation that the pulsing circle will use.
+ */
+ public LocationComponentOptions.Builder pulsingCircleInterpolator(String pulsingCircleInterpolator) {
+ this.pulsingCircleInterpolator = pulsingCircleInterpolator;
+ return this;
+ }
+
@Nullable
LocationComponentOptions autoBuild() {
String missing = "";
@@ -1772,7 +2077,14 @@ public class LocationComponentOptions implements Parcelable {
this.layerBelow,
this.trackingAnimationDurationMultiplier,
this.compassAnimationEnabled,
- this.accuracyAnimationEnabled);
+ this.accuracyAnimationEnabled,
+ this.pulsingCircleEnabled,
+ this.pulsingCircleFadeEnabled,
+ this.pulsingCircleColor,
+ this.pulsingCircleDuration,
+ this.pulsingCircleMaxRadius,
+ this.pulsingCircleAlpha,
+ this.pulsingCircleInterpolator);
}
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
index 54f8ee6d1a..53a6b74e4f 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java
@@ -45,15 +45,23 @@ import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PULSING_CIRCLE_LAYER;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_PULSING_OPACITY;
+import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_PULSING_RADIUS;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON;
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER;
+import static com.mapbox.mapboxsdk.style.expressions.Expression.get;
import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate;
import static com.mapbox.mapboxsdk.style.expressions.Expression.linear;
import static com.mapbox.mapboxsdk.style.expressions.Expression.stop;
import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom;
import static com.mapbox.mapboxsdk.style.layers.Property.NONE;
import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize;
import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility;
import static com.mapbox.mapboxsdk.utils.ColorUtils.colorToRgbaString;
@@ -127,6 +135,7 @@ final class LocationLayerController {
styleBearing(options);
styleAccuracy(options.accuracyAlpha(), options.accuracyColor());
styleScaling(options);
+ stylePulsingCircle(options);
determineIconsSource(options);
if (!isHidden) {
@@ -186,6 +195,13 @@ final class LocationLayerController {
}
}
+ /**
+ * Adjust the visibility of the pulsing LocationComponent circle.
+ */
+ void adjustPulsingCircleLayerVisibility(boolean visible) {
+ setLayerVisibility(PULSING_CIRCLE_LAYER, visible);
+ }
+
void hide() {
isHidden = true;
for (String layerId : layerSet) {
@@ -242,6 +258,7 @@ final class LocationLayerController {
addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER);
addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER);
addAccuracyLayer();
+ addPulsingCircleLayerToMap();
}
private void addSymbolLayer(@NonNull String layerId, @NonNull String beforeLayerId) {
@@ -259,6 +276,14 @@ final class LocationLayerController {
layerSet.add(layer.getId());
}
+ /**
+ * Add the pulsing LocationComponent circle to the map for future use, if need be.updatePulsingLocationCircleRadius
+ */
+ private void addPulsingCircleLayerToMap() {
+ Layer pulsingCircleLayer = layerSourceProvider.generatePulsingCircleLayer();
+ addLayerToMap(pulsingCircleLayer, ACCURACY_LAYER);
+ }
+
private void removeLayers() {
for (String layerId : layerSet) {
style.removeLayer(layerId);
@@ -276,6 +301,30 @@ final class LocationLayerController {
refreshSource();
}
+ /**
+ * Updates the {@link LocationComponentConstants#PROPERTY_PULSING_RADIUS} property value and refreshes
+ * the LocationComponent source. This leads to a smooth update to the visuals of the pulsing
+ * LocationComponent circle.
+ *
+ * @param radius The new radius in the animation.
+ */
+ private void updatePulsingLocationCircleRadius(float radius) {
+ locationFeature.addNumberProperty(PROPERTY_PULSING_RADIUS, radius);
+ refreshSource();
+ }
+
+ /**
+ * Updates the {@link LocationComponentConstants#PROPERTY_PULSING_OPACITY} property value and refreshes
+ * the LocationComponent source. This leads to a smooth update to the visuals of the pulsing
+ * LocationComponent circle. This is used if the fade option is set to true while setting pulsing options.
+ *
+ * @param opacity The new opacity in the animation.
+ */
+ private void updatePulsingLocationCircleOpacity(float opacity) {
+ locationFeature.addNumberProperty(PROPERTY_PULSING_OPACITY, opacity);
+ refreshSource();
+ }
+
//
// Source actions
//
@@ -365,6 +414,24 @@ final class LocationLayerController {
}
}
+ /**
+ * Use the Maps SDK's data-driven styling properties to set the pulsing circle location UI.
+ *
+ * @param options The {@link LocationComponentOptions} set upstream during LocationComponent
+ * initialization.
+ */
+ private void stylePulsingCircle(LocationComponentOptions options) {
+ if (style.getLayer(PULSING_CIRCLE_LAYER) != null) {
+ setLayerVisibility(PULSING_CIRCLE_LAYER, true);
+ style.getLayer(PULSING_CIRCLE_LAYER).setProperties(
+ circleRadius(get(PROPERTY_PULSING_RADIUS)),
+ circleColor(options.pulseColor()),
+ circleStrokeColor(options.pulseColor()),
+ circleOpacity(get(PROPERTY_PULSING_OPACITY))
+ );
+ }
+ }
+
private void determineIconsSource(LocationComponentOptions options) {
String foregroundIconString = buildIconString(
renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON);
@@ -444,6 +511,21 @@ final class LocationLayerController {
}
};
+ /**
+ * The listener that handles the updating of the pulsing circle's radius and opacity.
+ */
+ private final MapboxAnimator.AnimationsValueChangeListener<Float> pulsingCircleRadiusListener =
+ new MapboxAnimator.AnimationsValueChangeListener<Float>() {
+ @Override
+ public void onNewAnimationValue(Float newPulsingRadiusValue) {
+ updatePulsingLocationCircleRadius(newPulsingRadiusValue);
+ if (options.pulsingCircleFadeEnabled()) {
+ double newPulsingOpacityValue = 1 - ((newPulsingRadiusValue / 100) * 3);
+ updatePulsingLocationCircleOpacity((float) newPulsingOpacityValue);
+ }
+ }
+ };
+
Set<AnimatorListenerHolder> getAnimationListeners() {
Set<AnimatorListenerHolder> holders = new HashSet<>();
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_LATLNG, latLngValueListener));
@@ -459,6 +541,10 @@ final class LocationLayerController {
holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_ACCURACY, accuracyValueListener));
}
+ if (options.pulseEnabled()) {
+ holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_PULSING_CIRCLE,
+ pulsingCircleRadiusListener));
+ }
return holders;
}
} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
index dff7369cd5..2ffaceb507 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java
@@ -26,7 +26,8 @@ abstract class MapboxAnimator<K> extends ValueAnimator implements ValueAnimator.
ANIMATOR_CAMERA_COMPASS_BEARING,
ANIMATOR_LAYER_ACCURACY,
ANIMATOR_ZOOM,
- ANIMATOR_TILT
+ ANIMATOR_TILT,
+ ANIMATOR_PULSING_CIRCLE
})
@interface Type {
}
@@ -40,6 +41,7 @@ abstract class MapboxAnimator<K> extends ValueAnimator implements ValueAnimator.
static final int ANIMATOR_LAYER_ACCURACY = 6;
static final int ANIMATOR_ZOOM = 7;
static final int ANIMATOR_TILT = 8;
+ static final int ANIMATOR_PULSING_CIRCLE = 9;
private final AnimationsValueChangeListener<K> updateListener;
private final K target;
@@ -59,6 +61,15 @@ abstract class MapboxAnimator<K> extends ValueAnimator implements ValueAnimator.
addListener(new AnimatorListener());
}
+ public MapboxAnimator(AnimationsValueChangeListener<K> updateListener, K target, K animatedValue,
+ double minUpdateInterval, long timeElapsed) {
+ this.updateListener = updateListener;
+ this.target = target;
+ this.animatedValue = animatedValue;
+ this.minUpdateInterval = minUpdateInterval;
+ this.timeElapsed = timeElapsed;
+ }
+
@Override
public void onAnimationUpdate(ValueAnimator animation) {
animatedValue = (K) animation.getAnimatedValue();
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
index 938f4ec74a..36cce25d2b 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java
@@ -1,5 +1,6 @@
package com.mapbox.mapboxsdk.location;
+import android.animation.ValueAnimator;
import android.support.annotation.Nullable;
import com.mapbox.mapboxsdk.geometry.LatLng;
@@ -37,4 +38,33 @@ final class MapboxAnimatorProvider {
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
return new MapboxCameraAnimatorAdapter(previous, target, updateListener, cancelableCallback);
}
+
+ /**
+ * This animator is for the LocationComponent pulsing circle.
+ *
+ * @param updateListener the listener that is found in the {@link LocationAnimatorCoordinator}'s
+ * listener array.
+ * @param maxAnimationFps the max frames per second of the pulsing animation
+ * @param pulseSingleDuration the number of milliseconds it takes for the animator to create
+ * a single pulse.
+ * @param pulseMaxRadius the max radius when the circle is finished with a single pulse.
+ * @param desiredInterpolatorFromOptions the type of Android-system interpolator to use for
+ * the pulsing animation (linear, accelerate, bounce, etc.)
+ * @return a built {@link PulsingLocationCircleAnimator} object.
+ */
+ PulsingLocationCircleAnimator pulsingCircleAnimator(MapboxAnimator.AnimationsValueChangeListener updateListener,
+ int maxAnimationFps,
+ float pulseSingleDuration,
+ float pulseMaxRadius,
+ String desiredInterpolatorFromOptions) {
+ PulsingLocationCircleAnimator pulsingLocationCircleAnimator =
+ new PulsingLocationCircleAnimator(updateListener, maxAnimationFps, pulseMaxRadius);
+ pulsingLocationCircleAnimator.setDuration((long) pulseSingleDuration);
+ pulsingLocationCircleAnimator.setRepeatMode(ValueAnimator.RESTART);
+ pulsingLocationCircleAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ pulsingLocationCircleAnimator.retrievePulseInterpolator(desiredInterpolatorFromOptions);
+ pulsingLocationCircleAnimator.setInterpolator(
+ pulsingLocationCircleAnimator.retrievePulseInterpolator(desiredInterpolatorFromOptions));
+ return pulsingLocationCircleAnimator;
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java
index 1d09f8ae71..da7c666ae9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java
@@ -29,4 +29,15 @@ class MapboxAnimatorSetProvider {
locationAnimatorSet.setDuration(duration);
locationAnimatorSet.start();
}
+
+ /**
+ * Starts a single animator rather than playing multliple animators all at once.
+ *
+ * @param singleAnimation the {@link Animator} to run.
+ */
+ void startSingleAnimation(@NonNull Animator singleAnimation) {
+ AnimatorSet locationAnimatorSet = new AnimatorSet();
+ locationAnimatorSet.play(singleAnimation);
+ locationAnimatorSet.start();
+ }
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PulsingLocationCircleAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PulsingLocationCircleAnimator.java
new file mode 100644
index 0000000000..bfae5102ab
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/PulsingLocationCircleAnimator.java
@@ -0,0 +1,42 @@
+package com.mapbox.mapboxsdk.location;
+
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import com.mapbox.mapboxsdk.location.modes.PulseMode;
+
+/**
+ * Manages the logic of the interpolated animation which is applied to the LocationComponent's pulsing circle
+ */
+public class PulsingLocationCircleAnimator extends MapboxFloatAnimator {
+
+ /**
+ *
+ * @param updateListener the {@link AnimationsValueChangeListener} associated with this animator.
+ * @param maxAnimationFps the maximum frames per second that the animator should use. Default
+ * is the {@link LocationAnimatorCoordinator#maxAnimationFps} variable.
+ */
+ public PulsingLocationCircleAnimator(AnimationsValueChangeListener updateListener,
+ int maxAnimationFps,
+ float circleMaxRadius) {
+ super(0f, circleMaxRadius, updateListener, maxAnimationFps);
+ }
+
+ public Interpolator retrievePulseInterpolator(String desiredInterpolatorFromOptions) {
+ switch (desiredInterpolatorFromOptions) {
+ case PulseMode.LINEAR:
+ return new LinearInterpolator();
+ case PulseMode.ACCELERATE:
+ return new AccelerateInterpolator();
+ case PulseMode.DECELERATE:
+ return new DecelerateInterpolator();
+ case PulseMode.BOUNCE:
+ return new BounceInterpolator();
+ default:
+ return new DecelerateInterpolator();
+ }
+ }
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/PulseMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/PulseMode.java
new file mode 100644
index 0000000000..f8713ffe86
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/PulseMode.java
@@ -0,0 +1,50 @@
+package com.mapbox.mapboxsdk.location.modes;
+
+import android.support.annotation.StringDef;
+
+import com.mapbox.mapboxsdk.location.LocationComponentOptions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A convenience class for setting the {@link com.mapbox.mapboxsdk.location.LocationComponent}'s
+ * pulsing circle UI functionality. Use with
+ */
+public final class PulseMode {
+
+ private PulseMode() {
+ // Class should not be initialized
+ }
+
+ /**
+ * An interpolator defines the rate of change of an animation.
+ *
+ * One of these constants should be used with {@link LocationComponentOptions#pulseInterpolator}.
+ *
+ */
+ @StringDef( {LINEAR, ACCELERATE, DECELERATE, BOUNCE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Mode {
+ }
+
+ /**
+ * An interpolator where the rate of change is constant.
+ */
+ public static final String LINEAR = "linear";
+
+ /**
+ * An interpolator where the rate of change starts out slowly and and then accelerates.
+ */
+ public static final String ACCELERATE = "accelerate";
+
+ /**
+ * An interpolator where the rate of change starts out quickly and and then decelerates.
+ */
+ public static final String DECELERATE = "decelerate";
+
+ /**
+ * An interpolator where the change bounces at the end.
+ */
+ public static final String BOUNCE = "bounce";
+}