Adding pulsing circle to LocationComponent options (#172)
* [location] added pulsing locationcomponent circle * adjusting style switching to roll through all styles * refactored methods and variables for prefix continuity * refactoring to after making start and stop private methods * refactoring to stop pulsing when LocationComponent is disabled and color/speed is then selected * refactoring to swap interpolator string paramete for interpolator interface parameter * LocationLayerControllerTest adjustments * removing unneeded test * more rebase adjustments * [location] added pulsing locationcomponent circle * refactoring to swap interpolator string paramete for interpolator interface parameter * consolidating pulse ui update methods * gl-native vendor bump * refactor to check for activation with locationcomponent * Update MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java Co-Authored-By: Łukasz Paczos <lukasz.paczos@mapbox.com> * Update MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java Co-Authored-By: Łukasz Paczos <lukasz.paczos@mapbox.com> * Update MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java Co-Authored-By: Łukasz Paczos <lukasz.paczos@mapbox.com> * Update MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java Co-Authored-By: Łukasz Paczos <lukasz.paczos@mapbox.com> * Update MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java Co-Authored-By: Łukasz Paczos <lukasz.paczos@mapbox.com> * Added incompatibility docs * Added pulse alpha range explanation * Do not wrap pulsing circle animator with an animator set to avoid leaks Co-authored-by: Łukasz Paczos <lukasz.paczos@mapbox.com>
This commit is contained in:
@@ -2,8 +2,6 @@ package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.location.modes.RenderMode;
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
@@ -12,6 +10,8 @@ import com.mapbox.mapboxsdk.style.layers.Layer;
|
||||
import com.mapbox.mapboxsdk.utils.BitmapUtils;
|
||||
import com.mapbox.mapboxsdk.utils.ColorUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_STALE_ICON;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_ICON;
|
||||
@@ -181,6 +181,30 @@ class IndicatorLocationLayerRenderer implements LocationLayerRenderer {
|
||||
lastBearing = bearing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the visibility of the pulsing LocationComponent circle.
|
||||
*/
|
||||
@Override
|
||||
public void adjustPulsingCircleLayerVisibility(boolean visible) {
|
||||
// not supported at this time
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the the pulsing LocationComponent circle based on the set options.
|
||||
*/
|
||||
@Override
|
||||
public void stylePulsingCircle(LocationComponentOptions options) {
|
||||
// not supported at this time
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the visual appearance of the pulsing LocationComponent circle.
|
||||
*/
|
||||
@Override
|
||||
public void updatePulsingUi(float radius, @Nullable Float opacity) {
|
||||
// not supported at this time
|
||||
}
|
||||
|
||||
private void setImages(@RenderMode.Mode int renderMode, boolean isStale) {
|
||||
String topImage = "";
|
||||
String bearingImage = "";
|
||||
|
||||
@@ -32,6 +32,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;
|
||||
@@ -131,9 +132,23 @@ class LayerSourceProvider {
|
||||
LocationIndicatorLayer layer = new LocationIndicatorLayer(FOREGROUND_LAYER);
|
||||
layer.setLocationTransition(new TransitionOptions(0, 0));
|
||||
layer.setProperties(
|
||||
LocationPropertyFactory.perspectiveCompensation(0.9f),
|
||||
LocationPropertyFactory.imageTiltDisplacement(4f)
|
||||
LocationPropertyFactory.perspectiveCompensation(0.9f),
|
||||
LocationPropertyFactory.imageTiltDisplacement(4f)
|
||||
);
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.animation.Animator;
|
||||
import android.location.Location;
|
||||
import android.os.SystemClock;
|
||||
import android.util.SparseArray;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -32,6 +33,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;
|
||||
@@ -186,6 +188,28 @@ 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);
|
||||
if (listener != null) {
|
||||
PulsingLocationCircleAnimator pulsingLocationCircleAnimator = animatorProvider.pulsingCircleAnimator(
|
||||
listener,
|
||||
maxAnimationFps,
|
||||
options.pulseSingleDuration(),
|
||||
options.pulseMaxRadius(),
|
||||
options.pulseInterpolator() == null ? new DecelerateInterpolator() : options.pulseInterpolator());
|
||||
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);
|
||||
@@ -349,6 +373,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) {
|
||||
animator.start();
|
||||
}
|
||||
}
|
||||
|
||||
void resetAllCameraAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) {
|
||||
resetCameraCompassAnimation(currentCameraPosition);
|
||||
boolean snap = resetCameraLocationAnimations(currentCameraPosition, isGpsNorth);
|
||||
@@ -446,6 +482,13 @@ final class LocationAnimatorCoordinator {
|
||||
cancelAnimator(ANIMATOR_TILT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the pulsing circle location animator.
|
||||
*/
|
||||
void stopPulsingCircleAnimation() {
|
||||
cancelAnimator(ANIMATOR_PULSING_CIRCLE);
|
||||
}
|
||||
|
||||
void cancelAllAnimations() {
|
||||
for (int i = 0; i < animatorArray.size(); i++) {
|
||||
@MapboxAnimator.Type int animatorType = animatorArray.keyAt(i);
|
||||
|
||||
@@ -726,10 +726,25 @@ public final class LocationComponent {
|
||||
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options.trackingAnimationDurationMultiplier());
|
||||
locationAnimatorCoordinator.setCompassAnimationEnabled(options.compassAnimationEnabled());
|
||||
locationAnimatorCoordinator.setAccuracyAnimationEnabled(options.accuracyAnimationEnabled());
|
||||
if (options.pulseEnabled()) {
|
||||
startPulsingLocationCircle();
|
||||
} else {
|
||||
stopPulsingLocationCircle();
|
||||
}
|
||||
updateMapWithOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the LocationComponent's pulsing circle UI.
|
||||
*/
|
||||
private void startPulsingLocationCircle() {
|
||||
if (isEnabled && isLayerReady) {
|
||||
locationAnimatorCoordinator.startLocationComponentCirclePulsing(options);
|
||||
locationLayerController.adjustPulsingCircleLayerVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Zooms to the desired zoom level.
|
||||
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
||||
@@ -1215,6 +1230,14 @@ public final class LocationComponent {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the LocationComponent's pulsing circle animation.
|
||||
*/
|
||||
private void stopPulsingLocationCircle() {
|
||||
locationAnimatorCoordinator.stopPulsingCircleAnimation();
|
||||
locationLayerController.adjustPulsingCircleLayerVisibility(false);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private void onLocationLayerStart() {
|
||||
if (!isComponentInitialized || !isComponentStarted || mapboxMap.getStyle() == null) {
|
||||
@@ -1240,6 +1263,11 @@ public final class LocationComponent {
|
||||
}
|
||||
}
|
||||
setCameraMode(locationCameraController.getCameraMode());
|
||||
if (options.pulseEnabled()) {
|
||||
startPulsingLocationCircle();
|
||||
} else {
|
||||
stopPulsingLocationCircle();
|
||||
}
|
||||
setLastLocation();
|
||||
updateCompassListenerState(true);
|
||||
setLastCompassHeading();
|
||||
@@ -1256,6 +1284,8 @@ public final class LocationComponent {
|
||||
if (compassEngine != null) {
|
||||
updateCompassListenerState(false);
|
||||
}
|
||||
|
||||
stopPulsingLocationCircle();
|
||||
locationAnimatorCoordinator.cancelAllAnimations();
|
||||
if (locationEngine != null) {
|
||||
locationEngine.removeLocationUpdates(currentLocationEngineListener);
|
||||
@@ -1430,6 +1460,9 @@ public final class LocationComponent {
|
||||
boolean isLocationLayerHidden = locationLayerController.isHidden();
|
||||
if (isEnabled && isComponentStarted && isLocationLayerHidden) {
|
||||
locationLayerController.show();
|
||||
if (options.pulseEnabled()) {
|
||||
locationLayerController.adjustPulsingCircleLayerVisibility(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,6 +131,10 @@ public class LocationComponentActivationOptions {
|
||||
* or {@link LocationComponentConstants#LOCATION_SOURCE} are ignored.
|
||||
* <li> All options that alter the image ID, like {@link LocationComponentOptions#foregroundName()}, are ignored.
|
||||
* Use {@link LocationComponentOptions#foregroundDrawable()} to alter the image rendered as the puck.
|
||||
* <li> The LocationComponent's pulsing effect. Any of the {@link LocationComponentOptions}'
|
||||
* pulsing methods such as {@link LocationComponentOptions#pulseEnabled()} or
|
||||
* {@link LocationComponentOptions#pulseColor()}, are useless when using the
|
||||
* the specialized location layer.
|
||||
* </ul>
|
||||
*
|
||||
* @return whether the default specialized location layer is used
|
||||
@@ -244,6 +248,10 @@ public class LocationComponentActivationOptions {
|
||||
* or {@link LocationComponentConstants#LOCATION_SOURCE} are ignored.
|
||||
* <li> All options that alter the image ID, like {@link LocationComponentOptions#foregroundName()}, are ignored.
|
||||
* Use {@link LocationComponentOptions#foregroundDrawable()} to alter the image rendered as the puck.
|
||||
* <li> The LocationComponent's pulsing effect. Any of the {@link LocationComponentOptions}'
|
||||
* pulsing methods such as {@link LocationComponentOptions#pulseEnabled()} or
|
||||
* {@link LocationComponentOptions#pulseColor()}, are useless when using the
|
||||
* the specialized location layer.
|
||||
* </ul>
|
||||
*
|
||||
* @param useSpecializedLocationLayer true if you want to initialize and use the
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -5,12 +5,7 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.mapbox.android.gestures.AndroidGesturesManager;
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
@@ -18,6 +13,13 @@ import com.mapbox.mapboxsdk.style.layers.Layer;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.Dimension;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StyleRes;
|
||||
|
||||
/**
|
||||
* This class exposes options for the Location Component. The options can be set by defining a
|
||||
* style in your apps style.xml file and passing in directly into the {@link LocationComponent}
|
||||
@@ -69,6 +71,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,41 +131,56 @@ public class LocationComponentOptions implements Parcelable {
|
||||
private float trackingAnimationDurationMultiplier;
|
||||
private boolean compassAnimationEnabled;
|
||||
private boolean accuracyAnimationEnabled;
|
||||
private Boolean pulseEnabled;
|
||||
private Boolean pulseFadeEnabled;
|
||||
private Integer pulseColor;
|
||||
private float pulseSingleDuration;
|
||||
private float pulseMaxRadius;
|
||||
private float pulseAlpha;
|
||||
@Nullable
|
||||
private Interpolator pulseInterpolator;
|
||||
|
||||
public LocationComponentOptions(
|
||||
float accuracyAlpha,
|
||||
int accuracyColor,
|
||||
int backgroundDrawableStale,
|
||||
@Nullable String backgroundStaleName,
|
||||
int foregroundDrawableStale,
|
||||
@Nullable String foregroundStaleName,
|
||||
int gpsDrawable,
|
||||
@Nullable String gpsName,
|
||||
int foregroundDrawable,
|
||||
@Nullable String foregroundName,
|
||||
int backgroundDrawable,
|
||||
@Nullable String backgroundName,
|
||||
int bearingDrawable,
|
||||
@Nullable String bearingName,
|
||||
@Nullable Integer bearingTintColor,
|
||||
@Nullable Integer foregroundTintColor,
|
||||
@Nullable Integer backgroundTintColor,
|
||||
@Nullable Integer foregroundStaleTintColor,
|
||||
@Nullable Integer backgroundStaleTintColor,
|
||||
float elevation,
|
||||
boolean enableStaleState,
|
||||
long staleStateTimeout,
|
||||
@Nullable int[] padding,
|
||||
float maxZoomIconScale,
|
||||
float minZoomIconScale,
|
||||
boolean trackingGesturesManagement,
|
||||
float trackingInitialMoveThreshold,
|
||||
float trackingMultiFingerMoveThreshold,
|
||||
String layerAbove,
|
||||
String layerBelow,
|
||||
float trackingAnimationDurationMultiplier,
|
||||
boolean compassAnimationEnabled,
|
||||
boolean accuracyAnimationEnabled) {
|
||||
float accuracyAlpha,
|
||||
int accuracyColor,
|
||||
int backgroundDrawableStale,
|
||||
@Nullable String backgroundStaleName,
|
||||
int foregroundDrawableStale,
|
||||
@Nullable String foregroundStaleName,
|
||||
int gpsDrawable,
|
||||
@Nullable String gpsName,
|
||||
int foregroundDrawable,
|
||||
@Nullable String foregroundName,
|
||||
int backgroundDrawable,
|
||||
@Nullable String backgroundName,
|
||||
int bearingDrawable,
|
||||
@Nullable String bearingName,
|
||||
@Nullable Integer bearingTintColor,
|
||||
@Nullable Integer foregroundTintColor,
|
||||
@Nullable Integer backgroundTintColor,
|
||||
@Nullable Integer foregroundStaleTintColor,
|
||||
@Nullable Integer backgroundStaleTintColor,
|
||||
float elevation,
|
||||
boolean enableStaleState,
|
||||
long staleStateTimeout,
|
||||
@Nullable int[] padding,
|
||||
float maxZoomIconScale,
|
||||
float minZoomIconScale,
|
||||
boolean trackingGesturesManagement,
|
||||
float trackingInitialMoveThreshold,
|
||||
float trackingMultiFingerMoveThreshold,
|
||||
String layerAbove,
|
||||
String layerBelow,
|
||||
float trackingAnimationDurationMultiplier,
|
||||
boolean compassAnimationEnabled,
|
||||
boolean accuracyAnimationEnabled,
|
||||
Boolean pulseEnabled,
|
||||
Boolean pulseFadeEnabled,
|
||||
Integer pulseColor,
|
||||
float pulseSingleDuration,
|
||||
float pulseMaxRadius,
|
||||
float pulseAlpha,
|
||||
@Nullable Interpolator pulseInterpolator) {
|
||||
this.accuracyAlpha = accuracyAlpha;
|
||||
this.accuracyColor = accuracyColor;
|
||||
this.backgroundDrawableStale = backgroundDrawableStale;
|
||||
@@ -185,6 +217,13 @@ public class LocationComponentOptions implements Parcelable {
|
||||
this.trackingAnimationDurationMultiplier = trackingAnimationDurationMultiplier;
|
||||
this.compassAnimationEnabled = compassAnimationEnabled;
|
||||
this.accuracyAnimationEnabled = accuracyAnimationEnabled;
|
||||
this.pulseEnabled = pulseEnabled;
|
||||
this.pulseFadeEnabled = pulseFadeEnabled;
|
||||
this.pulseColor = pulseColor;
|
||||
this.pulseSingleDuration = pulseSingleDuration;
|
||||
this.pulseMaxRadius = pulseMaxRadius;
|
||||
this.pulseAlpha = pulseAlpha;
|
||||
this.pulseInterpolator = pulseInterpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,106 +241,131 @@ public class LocationComponentOptions implements Parcelable {
|
||||
@StyleRes int styleRes) {
|
||||
|
||||
TypedArray typedArray = context.obtainStyledAttributes(
|
||||
styleRes, R.styleable.mapbox_LocationComponent);
|
||||
styleRes, R.styleable.mapbox_LocationComponent);
|
||||
|
||||
LocationComponentOptions.Builder builder = new LocationComponentOptions.Builder()
|
||||
.enableStaleState(true)
|
||||
.staleStateTimeout(STALE_STATE_DELAY_MS)
|
||||
.maxZoomIconScale(MAX_ZOOM_ICON_SCALE_DEFAULT)
|
||||
.minZoomIconScale(MIN_ZOOM_ICON_SCALE_DEFAULT)
|
||||
.padding(PADDING_DEFAULT);
|
||||
.enableStaleState(true)
|
||||
.staleStateTimeout(STALE_STATE_DELAY_MS)
|
||||
.maxZoomIconScale(MAX_ZOOM_ICON_SCALE_DEFAULT)
|
||||
.minZoomIconScale(MIN_ZOOM_ICON_SCALE_DEFAULT)
|
||||
.padding(PADDING_DEFAULT);
|
||||
|
||||
builder.foregroundDrawable(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1));
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor)) {
|
||||
builder.foregroundTintColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor, -1));
|
||||
}
|
||||
builder.backgroundDrawable(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawable, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawable, -1));
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor)) {
|
||||
builder.backgroundTintColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor, -1));
|
||||
}
|
||||
builder.foregroundDrawableStale(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawableStale, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawableStale, -1));
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor)) {
|
||||
builder.foregroundStaleTintColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor, -1));
|
||||
}
|
||||
builder.backgroundDrawableStale(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawableStale, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawableStale, -1));
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor)) {
|
||||
builder.backgroundStaleTintColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor, -1));
|
||||
}
|
||||
builder.bearingDrawable(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_bearingDrawable, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_bearingDrawable, -1));
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor)) {
|
||||
builder.bearingTintColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor, -1));
|
||||
}
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_enableStaleState)) {
|
||||
builder.enableStaleState(typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_enableStaleState, true));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_enableStaleState, true));
|
||||
}
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout)) {
|
||||
builder.staleStateTimeout(typedArray.getInteger(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout, (int) STALE_STATE_DELAY_MS));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout, (int) STALE_STATE_DELAY_MS));
|
||||
}
|
||||
builder.gpsDrawable(typedArray.getResourceId(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_gpsDrawable, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_gpsDrawable, -1));
|
||||
float elevation = typedArray.getDimension(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_elevation, 0);
|
||||
R.styleable.mapbox_LocationComponent_mapbox_elevation, 0);
|
||||
builder.accuracyColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyColor, -1));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyColor, -1));
|
||||
builder.accuracyAlpha(typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyAlpha, ACCURACY_ALPHA_DEFAULT));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyAlpha, ACCURACY_ALPHA_DEFAULT));
|
||||
builder.elevation(elevation);
|
||||
|
||||
builder.trackingGesturesManagement(typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingGesturesManagement, false));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingGesturesManagement, false));
|
||||
builder.trackingInitialMoveThreshold(typedArray.getDimension(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingInitialMoveThreshold,
|
||||
context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingInitialMoveThreshold)));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingInitialMoveThreshold,
|
||||
context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingInitialMoveThreshold)));
|
||||
builder.trackingMultiFingerMoveThreshold(typedArray.getDimension(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingMultiFingerMoveThreshold,
|
||||
context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold)));
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingMultiFingerMoveThreshold,
|
||||
context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold)));
|
||||
|
||||
builder.padding(new int[] {
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingLeft, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingTop, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingRight, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingBottom, 0),
|
||||
builder.padding(new int[]{
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingLeft, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingTop, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingRight, 0),
|
||||
typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingBottom, 0),
|
||||
});
|
||||
|
||||
builder.layerAbove(
|
||||
typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_above));
|
||||
typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_above));
|
||||
|
||||
builder.layerBelow(
|
||||
typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_below));
|
||||
typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_below));
|
||||
|
||||
float minScale = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_minZoomIconScale, MIN_ZOOM_ICON_SCALE_DEFAULT);
|
||||
R.styleable.mapbox_LocationComponent_mapbox_minZoomIconScale, MIN_ZOOM_ICON_SCALE_DEFAULT);
|
||||
float maxScale = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_maxZoomIconScale, MAX_ZOOM_ICON_SCALE_DEFAULT);
|
||||
R.styleable.mapbox_LocationComponent_mapbox_maxZoomIconScale, MAX_ZOOM_ICON_SCALE_DEFAULT);
|
||||
builder.minZoomIconScale(minScale);
|
||||
builder.maxZoomIconScale(maxScale);
|
||||
|
||||
float trackingAnimationDurationMultiplier = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingAnimationDurationMultiplier,
|
||||
TRACKING_ANIMATION_DURATION_MULTIPLIER_DEFAULT
|
||||
R.styleable.mapbox_LocationComponent_mapbox_trackingAnimationDurationMultiplier,
|
||||
TRACKING_ANIMATION_DURATION_MULTIPLIER_DEFAULT
|
||||
);
|
||||
builder.trackingAnimationDurationMultiplier(trackingAnimationDurationMultiplier);
|
||||
|
||||
builder.compassAnimationEnabled = typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_compassAnimationEnabled, true
|
||||
R.styleable.mapbox_LocationComponent_mapbox_compassAnimationEnabled, true
|
||||
);
|
||||
|
||||
builder.accuracyAnimationEnabled = typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyAnimationEnabled, true
|
||||
R.styleable.mapbox_LocationComponent_mapbox_accuracyAnimationEnabled, true
|
||||
);
|
||||
|
||||
builder.pulseEnabled = typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleEnabled, false
|
||||
);
|
||||
|
||||
builder.pulseFadeEnabled = typedArray.getBoolean(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleFadeEnabled, true
|
||||
);
|
||||
|
||||
if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleColor)) {
|
||||
builder.pulseColor(typedArray.getColor(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleColor,
|
||||
-1));
|
||||
}
|
||||
|
||||
builder.pulseSingleDuration = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleDuration, CIRCLE_PULSING_DURATION_DEFAULT_MS
|
||||
);
|
||||
|
||||
builder.pulseMaxRadius = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleRadius, CIRCLE_PULSING_MAX_RADIUS_DEFAULT
|
||||
);
|
||||
|
||||
builder.pulseAlpha = typedArray.getFloat(
|
||||
R.styleable.mapbox_LocationComponent_mapbox_pulsingLocationCircleAlpha, CIRCLE_PULSING_ALPHA_DEFAULT);
|
||||
|
||||
typedArray.recycle();
|
||||
|
||||
return builder.build();
|
||||
@@ -330,7 +394,7 @@ public class LocationComponentOptions implements Parcelable {
|
||||
@NonNull
|
||||
public static Builder builder(@NonNull Context context) {
|
||||
return LocationComponentOptions.createFromAttributes(context,
|
||||
R.style.mapbox_LocationComponent).toBuilder();
|
||||
R.style.mapbox_LocationComponent).toBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -740,42 +804,114 @@ 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 pulseEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pulseFadeEnabled() {
|
||||
return pulseFadeEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 pulseMaxRadius;
|
||||
}
|
||||
|
||||
/**
|
||||
* The opacity of the LocationComponent's circle as it pulses. The expected range is
|
||||
* 0 to 1. An opacity of 1 makes the layer fully visible.
|
||||
*
|
||||
* @return the current opacity of the LocationComponent's pulsing 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
|
||||
*/
|
||||
@Nullable
|
||||
public Interpolator pulseInterpolator() {
|
||||
return pulseInterpolator;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LocationComponentOptions{"
|
||||
+ "accuracyAlpha=" + accuracyAlpha + ", "
|
||||
+ "accuracyColor=" + accuracyColor + ", "
|
||||
+ "backgroundDrawableStale=" + backgroundDrawableStale + ", "
|
||||
+ "backgroundStaleName=" + backgroundStaleName + ", "
|
||||
+ "foregroundDrawableStale=" + foregroundDrawableStale + ", "
|
||||
+ "foregroundStaleName=" + foregroundStaleName + ", "
|
||||
+ "gpsDrawable=" + gpsDrawable + ", "
|
||||
+ "gpsName=" + gpsName + ", "
|
||||
+ "foregroundDrawable=" + foregroundDrawable + ", "
|
||||
+ "foregroundName=" + foregroundName + ", "
|
||||
+ "backgroundDrawable=" + backgroundDrawable + ", "
|
||||
+ "backgroundName=" + backgroundName + ", "
|
||||
+ "bearingDrawable=" + bearingDrawable + ", "
|
||||
+ "bearingName=" + bearingName + ", "
|
||||
+ "bearingTintColor=" + bearingTintColor + ", "
|
||||
+ "foregroundTintColor=" + foregroundTintColor + ", "
|
||||
+ "backgroundTintColor=" + backgroundTintColor + ", "
|
||||
+ "foregroundStaleTintColor=" + foregroundStaleTintColor + ", "
|
||||
+ "backgroundStaleTintColor=" + backgroundStaleTintColor + ", "
|
||||
+ "elevation=" + elevation + ", "
|
||||
+ "enableStaleState=" + enableStaleState + ", "
|
||||
+ "staleStateTimeout=" + staleStateTimeout + ", "
|
||||
+ "padding=" + Arrays.toString(padding) + ", "
|
||||
+ "maxZoomIconScale=" + maxZoomIconScale + ", "
|
||||
+ "minZoomIconScale=" + minZoomIconScale + ", "
|
||||
+ "trackingGesturesManagement=" + trackingGesturesManagement + ", "
|
||||
+ "trackingInitialMoveThreshold=" + trackingInitialMoveThreshold + ", "
|
||||
+ "trackingMultiFingerMoveThreshold=" + trackingMultiFingerMoveThreshold + ", "
|
||||
+ "layerAbove=" + layerAbove
|
||||
+ "layerBelow=" + layerBelow
|
||||
+ "trackingAnimationDurationMultiplier=" + trackingAnimationDurationMultiplier
|
||||
+ "}";
|
||||
+ "accuracyAlpha=" + accuracyAlpha + ", "
|
||||
+ "accuracyColor=" + accuracyColor + ", "
|
||||
+ "backgroundDrawableStale=" + backgroundDrawableStale + ", "
|
||||
+ "backgroundStaleName=" + backgroundStaleName + ", "
|
||||
+ "foregroundDrawableStale=" + foregroundDrawableStale + ", "
|
||||
+ "foregroundStaleName=" + foregroundStaleName + ", "
|
||||
+ "gpsDrawable=" + gpsDrawable + ", "
|
||||
+ "gpsName=" + gpsName + ", "
|
||||
+ "foregroundDrawable=" + foregroundDrawable + ", "
|
||||
+ "foregroundName=" + foregroundName + ", "
|
||||
+ "backgroundDrawable=" + backgroundDrawable + ", "
|
||||
+ "backgroundName=" + backgroundName + ", "
|
||||
+ "bearingDrawable=" + bearingDrawable + ", "
|
||||
+ "bearingName=" + bearingName + ", "
|
||||
+ "bearingTintColor=" + bearingTintColor + ", "
|
||||
+ "foregroundTintColor=" + foregroundTintColor + ", "
|
||||
+ "backgroundTintColor=" + backgroundTintColor + ", "
|
||||
+ "foregroundStaleTintColor=" + foregroundStaleTintColor + ", "
|
||||
+ "backgroundStaleTintColor=" + backgroundStaleTintColor + ", "
|
||||
+ "elevation=" + elevation + ", "
|
||||
+ "enableStaleState=" + enableStaleState + ", "
|
||||
+ "staleStateTimeout=" + staleStateTimeout + ", "
|
||||
+ "padding=" + Arrays.toString(padding) + ", "
|
||||
+ "maxZoomIconScale=" + maxZoomIconScale + ", "
|
||||
+ "minZoomIconScale=" + minZoomIconScale + ", "
|
||||
+ "trackingGesturesManagement=" + trackingGesturesManagement + ", "
|
||||
+ "trackingInitialMoveThreshold=" + trackingInitialMoveThreshold + ", "
|
||||
+ "trackingMultiFingerMoveThreshold=" + trackingMultiFingerMoveThreshold + ", "
|
||||
+ "layerAbove=" + layerAbove
|
||||
+ "layerBelow=" + layerBelow
|
||||
+ "trackingAnimationDurationMultiplier=" + trackingAnimationDurationMultiplier
|
||||
+ "pulseEnabled=" + pulseEnabled
|
||||
+ "pulseFadeEnabled=" + pulseFadeEnabled
|
||||
+ "pulseColor=" + pulseColor
|
||||
+ "pulseSingleDuration=" + pulseSingleDuration
|
||||
+ "pulseMaxRadius=" + pulseMaxRadius
|
||||
+ "pulseAlpha=" + pulseAlpha
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -847,11 +983,11 @@ public class LocationComponentOptions implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
if (backgroundStaleName != null ? !backgroundStaleName.equals(options.backgroundStaleName) :
|
||||
options.backgroundStaleName != null) {
|
||||
options.backgroundStaleName != null) {
|
||||
return false;
|
||||
}
|
||||
if (foregroundStaleName != null ? !foregroundStaleName.equals(options.foregroundStaleName) :
|
||||
options.foregroundStaleName != null) {
|
||||
options.foregroundStaleName != null) {
|
||||
return false;
|
||||
}
|
||||
if (gpsName != null ? !gpsName.equals(options.gpsName) : options.gpsName != null) {
|
||||
@@ -867,23 +1003,23 @@ public class LocationComponentOptions implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
if (bearingTintColor != null ? !bearingTintColor.equals(options.bearingTintColor) :
|
||||
options.bearingTintColor != null) {
|
||||
options.bearingTintColor != null) {
|
||||
return false;
|
||||
}
|
||||
if (foregroundTintColor != null ? !foregroundTintColor.equals(options.foregroundTintColor) :
|
||||
options.foregroundTintColor != null) {
|
||||
options.foregroundTintColor != null) {
|
||||
return false;
|
||||
}
|
||||
if (backgroundTintColor != null ? !backgroundTintColor.equals(options.backgroundTintColor) :
|
||||
options.backgroundTintColor != null) {
|
||||
options.backgroundTintColor != null) {
|
||||
return false;
|
||||
}
|
||||
if (foregroundStaleTintColor != null ? !foregroundStaleTintColor.equals(options.foregroundStaleTintColor) :
|
||||
options.foregroundStaleTintColor != null) {
|
||||
options.foregroundStaleTintColor != null) {
|
||||
return false;
|
||||
}
|
||||
if (backgroundStaleTintColor != null ? !backgroundStaleTintColor.equals(options.backgroundStaleTintColor) :
|
||||
options.backgroundStaleTintColor != null) {
|
||||
options.backgroundStaleTintColor != null) {
|
||||
return false;
|
||||
}
|
||||
if (!Arrays.equals(padding, options.padding)) {
|
||||
@@ -892,6 +1028,31 @@ public class LocationComponentOptions implements Parcelable {
|
||||
if (layerAbove != null ? !layerAbove.equals(options.layerAbove) : options.layerAbove != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pulseEnabled != options.pulseEnabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pulseFadeEnabled != options.pulseFadeEnabled) {
|
||||
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.pulseMaxRadius, pulseMaxRadius) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Float.compare(options.pulseAlpha, pulseAlpha) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return layerBelow != null ? layerBelow.equals(options.layerBelow) : options.layerBelow == null;
|
||||
}
|
||||
|
||||
@@ -924,57 +1085,70 @@ public class LocationComponentOptions implements Parcelable {
|
||||
result = 31 * result + (minZoomIconScale != +0.0f ? Float.floatToIntBits(minZoomIconScale) : 0);
|
||||
result = 31 * result + (trackingGesturesManagement ? 1 : 0);
|
||||
result = 31 * result + (trackingInitialMoveThreshold != +0.0f
|
||||
? Float.floatToIntBits(trackingInitialMoveThreshold) : 0);
|
||||
? Float.floatToIntBits(trackingInitialMoveThreshold) : 0);
|
||||
result = 31 * result + (trackingMultiFingerMoveThreshold != +0.0f
|
||||
? Float.floatToIntBits(trackingMultiFingerMoveThreshold) : 0);
|
||||
? Float.floatToIntBits(trackingMultiFingerMoveThreshold) : 0);
|
||||
result = 31 * result + (layerAbove != null ? layerAbove.hashCode() : 0);
|
||||
result = 31 * result + (layerBelow != null ? layerBelow.hashCode() : 0);
|
||||
result = 31 * result + (trackingAnimationDurationMultiplier != +0.0f
|
||||
? Float.floatToIntBits(trackingAnimationDurationMultiplier) : 0);
|
||||
? Float.floatToIntBits(trackingAnimationDurationMultiplier) : 0);
|
||||
result = 31 * result + (compassAnimationEnabled ? 1 : 0);
|
||||
result = 31 * result + (accuracyAnimationEnabled ? 1 : 0);
|
||||
result = 31 * result + (pulseEnabled ? 1 : 0);
|
||||
result = 31 * result + (pulseFadeEnabled ? 1 : 0);
|
||||
result = 31 * result + (pulseColor != null ? pulseColor.hashCode() : 0);
|
||||
result = 31 * result + (pulseSingleDuration != +0.0f ? Float.floatToIntBits(pulseSingleDuration) : 0);
|
||||
result = 31 * result + (pulseMaxRadius != +0.0f ? Float.floatToIntBits(pulseMaxRadius) : 0);
|
||||
result = 31 * result + (pulseAlpha != +0.0f ? Float.floatToIntBits(pulseAlpha) : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<LocationComponentOptions> CREATOR =
|
||||
new Parcelable.Creator<LocationComponentOptions>() {
|
||||
new Parcelable.Creator<LocationComponentOptions>() {
|
||||
@Override
|
||||
public LocationComponentOptions createFromParcel(Parcel in) {
|
||||
public LocationComponentOptions createFromParcel(Parcel in) {
|
||||
return new LocationComponentOptions(
|
||||
in.readFloat(),
|
||||
in.readInt(),
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readLong(),
|
||||
in.createIntArray(),
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
in.readString(),
|
||||
in.readString(),
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readInt() == 1
|
||||
);
|
||||
in.readFloat(),
|
||||
in.readInt(),
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt(),
|
||||
in.readInt() == 0 ? in.readString() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readLong(),
|
||||
in.createIntArray(),
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
in.readString(),
|
||||
in.readString(),
|
||||
in.readFloat(),
|
||||
in.readInt() == 1,
|
||||
in.readInt() == 1,
|
||||
in.readInt() == 1,
|
||||
in.readInt() == 1,
|
||||
in.readInt() == 0 ? in.readInt() : null,
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
in.readFloat(),
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1073,6 +1247,17 @@ 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(pulseFadeEnabled() ? 1 : 0);
|
||||
if (pulseColor() == null) {
|
||||
dest.writeInt(1);
|
||||
} else {
|
||||
dest.writeInt(0);
|
||||
dest.writeInt(pulseColor());
|
||||
}
|
||||
dest.writeFloat(pulseSingleDuration());
|
||||
dest.writeFloat(pulseMaxRadius());
|
||||
dest.writeFloat(pulseAlpha());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1095,19 +1280,45 @@ public class LocationComponentOptions implements Parcelable {
|
||||
LocationComponentOptions locationComponentOptions = autoBuild();
|
||||
if (locationComponentOptions.accuracyAlpha() < 0 || locationComponentOptions.accuracyAlpha() > 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"Accuracy alpha value must be between 0.0 and 1.0.");
|
||||
"Accuracy alpha value must be between 0.0 and 1.0.");
|
||||
}
|
||||
|
||||
if (locationComponentOptions.elevation() < 0f) {
|
||||
throw new IllegalArgumentException("Invalid shadow size "
|
||||
+ locationComponentOptions.elevation() + ". Must be >= 0");
|
||||
+ locationComponentOptions.elevation() + ". Must be >= 0");
|
||||
}
|
||||
|
||||
if (locationComponentOptions.layerAbove() != null && locationComponentOptions.layerBelow() != null) {
|
||||
throw new IllegalArgumentException("You cannot set both layerAbove and layerBelow options."
|
||||
+ "Choose one or the other.");
|
||||
+ " Choose one or the other.");
|
||||
}
|
||||
|
||||
if (locationComponentOptions.pulseEnabled() == null) {
|
||||
String pulsingSetupError = "";
|
||||
if (locationComponentOptions.pulseFadeEnabled() != null) {
|
||||
pulsingSetupError += " pulseFadeEnabled";
|
||||
}
|
||||
if (locationComponentOptions.pulseColor() != null) {
|
||||
pulsingSetupError += " pulseColor";
|
||||
}
|
||||
if (locationComponentOptions.pulseSingleDuration() > 0) {
|
||||
pulsingSetupError += " pulseSingleDuration";
|
||||
}
|
||||
if (locationComponentOptions.pulseMaxRadius() > 0) {
|
||||
pulsingSetupError += " pulseMaxRadius";
|
||||
}
|
||||
if (locationComponentOptions.pulseAlpha() >= 0 && locationComponentOptions.pulseAlpha() <= 1) {
|
||||
pulsingSetupError += " pulseAlpha";
|
||||
}
|
||||
if (locationComponentOptions.pulseInterpolator() != null) {
|
||||
pulsingSetupError += " pulseInterpolator";
|
||||
}
|
||||
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 +1367,14 @@ public class LocationComponentOptions implements Parcelable {
|
||||
private Float trackingAnimationDurationMultiplier;
|
||||
private Boolean compassAnimationEnabled;
|
||||
private Boolean accuracyAnimationEnabled;
|
||||
private Boolean pulseEnabled;
|
||||
private Boolean pulseFadeEnabled;
|
||||
private int pulseColor;
|
||||
private float pulseSingleDuration;
|
||||
private float pulseMaxRadius;
|
||||
private float pulseAlpha;
|
||||
@Nullable
|
||||
private Interpolator pulseInterpolator;
|
||||
|
||||
Builder() {
|
||||
}
|
||||
@@ -1194,6 +1413,13 @@ public class LocationComponentOptions implements Parcelable {
|
||||
this.trackingAnimationDurationMultiplier = source.trackingAnimationDurationMultiplier();
|
||||
this.compassAnimationEnabled = source.compassAnimationEnabled();
|
||||
this.accuracyAnimationEnabled = source.accuracyAnimationEnabled();
|
||||
this.pulseEnabled = source.pulseEnabled;
|
||||
this.pulseFadeEnabled = source.pulseFadeEnabled;
|
||||
this.pulseColor = source.pulseColor;
|
||||
this.pulseSingleDuration = source.pulseSingleDuration;
|
||||
this.pulseMaxRadius = source.pulseMaxRadius;
|
||||
this.pulseAlpha = source.pulseAlpha;
|
||||
this.pulseInterpolator = source.pulseInterpolator;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1653,7 +1879,7 @@ public class LocationComponentOptions implements Parcelable {
|
||||
*/
|
||||
@NonNull
|
||||
public LocationComponentOptions.Builder trackingAnimationDurationMultiplier(
|
||||
float trackingAnimationDurationMultiplier) {
|
||||
float trackingAnimationDurationMultiplier) {
|
||||
this.trackingAnimationDurationMultiplier = trackingAnimationDurationMultiplier;
|
||||
return this;
|
||||
}
|
||||
@@ -1674,11 +1900,85 @@ 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 pulseEnabled(boolean pulseEnabled) {
|
||||
this.pulseEnabled = pulseEnabled;
|
||||
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 pulseFadeEnabled(boolean pulseFadeEnabled) {
|
||||
this.pulseFadeEnabled = pulseFadeEnabled;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color of the LocationComponent's pulsing circle.
|
||||
*
|
||||
* @return the current set color of the circle
|
||||
*/
|
||||
public LocationComponentOptions.Builder pulseColor(@ColorInt int pulseColor) {
|
||||
this.pulseColor = pulseColor;
|
||||
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 pulseSingleDuration(float pulseSingleDuration) {
|
||||
this.pulseSingleDuration = pulseSingleDuration;
|
||||
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 pulseMaxRadius(float pulseMaxRadius) {
|
||||
this.pulseMaxRadius = pulseMaxRadius;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the opacity of the LocationComponent's pulsing circle. The expected range is
|
||||
* 0 to 1. An opacity of 1 makes the layer fully visible.
|
||||
*
|
||||
* @return the current opacity of the LocationComponent's pulsing circle
|
||||
*/
|
||||
public LocationComponentOptions.Builder pulseAlpha(float pulseAlpha) {
|
||||
this.pulseAlpha = pulseAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pulsing circle's interpolator animation.
|
||||
*
|
||||
* @param pulseInterpolator the type of Android-system interpolator to use when
|
||||
* creating the pulsing animation
|
||||
* @return a String which represents the interpolator animation that the pulsing circle will use.
|
||||
*/
|
||||
public LocationComponentOptions.Builder pulseInterpolator(Interpolator pulseInterpolator) {
|
||||
this.pulseInterpolator = pulseInterpolator;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
LocationComponentOptions autoBuild() {
|
||||
String missing = "";
|
||||
@@ -1740,39 +2040,46 @@ public class LocationComponentOptions implements Parcelable {
|
||||
throw new IllegalStateException("Missing required properties:" + missing);
|
||||
}
|
||||
return new LocationComponentOptions(
|
||||
this.accuracyAlpha,
|
||||
this.accuracyColor,
|
||||
this.backgroundDrawableStale,
|
||||
this.backgroundStaleName,
|
||||
this.foregroundDrawableStale,
|
||||
this.foregroundStaleName,
|
||||
this.gpsDrawable,
|
||||
this.gpsName,
|
||||
this.foregroundDrawable,
|
||||
this.foregroundName,
|
||||
this.backgroundDrawable,
|
||||
this.backgroundName,
|
||||
this.bearingDrawable,
|
||||
this.bearingName,
|
||||
this.bearingTintColor,
|
||||
this.foregroundTintColor,
|
||||
this.backgroundTintColor,
|
||||
this.foregroundStaleTintColor,
|
||||
this.backgroundStaleTintColor,
|
||||
this.elevation,
|
||||
this.enableStaleState,
|
||||
this.staleStateTimeout,
|
||||
this.padding,
|
||||
this.maxZoomIconScale,
|
||||
this.minZoomIconScale,
|
||||
trackingGesturesManagement,
|
||||
this.trackingInitialMoveThreshold,
|
||||
this.trackingMultiFingerMoveThreshold,
|
||||
this.layerAbove,
|
||||
this.layerBelow,
|
||||
this.trackingAnimationDurationMultiplier,
|
||||
this.compassAnimationEnabled,
|
||||
this.accuracyAnimationEnabled);
|
||||
this.accuracyAlpha,
|
||||
this.accuracyColor,
|
||||
this.backgroundDrawableStale,
|
||||
this.backgroundStaleName,
|
||||
this.foregroundDrawableStale,
|
||||
this.foregroundStaleName,
|
||||
this.gpsDrawable,
|
||||
this.gpsName,
|
||||
this.foregroundDrawable,
|
||||
this.foregroundName,
|
||||
this.backgroundDrawable,
|
||||
this.backgroundName,
|
||||
this.bearingDrawable,
|
||||
this.bearingName,
|
||||
this.bearingTintColor,
|
||||
this.foregroundTintColor,
|
||||
this.backgroundTintColor,
|
||||
this.foregroundStaleTintColor,
|
||||
this.backgroundStaleTintColor,
|
||||
this.elevation,
|
||||
this.enableStaleState,
|
||||
this.staleStateTimeout,
|
||||
this.padding,
|
||||
this.maxZoomIconScale,
|
||||
this.minZoomIconScale,
|
||||
trackingGesturesManagement,
|
||||
this.trackingInitialMoveThreshold,
|
||||
this.trackingMultiFingerMoveThreshold,
|
||||
this.layerAbove,
|
||||
this.layerBelow,
|
||||
this.trackingAnimationDurationMultiplier,
|
||||
this.compassAnimationEnabled,
|
||||
this.accuracyAnimationEnabled,
|
||||
this.pulseEnabled,
|
||||
this.pulseFadeEnabled,
|
||||
this.pulseColor,
|
||||
this.pulseSingleDuration,
|
||||
this.pulseMaxRadius,
|
||||
this.pulseAlpha,
|
||||
this.pulseInterpolator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ final class LocationLayerController {
|
||||
styleBitmaps(options);
|
||||
locationLayerRenderer.styleAccuracy(options.accuracyAlpha(), options.accuracyColor());
|
||||
styleScaling(options);
|
||||
locationLayerRenderer.stylePulsingCircle(options);
|
||||
determineIconsSource(options);
|
||||
|
||||
if (!isHidden) {
|
||||
@@ -258,7 +259,7 @@ final class LocationLayerController {
|
||||
public void onNewAnimationValue(LatLng value) {
|
||||
locationLayerRenderer.setLatLng(value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> gpsBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@@ -266,7 +267,7 @@ final class LocationLayerController {
|
||||
public void onNewAnimationValue(Float value) {
|
||||
locationLayerRenderer.setGpsBearing(value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> compassBearingValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@@ -274,7 +275,7 @@ final class LocationLayerController {
|
||||
public void onNewAnimationValue(Float value) {
|
||||
locationLayerRenderer.setCompassBearing(value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
private final MapboxAnimator.AnimationsValueChangeListener<Float> accuracyValueListener =
|
||||
new MapboxAnimator.AnimationsValueChangeListener<Float>() {
|
||||
@@ -282,7 +283,22 @@ final class LocationLayerController {
|
||||
public void onNewAnimationValue(Float value) {
|
||||
locationLayerRenderer.setAccuracyRadius(value);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 newPulseRadiusValue) {
|
||||
Float newPulseOpacityValue = null;
|
||||
if (options.pulseFadeEnabled()) {
|
||||
newPulseOpacityValue = (float) 1 - ((newPulseRadiusValue / 100) * 3);
|
||||
}
|
||||
locationLayerRenderer.updatePulsingUi(newPulseRadiusValue, newPulseOpacityValue);
|
||||
}
|
||||
};
|
||||
|
||||
Set<AnimatorListenerHolder> getAnimationListeners() {
|
||||
Set<AnimatorListenerHolder> holders = new HashSet<>();
|
||||
@@ -299,6 +315,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;
|
||||
}
|
||||
|
||||
@@ -311,4 +331,8 @@ final class LocationLayerController {
|
||||
void cameraTiltUpdated(double tilt) {
|
||||
locationLayerRenderer.cameraTiltUpdated(tilt);
|
||||
}
|
||||
|
||||
void adjustPulsingCircleLayerVisibility(boolean visible) {
|
||||
locationLayerRenderer.adjustPulsingCircleLayerVisibility(visible);
|
||||
}
|
||||
}
|
||||
@@ -38,6 +38,12 @@ interface LocationLayerRenderer {
|
||||
|
||||
void setLocationStale(boolean isStale, int renderMode);
|
||||
|
||||
void adjustPulsingCircleLayerVisibility(boolean visible);
|
||||
|
||||
void stylePulsingCircle(LocationComponentOptions options);
|
||||
|
||||
void updatePulsingUi(float radius, @Nullable Float opacity);
|
||||
|
||||
void updateIconIds(String foregroundIconString, String foregroundStaleIconString, String backgroundIconString,
|
||||
String backgroundStaleIconString, String bearingIconString);
|
||||
|
||||
|
||||
@@ -28,7 +28,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 {
|
||||
}
|
||||
@@ -42,6 +43,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;
|
||||
@@ -68,6 +70,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();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
@@ -35,4 +38,31 @@ final class MapboxAnimatorProvider {
|
||||
@Nullable MapboxMap.CancelableCallback cancelableCallback) {
|
||||
return new MapboxCameraAnimatorAdapter(values, 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 pulseInterpolator 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,
|
||||
Interpolator pulseInterpolator) {
|
||||
PulsingLocationCircleAnimator pulsingLocationCircleAnimator =
|
||||
new PulsingLocationCircleAnimator(updateListener, maxAnimationFps, pulseMaxRadius);
|
||||
pulsingLocationCircleAnimator.setDuration((long) pulseSingleDuration);
|
||||
pulsingLocationCircleAnimator.setRepeatMode(ValueAnimator.RESTART);
|
||||
pulsingLocationCircleAnimator.setRepeatCount(ValueAnimator.INFINITE);
|
||||
pulsingLocationCircleAnimator.setInterpolator(pulseInterpolator);
|
||||
return pulsingLocationCircleAnimator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ package com.mapbox.mapboxsdk.location;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class MapboxAnimatorSetProvider {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mapbox.mapboxsdk.location;
|
||||
|
||||
/**
|
||||
* 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(new Float[]{0f, circleMaxRadius}, updateListener, maxAnimationFps);
|
||||
}
|
||||
}
|
||||
@@ -41,11 +41,19 @@ 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.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.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;
|
||||
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;
|
||||
@@ -84,6 +92,7 @@ final class SymbolLocationLayerRenderer implements LocationLayerRenderer {
|
||||
addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER);
|
||||
addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER);
|
||||
addAccuracyLayer();
|
||||
addPulsingCircleLayerToMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -244,6 +253,42 @@ final class SymbolLocationLayerRenderer implements LocationLayerRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the visibility of the pulsing LocationComponent circle.
|
||||
*/
|
||||
@Override
|
||||
public void adjustPulsingCircleLayerVisibility(boolean visible) {
|
||||
setLayerVisibility(PULSING_CIRCLE_LAYER, visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the the pulsing LocationComponent circle based on the set options.
|
||||
*/
|
||||
@Override
|
||||
public 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))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust the visual appearance of the pulsing LocationComponent circle.
|
||||
*/
|
||||
@Override
|
||||
public void updatePulsingUi(float radius, @Nullable Float opacity) {
|
||||
locationFeature.addNumberProperty(PROPERTY_PULSING_RADIUS, radius);
|
||||
if (opacity != null) {
|
||||
locationFeature.addNumberProperty(PROPERTY_PULSING_OPACITY, opacity);
|
||||
}
|
||||
refreshSource();
|
||||
}
|
||||
|
||||
private void addSymbolLayer(@NonNull String layerId, @NonNull String beforeLayerId) {
|
||||
Layer layer = layerSourceProvider.generateLayer(layerId);
|
||||
addLayerToMap(layer, beforeLayerId);
|
||||
@@ -254,6 +299,14 @@ final class SymbolLocationLayerRenderer implements LocationLayerRenderer {
|
||||
addLayerToMap(accuracyLayer, BACKGROUND_LAYER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the pulsing LocationComponent circle to the map for future use, if need be.
|
||||
*/
|
||||
private void addPulsingCircleLayerToMap() {
|
||||
Layer pulsingCircleLayer = layerSourceProvider.generatePulsingCircleLayer();
|
||||
addLayerToMap(pulsingCircleLayer, ACCURACY_LAYER);
|
||||
}
|
||||
|
||||
private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) {
|
||||
style.addLayerBelow(layer, idBelowLayer);
|
||||
layerSet.add(layer.getId());
|
||||
|
||||
@@ -165,4 +165,13 @@
|
||||
<public name="mapbox_layer_above" format="string" type="attr" />
|
||||
<public name="mapbox_layer_below" format="string" type="attr" />
|
||||
|
||||
<!-- Pulsing circle -->
|
||||
<public name="mapbox_pulsingLocationCircleEnabled" format="boolean" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleFadeEnabled" format="boolean" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleColor" format="color" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleDuration" format="float" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleRadius" format="float" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleAlpha" format="float" type="attr" />
|
||||
<public name="mapbox_pulsingLocationCircleInterpolator" format="string" type="attr" />
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -188,5 +188,14 @@
|
||||
<!-- Accuracy animation-->
|
||||
<attr name="mapbox_accuracyAnimationEnabled" format="boolean" />
|
||||
|
||||
<!-- Pulsing circle -->
|
||||
<attr name="mapbox_pulsingLocationCircleEnabled" format="boolean" />
|
||||
<attr name="mapbox_pulsingLocationCircleFadeEnabled" format="boolean" />
|
||||
<attr name="mapbox_pulsingLocationCircleColor" format="color" />
|
||||
<attr name="mapbox_pulsingLocationCircleDuration" format="float" />
|
||||
<attr name="mapbox_pulsingLocationCircleRadius" format="float" />
|
||||
<attr name="mapbox_pulsingLocationCircleAlpha" format="float" />
|
||||
<attr name="mapbox_pulsingLocationCircleInterpolator" format="string" />
|
||||
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -33,5 +33,14 @@
|
||||
<item name="mapbox_trackingInitialMoveThreshold">@dimen/mapbox_locationComponentTrackingInitialMoveThreshold</item>
|
||||
<item name="mapbox_trackingMultiFingerMoveThreshold">@dimen/mapbox_locationComponentTrackingMultiFingerMoveThreshold</item>
|
||||
|
||||
<!-- Location pulsing circle -->
|
||||
<item name="mapbox_pulsingLocationCircleEnabled">false</item>
|
||||
<item name="mapbox_pulsingLocationCircleFadeEnabled">true</item>
|
||||
<item name="mapbox_pulsingLocationCircleColor">@color/mapbox_location_layer_blue</item>
|
||||
<item name="mapbox_pulsingLocationCircleDuration">2300</item>
|
||||
<item name="mapbox_pulsingLocationCircleRadius">35</item>
|
||||
<item name="mapbox_pulsingLocationCircleAlpha">0.4</item>
|
||||
<item name="mapbox_pulsingLocationCircleInterpolator">decelerate</item>
|
||||
|
||||
</style>
|
||||
</resources>
|
||||
@@ -35,7 +35,9 @@ class LocationAnimatorCoordinatorTest {
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
locationAnimatorCoordinator = LocationAnimatorCoordinator(projection, animatorSetProvider, animatorProvider)
|
||||
locationAnimatorCoordinator = LocationAnimatorCoordinator(projection,
|
||||
animatorSetProvider,
|
||||
animatorProvider)
|
||||
configureAnimatorProvider()
|
||||
every { projection.getMetersPerPixelAtLatitude(any()) } answers { 1.0 }
|
||||
every { animatorSetProvider.startAnimation(any(), any(), any()) } answers {}
|
||||
|
||||
@@ -3,6 +3,9 @@ package com.mapbox.mapboxsdk.location;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.mapbox.mapboxsdk.R;
|
||||
@@ -66,6 +69,44 @@ public class LocationComponentActivationOptionsTest {
|
||||
assertNotNull(locationComponentActivationOptions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanityWithDefaultPulsingCircle() throws Exception {
|
||||
when(style.isFullyLoaded()).thenReturn(true);
|
||||
|
||||
LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context)
|
||||
.accuracyAlpha(0.5f)
|
||||
.pulseEnabled(true)
|
||||
.build();
|
||||
assertNotNull(locationComponentOptions);
|
||||
|
||||
LocationComponentActivationOptions locationComponentActivationOptions =
|
||||
LocationComponentActivationOptions.builder(context, style)
|
||||
.locationComponentOptions(locationComponentOptions)
|
||||
.useDefaultLocationEngine(true)
|
||||
.build();
|
||||
assertNotNull(locationComponentActivationOptions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sanityWithCustomizedPulsingCircle() throws Exception {
|
||||
when(style.isFullyLoaded()).thenReturn(true);
|
||||
|
||||
LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context)
|
||||
.accuracyAlpha(0.5f)
|
||||
.pulseEnabled(true)
|
||||
.pulseColor(Color.RED)
|
||||
.pulseInterpolator(new LinearInterpolator())
|
||||
.build();
|
||||
assertNotNull(locationComponentOptions);
|
||||
|
||||
LocationComponentActivationOptions locationComponentActivationOptions =
|
||||
LocationComponentActivationOptions.builder(context, style)
|
||||
.locationComponentOptions(locationComponentOptions)
|
||||
.useDefaultLocationEngine(true)
|
||||
.build();
|
||||
assertNotNull(locationComponentActivationOptions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void includingBothStyleResAndComponentOptions_causesExceptionToBeThrown() throws Exception {
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class LocationComponentOptionsTest {
|
||||
public void passingBothLayerPositionOptions_throwsException() throws Exception {
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("You cannot set both layerAbove and layerBelow options."
|
||||
+ "Choose one or the other.");
|
||||
+ " Choose one or the other.");
|
||||
LocationComponentOptions.builder(context)
|
||||
.layerAbove("above")
|
||||
.layerBelow("below")
|
||||
|
||||
@@ -37,6 +37,7 @@ import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET;
|
||||
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING;
|
||||
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.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY;
|
||||
@@ -167,6 +168,23 @@ public class LocationLayerControllerTest {
|
||||
verify(style).addLayerBelow(accuracyLayer, BACKGROUND_LAYER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInitialization_pulsingCircleLayerIsAdded() {
|
||||
OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class);
|
||||
LayerSourceProvider sourceProvider = buildLayerProvider();
|
||||
Layer pulsingCircleLayer = mock(Layer.class);
|
||||
when(sourceProvider.generatePulsingCircleLayer()).thenReturn(pulsingCircleLayer);
|
||||
GeoJsonSource locationSource = mock(GeoJsonSource.class);
|
||||
when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource);
|
||||
LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class);
|
||||
LocationComponentOptions options = mock(LocationComponentOptions.class);
|
||||
|
||||
new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options),
|
||||
bitmapProvider, options, internalRenderModeChangedListener, false);
|
||||
|
||||
verify(style).addLayerBelow(pulsingCircleLayer, ACCURACY_LAYER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onInitialization_numberOfCachedLayerIdsIsConstant() {
|
||||
OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class);
|
||||
@@ -184,7 +202,7 @@ public class LocationLayerControllerTest {
|
||||
|
||||
controller.initializeComponents(mapboxMap.getStyle(), options);
|
||||
|
||||
assertEquals(5, layerSet.size());
|
||||
assertEquals(6, layerSet.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -402,7 +420,7 @@ public class LocationLayerControllerTest {
|
||||
layerController.applyStyle(options);
|
||||
|
||||
verify(style, times(0)).removeLayer(any(String.class));
|
||||
verify(style, times(5)).addLayerBelow(any(Layer.class), any(String.class));
|
||||
verify(style, times(6)).addLayerBelow(any(Layer.class), any(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -424,7 +442,7 @@ public class LocationLayerControllerTest {
|
||||
|
||||
verify(style, times(0)).removeLayer(any(String.class));
|
||||
verify(style, times(1)).addLayer(any(Layer.class));
|
||||
verify(style, times(4)).addLayerBelow(any(Layer.class), Mockito.<String>any());
|
||||
verify(style, times(5)).addLayerBelow(any(Layer.class), Mockito.<String>any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -726,6 +744,9 @@ public class LocationLayerControllerTest {
|
||||
Layer accuracyLayer = mock(Layer.class);
|
||||
when(accuracyLayer.getId()).thenReturn(ACCURACY_LAYER);
|
||||
when(layerSourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer);
|
||||
Layer pulsingCircleLayer = mock(Layer.class);
|
||||
when(pulsingCircleLayer.getId()).thenReturn(PULSING_CIRCLE_LAYER);
|
||||
when(layerSourceProvider.generatePulsingCircleLayer()).thenReturn(pulsingCircleLayer);
|
||||
return layerSourceProvider;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.mapbox.mapboxsdk.location
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.location.Location
|
||||
import androidx.test.annotation.UiThreadTest
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
@@ -123,11 +124,49 @@ class LocationLayerControllerTest : EspressoTest() {
|
||||
assertThat(mapboxMap.isLayerVisible(SHADOW_LAYER), `is`(true))
|
||||
assertThat(mapboxMap.isLayerVisible(ACCURACY_LAYER), `is`(true))
|
||||
assertThat(mapboxMap.isLayerVisible(BEARING_LAYER), `is`(false))
|
||||
assertThat(mapboxMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(false))
|
||||
}
|
||||
}
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onMapChange_locationComponentPulsingCircleLayerGetsRedrawn() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
override fun onLocationComponentAction(
|
||||
component: LocationComponent,
|
||||
mapboxMap: MapboxMap,
|
||||
style: Style,
|
||||
uiController: UiController,
|
||||
context: Context
|
||||
) {
|
||||
component.activateLocationComponent(
|
||||
LocationComponentActivationOptions
|
||||
.builder(context, style)
|
||||
.locationComponentOptions(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true)
|
||||
.build())
|
||||
.useDefaultLocationEngine(false)
|
||||
.build()
|
||||
)
|
||||
component.isLocationComponentEnabled = true
|
||||
component.renderMode = RenderMode.NORMAL
|
||||
component.forceLocationUpdate(location)
|
||||
styleChangeIdlingResource.waitForStyle(mapboxMap, Style.LIGHT)
|
||||
TestingAsyncUtils.waitForLayer(uiController, mapView)
|
||||
|
||||
assertThat(component.renderMode, `is`(equalTo(RenderMode.NORMAL)))
|
||||
|
||||
// Check that the Source has been re-added to the new map style
|
||||
val source: GeoJsonSource? = mapboxMap.style!!.getSourceAs(LOCATION_SOURCE)
|
||||
assertThat(source, notNullValue())
|
||||
|
||||
// Check that the pulsing circle layer visibilities is set to visible
|
||||
assertThat(mapboxMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renderModeCompass_bearingLayersDoGetAdded() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
@@ -159,6 +198,137 @@ class LocationLayerControllerTest : EspressoTest() {
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pulsingCircle_enableLocationComponent_pulsingLayerVisibility() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
override fun onLocationComponentAction(
|
||||
component: LocationComponent,
|
||||
mapboxMap: MapboxMap,
|
||||
style: Style,
|
||||
uiController: UiController,
|
||||
context: Context
|
||||
) {
|
||||
component.activateLocationComponent(
|
||||
LocationComponentActivationOptions
|
||||
.builder(context, style)
|
||||
.useDefaultLocationEngine(false)
|
||||
.build()
|
||||
)
|
||||
component.isLocationComponentEnabled = true
|
||||
component.forceLocationUpdate(location)
|
||||
TestingAsyncUtils.waitForLayer(uiController, mapView)
|
||||
|
||||
component.applyStyle(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true).build())
|
||||
|
||||
assertThat(mapboxMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(true))
|
||||
}
|
||||
}
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pulsingCircle_disableLocationComponent_pulsingLayerVisibility() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
override fun onLocationComponentAction(
|
||||
component: LocationComponent,
|
||||
mapboxMap: MapboxMap,
|
||||
style: Style,
|
||||
uiController: UiController,
|
||||
context: Context
|
||||
) {
|
||||
component.activateLocationComponent(
|
||||
LocationComponentActivationOptions
|
||||
.builder(context, style)
|
||||
.useDefaultLocationEngine(false)
|
||||
.build()
|
||||
)
|
||||
component.isLocationComponentEnabled = false
|
||||
component.forceLocationUpdate(location)
|
||||
TestingAsyncUtils.waitForLayer(uiController, mapView)
|
||||
|
||||
assertThat(mapboxMap.isLayerVisible(PULSING_CIRCLE_LAYER), `is`(false))
|
||||
}
|
||||
}
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pulsingCircle_changeColorCheck() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
override fun onLocationComponentAction(
|
||||
component: LocationComponent,
|
||||
mapboxMap: MapboxMap,
|
||||
style: Style,
|
||||
uiController: UiController,
|
||||
context: Context
|
||||
) {
|
||||
component.activateLocationComponent(
|
||||
LocationComponentActivationOptions
|
||||
.builder(context, style)
|
||||
.useDefaultLocationEngine(false)
|
||||
.build()
|
||||
)
|
||||
component.isLocationComponentEnabled = true
|
||||
component.forceLocationUpdate(location)
|
||||
TestingAsyncUtils.waitForLayer(uiController, mapView)
|
||||
|
||||
component.applyStyle(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true)
|
||||
.pulseColor(Color.RED)
|
||||
.build())
|
||||
|
||||
component.applyStyle(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true)
|
||||
.pulseColor(Color.BLUE)
|
||||
.build())
|
||||
|
||||
mapboxMap.style.apply {
|
||||
assertThat(component.locationComponentOptions.pulseColor(), `is`(Color.BLUE))
|
||||
}
|
||||
}
|
||||
}
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pulsingCircle_changeSpeedCheck() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
override fun onLocationComponentAction(
|
||||
component: LocationComponent,
|
||||
mapboxMap: MapboxMap,
|
||||
style: Style,
|
||||
uiController: UiController,
|
||||
context: Context
|
||||
) {
|
||||
component.activateLocationComponent(
|
||||
LocationComponentActivationOptions
|
||||
.builder(context, style)
|
||||
.useDefaultLocationEngine(false)
|
||||
.build()
|
||||
)
|
||||
component.isLocationComponentEnabled = true
|
||||
component.forceLocationUpdate(location)
|
||||
TestingAsyncUtils.waitForLayer(uiController, mapView)
|
||||
|
||||
component.applyStyle(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true)
|
||||
.pulseSingleDuration(8000f)
|
||||
.build())
|
||||
|
||||
component.applyStyle(LocationComponentOptions.builder(context)
|
||||
.pulseEnabled(true)
|
||||
.pulseSingleDuration(400f)
|
||||
.build())
|
||||
|
||||
mapboxMap.style.apply {
|
||||
assertThat(component.locationComponentOptions.pulseSingleDuration(), `is`(400f))
|
||||
}
|
||||
}
|
||||
}
|
||||
executeComponentTest(componentAction)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renderModeGps_navigationLayersDoGetAdded() {
|
||||
val componentAction = object : LocationComponentAction.OnPerformLocationComponentAction {
|
||||
|
||||
@@ -33,7 +33,7 @@ class StyleChangeIdlingResource : IdlingResource {
|
||||
|
||||
fun waitForStyle(mapboxMap: MapboxMap, styleUrl: String) {
|
||||
isIdle = false
|
||||
mapboxMap.setStyle(Style.Builder().fromUrl(styleUrl)) {
|
||||
mapboxMap.setStyle(Style.Builder().fromUri(styleUrl)) {
|
||||
setIdle()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1015,6 +1015,28 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.location.BasicLocationPulsingCircleActivity"
|
||||
android:description="@string/description_location_basic_pulsing_circle"
|
||||
android:label="@string/activity_basic_location_pulsing_circle">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_location" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.location.CustomizedLocationPulsingCircleActivity"
|
||||
android:description="@string/description_location_customized_pulsing_circle"
|
||||
android:label="@string/activity_customized_location_pulsing_circle">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_location" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.location.LocationFragmentActivity"
|
||||
android:description="@string/description_location_fragment"
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.location;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mapbox.android.core.location.LocationEngineRequest;
|
||||
import com.mapbox.android.core.permissions.PermissionsListener;
|
||||
import com.mapbox.android.core.permissions.PermissionsManager;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponent;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentOptions;
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
import com.mapbox.mapboxsdk.testapp.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import static com.mapbox.mapboxsdk.testapp.activity.location.Utils.getNextStyle;
|
||||
|
||||
/**
|
||||
* This activity shows a basic usage of the LocationComponent's pulsing circle. There's no
|
||||
* customization of the pulsing circle's color, radius, speed, etc.
|
||||
*/
|
||||
public class BasicLocationPulsingCircleActivity extends AppCompatActivity implements OnMapReadyCallback {
|
||||
|
||||
private static final String SAVED_STATE_LOCATION = "saved_state_location";
|
||||
private static final String TAG = "Mbgl-BasicLocationPulsingCircleActivity";
|
||||
|
||||
private Location lastLocation;
|
||||
private MapView mapView;
|
||||
private PermissionsManager permissionsManager;
|
||||
private LocationComponent locationComponent;
|
||||
private MapboxMap mapboxMap;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_location_layer_basic_pulsing_circle);
|
||||
|
||||
mapView = findViewById(R.id.mapView);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
lastLocation = savedInstanceState.getParcelable(SAVED_STATE_LOCATION);
|
||||
}
|
||||
mapView.onCreate(savedInstanceState);
|
||||
|
||||
checkPermissions();
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public void onMapReady(@NonNull MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
|
||||
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
|
||||
locationComponent = mapboxMap.getLocationComponent();
|
||||
|
||||
LocationComponentOptions locationComponentOptions =
|
||||
LocationComponentOptions.builder(BasicLocationPulsingCircleActivity.this)
|
||||
.pulseEnabled(true)
|
||||
.build();
|
||||
|
||||
LocationComponentActivationOptions locationComponentActivationOptions =
|
||||
buildLocationComponentActivationOptions(style,locationComponentOptions);
|
||||
|
||||
locationComponent.activateLocationComponent(locationComponentActivationOptions);
|
||||
locationComponent.setLocationComponentEnabled(true);
|
||||
locationComponent.setCameraMode(CameraMode.TRACKING);
|
||||
locationComponent.forceLocationUpdate(lastLocation);
|
||||
});
|
||||
}
|
||||
|
||||
private LocationComponentActivationOptions buildLocationComponentActivationOptions(@NonNull Style style,
|
||||
@NonNull LocationComponentOptions
|
||||
locationComponentOptions) {
|
||||
return LocationComponentActivationOptions
|
||||
.builder(this, style)
|
||||
.locationComponentOptions(locationComponentOptions)
|
||||
.useDefaultLocationEngine(true)
|
||||
.locationEngineRequest(new LocationEngineRequest.Builder(750)
|
||||
.setFastestInterval(750)
|
||||
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_pulsing_location_mode, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (locationComponent == null) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.action_map_style_change) {
|
||||
loadNewStyle();
|
||||
return true;
|
||||
} else if (id == R.id.action_component_disable) {
|
||||
locationComponent.setLocationComponentEnabled(false);
|
||||
return true;
|
||||
} else if (id == R.id.action_component_enabled) {
|
||||
locationComponent.setLocationComponentEnabled(true);
|
||||
return true;
|
||||
} else if (id == R.id.action_stop_pulsing) {
|
||||
locationComponent.applyStyle(LocationComponentOptions.builder(
|
||||
BasicLocationPulsingCircleActivity.this)
|
||||
.pulseEnabled(false)
|
||||
.build());
|
||||
return true;
|
||||
} else if (id == R.id.action_start_pulsing) {
|
||||
locationComponent.applyStyle(LocationComponentOptions.builder(
|
||||
BasicLocationPulsingCircleActivity.this)
|
||||
.pulseEnabled(true)
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void loadNewStyle() {
|
||||
mapboxMap.setStyle(new Style.Builder().fromUri(getNextStyle()));
|
||||
}
|
||||
|
||||
private void checkPermissions() {
|
||||
if (PermissionsManager.areLocationPermissionsGranted(this)) {
|
||||
mapView.getMapAsync(this);
|
||||
} else {
|
||||
permissionsManager = new PermissionsManager(new PermissionsListener() {
|
||||
@Override
|
||||
public void onExplanationNeeded(List<String> permissionsToExplain) {
|
||||
Toast.makeText(BasicLocationPulsingCircleActivity.this, "You need to accept location permissions.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionResult(boolean granted) {
|
||||
if (granted) {
|
||||
mapView.getMapAsync(BasicLocationPulsingCircleActivity.this);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
permissionsManager.requestLocationPermissions(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mapView.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mapView.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mapView.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mapView.onStop();
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
mapView.onSaveInstanceState(outState);
|
||||
if (locationComponent != null) {
|
||||
outState.putParcelable(SAVED_STATE_LOCATION, locationComponent.getLastKnownLocation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mapView.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
mapView.onLowMemory();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package com.mapbox.mapboxsdk.testapp.activity.location;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Color;
|
||||
import android.location.Location;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.mapbox.android.core.location.LocationEngineRequest;
|
||||
import com.mapbox.android.core.permissions.PermissionsListener;
|
||||
import com.mapbox.android.core.permissions.PermissionsManager;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponent;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentActivationOptions;
|
||||
import com.mapbox.mapboxsdk.location.LocationComponentOptions;
|
||||
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
import com.mapbox.mapboxsdk.maps.Style;
|
||||
import com.mapbox.mapboxsdk.testapp.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.ListPopupWindow;
|
||||
|
||||
import static com.mapbox.mapboxsdk.testapp.activity.location.Utils.getNextStyle;
|
||||
|
||||
/**
|
||||
* This activity shows how to customize the LocationComponent's pulsing circle.
|
||||
*/
|
||||
public class CustomizedLocationPulsingCircleActivity extends AppCompatActivity implements OnMapReadyCallback {
|
||||
|
||||
//region
|
||||
|
||||
// Adjust these variables to customize the example's pulsing circle UI
|
||||
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS = 2300;
|
||||
private static final float SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS = 800;
|
||||
private static final float THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS = 8000;
|
||||
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_RADIUS = 35;
|
||||
private static final float DEFAULT_LOCATION_CIRCLE_PULSE_ALPHA = .55f;
|
||||
private static final Interpolator DEFAULT_LOCATION_CIRCLE_INTERPOLATOR_PULSE_MODE
|
||||
= new DecelerateInterpolator();
|
||||
private static final boolean DEFAULT_LOCATION_CIRCLE_PULSE_FADE_MODE = true;
|
||||
//endregion
|
||||
|
||||
//region
|
||||
private static int LOCATION_CIRCLE_PULSE_COLOR;
|
||||
private static float LOCATION_CIRCLE_PULSE_DURATION = DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS;
|
||||
private static final String SAVED_STATE_LOCATION = "saved_state_location";
|
||||
private static final String SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR = "saved_state_color";
|
||||
private static final String SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION = "saved_state_duration";
|
||||
private static final String LAYER_BELOW_ID = "waterway-label";
|
||||
|
||||
private Location lastLocation;
|
||||
private MapView mapView;
|
||||
private Button pulsingCircleDurationButton;
|
||||
private Button pulsingCircleColorButton;
|
||||
private PermissionsManager permissionsManager;
|
||||
private LocationComponent locationComponent;
|
||||
private MapboxMap mapboxMap;
|
||||
private float currentPulseDuration;
|
||||
//endregion
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_location_layer_customized_pulsing_circle);
|
||||
|
||||
LOCATION_CIRCLE_PULSE_COLOR = Color.BLUE;
|
||||
|
||||
mapView = findViewById(R.id.mapView);
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
lastLocation = savedInstanceState.getParcelable(SAVED_STATE_LOCATION);
|
||||
LOCATION_CIRCLE_PULSE_COLOR = savedInstanceState.getInt(SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR);
|
||||
LOCATION_CIRCLE_PULSE_DURATION = savedInstanceState.getFloat(SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION);
|
||||
}
|
||||
|
||||
pulsingCircleDurationButton = findViewById(R.id.button_location_circle_duration);
|
||||
pulsingCircleDurationButton.setText(String.format("%sms",
|
||||
String.valueOf(LOCATION_CIRCLE_PULSE_DURATION)));
|
||||
pulsingCircleDurationButton.setOnClickListener(v -> {
|
||||
if (locationComponent == null) {
|
||||
return;
|
||||
}
|
||||
showDurationListDialog();
|
||||
});
|
||||
|
||||
pulsingCircleColorButton = findViewById(R.id.button_location_circle_color);
|
||||
pulsingCircleColorButton.setOnClickListener(v -> {
|
||||
if (locationComponent == null) {
|
||||
return;
|
||||
}
|
||||
showColorListDialog();
|
||||
});
|
||||
|
||||
mapView.onCreate(savedInstanceState);
|
||||
|
||||
checkPermissions();
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public void onMapReady(@NonNull MapboxMap mapboxMap) {
|
||||
this.mapboxMap = mapboxMap;
|
||||
|
||||
mapboxMap.setStyle(Style.MAPBOX_STREETS, style -> {
|
||||
locationComponent = mapboxMap.getLocationComponent();
|
||||
|
||||
LocationComponentOptions locationComponentOptions =
|
||||
buildLocationComponentOptions(
|
||||
LOCATION_CIRCLE_PULSE_COLOR,
|
||||
LOCATION_CIRCLE_PULSE_DURATION)
|
||||
.pulseEnabled(true)
|
||||
.build();
|
||||
|
||||
LocationComponentActivationOptions locationComponentActivationOptions =
|
||||
buildLocationComponentActivationOptions(style,locationComponentOptions);
|
||||
|
||||
locationComponent.activateLocationComponent(locationComponentActivationOptions);
|
||||
locationComponent.setLocationComponentEnabled(true);
|
||||
locationComponent.setCameraMode(CameraMode.TRACKING);
|
||||
locationComponent.forceLocationUpdate(lastLocation);
|
||||
});
|
||||
}
|
||||
|
||||
private LocationComponentOptions.Builder buildLocationComponentOptions(int pulsingCircleColor,
|
||||
float pulsingCircleDuration
|
||||
) {
|
||||
currentPulseDuration = pulsingCircleDuration;
|
||||
return LocationComponentOptions.builder(this)
|
||||
.layerBelow(LAYER_BELOW_ID)
|
||||
.pulseFadeEnabled(DEFAULT_LOCATION_CIRCLE_PULSE_FADE_MODE)
|
||||
.pulseInterpolator(DEFAULT_LOCATION_CIRCLE_INTERPOLATOR_PULSE_MODE)
|
||||
.pulseColor(pulsingCircleColor)
|
||||
.pulseAlpha(DEFAULT_LOCATION_CIRCLE_PULSE_ALPHA)
|
||||
.pulseSingleDuration(pulsingCircleDuration)
|
||||
.pulseMaxRadius(DEFAULT_LOCATION_CIRCLE_PULSE_RADIUS);
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private void setNewLocationComponentOptions(float newPulsingDuration,
|
||||
int newPulsingColor) {
|
||||
mapboxMap.getStyle(style -> locationComponent.applyStyle(
|
||||
buildLocationComponentOptions(
|
||||
newPulsingColor,
|
||||
newPulsingDuration)
|
||||
.pulseEnabled(true)
|
||||
.build()));
|
||||
}
|
||||
|
||||
private LocationComponentActivationOptions buildLocationComponentActivationOptions(
|
||||
@NonNull Style style,
|
||||
@NonNull LocationComponentOptions locationComponentOptions) {
|
||||
return LocationComponentActivationOptions
|
||||
.builder(this, style)
|
||||
.locationComponentOptions(locationComponentOptions)
|
||||
.useDefaultLocationEngine(true)
|
||||
.locationEngineRequest(new LocationEngineRequest.Builder(750)
|
||||
.setFastestInterval(750)
|
||||
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.menu_pulsing_location_mode, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (locationComponent == null) {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
int id = item.getItemId();
|
||||
if (id == R.id.action_map_style_change) {
|
||||
loadNewStyle();
|
||||
return true;
|
||||
} else if (id == R.id.action_component_disable) {
|
||||
locationComponent.setLocationComponentEnabled(false);
|
||||
return true;
|
||||
} else if (id == R.id.action_component_enabled) {
|
||||
locationComponent.setLocationComponentEnabled(true);
|
||||
return true;
|
||||
} else if (id == R.id.action_stop_pulsing) {
|
||||
locationComponent.applyStyle(LocationComponentOptions.builder(
|
||||
CustomizedLocationPulsingCircleActivity.this)
|
||||
.pulseEnabled(false)
|
||||
.build());
|
||||
return true;
|
||||
} else if (id == R.id.action_start_pulsing) {
|
||||
locationComponent.applyStyle(buildLocationComponentOptions(
|
||||
LOCATION_CIRCLE_PULSE_COLOR,
|
||||
LOCATION_CIRCLE_PULSE_DURATION)
|
||||
.pulseEnabled(true)
|
||||
.build());
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void loadNewStyle() {
|
||||
mapboxMap.setStyle(new Style.Builder().fromUri(getNextStyle()));
|
||||
}
|
||||
|
||||
private void checkPermissions() {
|
||||
if (PermissionsManager.areLocationPermissionsGranted(this)) {
|
||||
mapView.getMapAsync(this);
|
||||
} else {
|
||||
permissionsManager = new PermissionsManager(new PermissionsListener() {
|
||||
@Override
|
||||
public void onExplanationNeeded(List<String> permissionsToExplain) {
|
||||
Toast.makeText(CustomizedLocationPulsingCircleActivity.this, "You need to accept location permissions.",
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionResult(boolean granted) {
|
||||
if (granted) {
|
||||
mapView.getMapAsync(CustomizedLocationPulsingCircleActivity.this);
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
permissionsManager.requestLocationPermissions(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void showDurationListDialog() {
|
||||
List<String> modes = new ArrayList<>();
|
||||
modes.add(String.format("%sms", String.valueOf(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS)));
|
||||
modes.add(String.format("%sms", String.valueOf(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS)));
|
||||
modes.add(String.format("%sms", String.valueOf(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS)));
|
||||
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
|
||||
android.R.layout.simple_list_item_1, modes);
|
||||
ListPopupWindow listPopup = new ListPopupWindow(this);
|
||||
listPopup.setAdapter(profileAdapter);
|
||||
listPopup.setAnchorView(pulsingCircleDurationButton);
|
||||
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
|
||||
String selectedMode = modes.get(position);
|
||||
pulsingCircleDurationButton.setText(selectedMode);
|
||||
if (selectedMode.contentEquals(String.format("%sms",
|
||||
String.valueOf(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
|
||||
LOCATION_CIRCLE_PULSE_DURATION = DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS;
|
||||
setNewLocationComponentOptions(DEFAULT_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
|
||||
} else if (selectedMode.contentEquals(String.format("%sms",
|
||||
String.valueOf(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
|
||||
LOCATION_CIRCLE_PULSE_DURATION = SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS;
|
||||
setNewLocationComponentOptions(SECOND_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
|
||||
} else if (selectedMode.contentEquals(String.format("%sms",
|
||||
String.valueOf(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS)))) {
|
||||
LOCATION_CIRCLE_PULSE_DURATION = THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS;
|
||||
setNewLocationComponentOptions(THIRD_LOCATION_CIRCLE_PULSE_DURATION_MS, LOCATION_CIRCLE_PULSE_COLOR);
|
||||
}
|
||||
listPopup.dismiss();
|
||||
});
|
||||
listPopup.show();
|
||||
}
|
||||
|
||||
private void showColorListDialog() {
|
||||
List<String> trackingTypes = new ArrayList<>();
|
||||
trackingTypes.add("Blue");
|
||||
trackingTypes.add("Red");
|
||||
trackingTypes.add("Green");
|
||||
trackingTypes.add("Gray");
|
||||
ArrayAdapter<String> profileAdapter = new ArrayAdapter<>(this,
|
||||
android.R.layout.simple_list_item_1, trackingTypes);
|
||||
ListPopupWindow listPopup = new ListPopupWindow(this);
|
||||
listPopup.setAdapter(profileAdapter);
|
||||
listPopup.setAnchorView(pulsingCircleColorButton);
|
||||
listPopup.setOnItemClickListener((parent, itemView, position, id) -> {
|
||||
String selectedTrackingType = trackingTypes.get(position);
|
||||
pulsingCircleColorButton.setText(selectedTrackingType);
|
||||
if (selectedTrackingType.contentEquals("Blue")) {
|
||||
LOCATION_CIRCLE_PULSE_COLOR = Color.BLUE;
|
||||
setNewLocationComponentOptions(currentPulseDuration, Color.BLUE);
|
||||
} else if (selectedTrackingType.contentEquals("Red")) {
|
||||
LOCATION_CIRCLE_PULSE_COLOR = Color.RED;
|
||||
setNewLocationComponentOptions(currentPulseDuration, Color.RED);
|
||||
} else if (selectedTrackingType.contentEquals("Green")) {
|
||||
LOCATION_CIRCLE_PULSE_COLOR = Color.GREEN;
|
||||
setNewLocationComponentOptions(currentPulseDuration, Color.GREEN);
|
||||
} else if (selectedTrackingType.contentEquals("Gray")) {
|
||||
LOCATION_CIRCLE_PULSE_COLOR = Color.parseColor("#4a4a4a");
|
||||
setNewLocationComponentOptions(currentPulseDuration, Color.parseColor("#4a4a4a"));
|
||||
}
|
||||
listPopup.dismiss();
|
||||
});
|
||||
listPopup.show();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
mapView.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mapView.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mapView.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mapView.onStop();
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
mapView.onSaveInstanceState(outState);
|
||||
if (locationComponent != null) {
|
||||
outState.putParcelable(SAVED_STATE_LOCATION, locationComponent.getLastKnownLocation());
|
||||
outState.putInt(SAVED_STATE_LOCATION_CIRCLE_PULSE_COLOR, LOCATION_CIRCLE_PULSE_COLOR);
|
||||
outState.putFloat(SAVED_STATE_LOCATION_CIRCLE_PULSE_DURATION, LOCATION_CIRCLE_PULSE_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mapView.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
super.onLowMemory();
|
||||
mapView.onLowMemory();
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import java.util.Random;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Useful utilities used throughout the testapp.
|
||||
* Useful utilities used throughout the test app.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.mapbox.mapboxsdk.maps.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:mapbox_uiAttribution="false"/>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.mapbox.mapboxsdk.maps.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/linearLayout"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:mapbox_uiAttribution="false" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/primary"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="4"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
tools:layout_constraintBottom_creator="1"
|
||||
tools:layout_constraintLeft_creator="1"
|
||||
tools:layout_constraintRight_creator="1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_frequency"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight=".75"
|
||||
android:gravity="center"
|
||||
android:text="Duration:"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_location_circle_duration"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.25"
|
||||
android:gravity="center"
|
||||
tools:text="400ms"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_color"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight=".85"
|
||||
android:gravity="center"
|
||||
android:text="Color:"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_location_circle_color"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1.15"
|
||||
android:gravity="center"
|
||||
android:text="None"
|
||||
android:textColor="@android:color/white" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item android:id="@+id/action_map_style_change"
|
||||
android:title="Toggle custom Map style"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item android:id="@+id/action_component_disable"
|
||||
android:title="Disable Component"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item android:id="@+id/action_component_enabled"
|
||||
android:title="Enable Component"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item android:id="@+id/action_stop_pulsing"
|
||||
android:title="Stop pulsing"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item android:id="@+id/action_start_pulsing"
|
||||
android:title="Start pulsing"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
</menu>
|
||||
@@ -85,6 +85,8 @@
|
||||
<string name="description_location_map_change">Change map\'s style while location is displayed</string>
|
||||
<string name="description_location_modes">Showcases location render and tracking modes</string>
|
||||
<string name="description_location_fragment">Uses LocationComponent in a Fragment</string>
|
||||
<string name="description_location_basic_pulsing_circle">Display the LocationComponent\'s default pulsing circle</string>
|
||||
<string name="description_location_customized_pulsing_circle">Display and customize the LocationComponent\'s pulsing circle</string>
|
||||
<string name="description_location_manual">Force location updates and don\'t rely on the engine</string>
|
||||
<string name="description_location_activation_builder">Use LocationComponentActivationOptions to set options</string>
|
||||
<string name="description_recyclerview_textureview">Show multiple TextureView MapViews as recyclerView items</string>
|
||||
|
||||
@@ -81,6 +81,8 @@
|
||||
<string name="activity_draggable_maker">Draggable marker</string>
|
||||
<string name="activity_location_map_change">Simple Location Activity</string>
|
||||
<string name="activity_location_modes">Location Modes Activity</string>
|
||||
<string name="activity_basic_location_pulsing_circle">Basic Pulsing Circle Activity</string>
|
||||
<string name="activity_customized_location_pulsing_circle">Customized Pulsing Circle Activity</string>
|
||||
<string name="activity_location_fragment">Location Fragment</string>
|
||||
<string name="activity_location_manual">Manual Location updates</string>
|
||||
<string name="activity_location_activation_builder">Build Location Activation</string>
|
||||
|
||||
Reference in New Issue
Block a user