1701 lines
68 KiB
Java
1701 lines
68 KiB
Java
package com.mapbox.mapboxsdk.location;
|
|
|
|
import android.annotation.SuppressLint;
|
|
import android.content.Context;
|
|
import android.hardware.SensorManager;
|
|
import android.location.Location;
|
|
import android.os.Looper;
|
|
import android.os.SystemClock;
|
|
import android.view.WindowManager;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.annotation.RequiresPermission;
|
|
import androidx.annotation.StyleRes;
|
|
import androidx.annotation.VisibleForTesting;
|
|
|
|
import com.mapbox.android.core.location.LocationEngine;
|
|
import com.mapbox.android.core.location.LocationEngineCallback;
|
|
import com.mapbox.android.core.location.LocationEngineProvider;
|
|
import com.mapbox.android.core.location.LocationEngineRequest;
|
|
import com.mapbox.android.core.location.LocationEngineResult;
|
|
import com.mapbox.android.core.permissions.PermissionsManager;
|
|
import com.mapbox.mapboxsdk.R;
|
|
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
|
import com.mapbox.mapboxsdk.camera.CameraUpdate;
|
|
import com.mapbox.mapboxsdk.geometry.LatLng;
|
|
import com.mapbox.mapboxsdk.location.modes.CameraMode;
|
|
import com.mapbox.mapboxsdk.location.modes.RenderMode;
|
|
import com.mapbox.mapboxsdk.log.Logger;
|
|
import com.mapbox.mapboxsdk.maps.MapView;
|
|
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
|
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener;
|
|
import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener;
|
|
import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener;
|
|
import com.mapbox.mapboxsdk.maps.Style;
|
|
import com.mapbox.mapboxsdk.maps.Transform;
|
|
|
|
import java.lang.ref.WeakReference;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.concurrent.CopyOnWriteArrayList;
|
|
|
|
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
|
|
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
|
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_FASTEST_INTERVAL_MILLIS;
|
|
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_INTERVAL_MILLIS;
|
|
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION;
|
|
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION;
|
|
import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS;
|
|
|
|
/**
|
|
* The Location Component provides location awareness to your mobile application. Enabling this
|
|
* component provides a contextual experience to your users by showing an icon representing the users
|
|
* current location. A few different modes are offered to provide the right context to your users at
|
|
* the correct time. {@link RenderMode#NORMAL} simply shows the users location on the map
|
|
* represented as a dot. {@link RenderMode#COMPASS} mode allows you to display an arrow icon
|
|
* (by default) that points in the direction the device is pointing in.
|
|
* {@link RenderMode#GPS} can be used in conjunction with our Navigation SDK to
|
|
* display a larger icon (customized with {@link LocationComponentOptions#gpsDrawable()}) we call the user puck.
|
|
* <p>
|
|
* This component also offers the ability to set a map camera behavior for tracking the user
|
|
* location. These different {@link CameraMode}s will track, stop tracking the location based on the
|
|
* mode set with {@link LocationComponent#setCameraMode(int)}.
|
|
* <p>
|
|
* <strong>
|
|
* To get the component object use {@link MapboxMap#getLocationComponent()} and activate it with
|
|
* {@link #activateLocationComponent(Context, Style)} or one of the overloads.
|
|
* Then, manage its visibility with {@link #setLocationComponentEnabled(boolean)}.
|
|
* The component will not process location updates right after activation, but only after being enabled.
|
|
* </strong>
|
|
* <p>
|
|
* Using this component requires you to request permission beforehand manually or using
|
|
* {@link PermissionsManager}. Either
|
|
* {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested for
|
|
* this component to work as expected.
|
|
* <p>
|
|
* This component offers a default, built-in {@link LocationEngine} with some of the activation methods.
|
|
* This engine will be obtained by {@link LocationEngineProvider#getBestLocationEngine(Context, boolean)} which defaults
|
|
* to the {@link com.mapbox.android.core.location.MapboxFusedLocationEngineImpl}. If you'd like to utilize Google
|
|
* Play Services
|
|
* for more precise location updates, simply add the Google Play Location Services dependency in your build script.
|
|
* This will make the default engine the {@link com.mapbox.android.core.location.GoogleLocationEngineImpl} instead.
|
|
* After a custom engine is passed to the component, or the built-in is initialized,
|
|
* the location updates are going to be requested with the {@link LocationEngineRequest}, either a default one,
|
|
* or the one passed during the activation.
|
|
* When using any engine, requesting/removing the location updates is going to be managed internally.
|
|
* <p>
|
|
* You can also push location updates to the component without any internal engine management.
|
|
* To achieve that, use {@link #activateLocationComponent(Context, Style, boolean)} with false.
|
|
* No engine is going to be initialized and you can push location updates with {@link #forceLocationUpdate(Location)}.
|
|
* <p>
|
|
* For location puck animation purposes, like navigation,
|
|
* we recommend limiting the maximum zoom level of the map for the best user experience.
|
|
* <p>
|
|
* Location Component doesn't support state saving out-of-the-box.
|
|
*/
|
|
public final class LocationComponent {
|
|
private static final String TAG = "Mbgl-LocationComponent";
|
|
|
|
@NonNull
|
|
private final MapboxMap mapboxMap;
|
|
@NonNull
|
|
private final Transform transform;
|
|
private Style style;
|
|
private LocationComponentOptions options;
|
|
@NonNull
|
|
private InternalLocationEngineProvider internalLocationEngineProvider = new InternalLocationEngineProvider();
|
|
@Nullable
|
|
private LocationEngine locationEngine;
|
|
@NonNull
|
|
private LocationEngineRequest locationEngineRequest =
|
|
new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS)
|
|
.setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS)
|
|
.setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY)
|
|
.build();
|
|
private LocationEngineCallback<LocationEngineResult> currentLocationEngineListener
|
|
= new CurrentLocationEngineCallback(this);
|
|
private LocationEngineCallback<LocationEngineResult> lastLocationEngineListener
|
|
= new LastLocationEngineCallback(this);
|
|
|
|
@Nullable
|
|
private CompassEngine compassEngine;
|
|
|
|
private LocationLayerController locationLayerController;
|
|
private LocationCameraController locationCameraController;
|
|
|
|
private LocationAnimatorCoordinator locationAnimatorCoordinator;
|
|
|
|
/**
|
|
* Holds last location which is being returned in the {@link #getLastKnownLocation()}
|
|
* when there is no {@link #locationEngine} set or when the last location returned by the engine is null.
|
|
*/
|
|
@Nullable
|
|
private Location lastLocation;
|
|
private CameraPosition lastCameraPosition;
|
|
|
|
/**
|
|
* Indicates whether the component has been initialized.
|
|
*/
|
|
private boolean isComponentInitialized;
|
|
|
|
/**
|
|
* Indicates whether we're using the {@link com.mapbox.mapboxsdk.style.layers.LocationIndicatorLayer}
|
|
* or the stack of {@link com.mapbox.mapboxsdk.style.layers.SymbolLayer}s.
|
|
*/
|
|
private boolean useSpecializedLocationLayer;
|
|
|
|
/**
|
|
* Indicates that the component is enabled and should be displaying location if Mapbox components are available and
|
|
* the lifecycle is in a started state.
|
|
*/
|
|
private boolean isEnabled;
|
|
|
|
/**
|
|
* Indicated that component's lifecycle {@link #onStart()} method has been called.
|
|
* This allows Mapbox components enter started state and display data, and adds state safety for methods like
|
|
* {@link #setLocationComponentEnabled(boolean)}
|
|
*/
|
|
private boolean isComponentStarted;
|
|
|
|
/**
|
|
* Indicates if Mapbox components are ready to be interacted with. This can differ from {@link #isComponentStarted}
|
|
* if the Mapbox style is being reloaded.
|
|
*/
|
|
private boolean isLayerReady;
|
|
|
|
/**
|
|
* Indicates whether we are listening for compass updates.
|
|
*/
|
|
private boolean isListeningToCompass;
|
|
|
|
private StaleStateManager staleStateManager;
|
|
private final CopyOnWriteArrayList<OnLocationStaleListener> onLocationStaleListeners
|
|
= new CopyOnWriteArrayList<>();
|
|
private final CopyOnWriteArrayList<OnLocationClickListener> onLocationClickListeners
|
|
= new CopyOnWriteArrayList<>();
|
|
private final CopyOnWriteArrayList<OnLocationLongClickListener> onLocationLongClickListeners
|
|
= new CopyOnWriteArrayList<>();
|
|
private final CopyOnWriteArrayList<OnCameraTrackingChangedListener> onCameraTrackingChangedListeners
|
|
= new CopyOnWriteArrayList<>();
|
|
private final CopyOnWriteArrayList<OnRenderModeChangedListener> onRenderModeChangedListeners
|
|
= new CopyOnWriteArrayList<>();
|
|
|
|
// Workaround for too frequent updates, see https://github.com/mapbox/mapbox-gl-native/issues/13587
|
|
private long fastestInterval;
|
|
private long lastUpdateTime;
|
|
|
|
/**
|
|
* Internal use.
|
|
* <p>
|
|
* To get the component object use {@link MapboxMap#getLocationComponent()}.
|
|
*/
|
|
public LocationComponent(@NonNull MapboxMap mapboxMap,
|
|
@NonNull Transform transform,
|
|
@NonNull List<MapboxMap.OnDeveloperAnimationListener> developerAnimationListeners) {
|
|
this.mapboxMap = mapboxMap;
|
|
this.transform = transform;
|
|
developerAnimationListeners.add(developerAnimationListener);
|
|
}
|
|
|
|
// used for creating a spy
|
|
LocationComponent() {
|
|
//noinspection ConstantConditions
|
|
mapboxMap = null;
|
|
transform = null;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
LocationComponent(@NonNull MapboxMap mapboxMap,
|
|
@NonNull Transform transform,
|
|
@NonNull List<MapboxMap.OnDeveloperAnimationListener> developerAnimationListeners,
|
|
@NonNull LocationEngineCallback<LocationEngineResult> currentListener,
|
|
@NonNull LocationEngineCallback<LocationEngineResult> lastListener,
|
|
@NonNull LocationLayerController locationLayerController,
|
|
@NonNull LocationCameraController locationCameraController,
|
|
@NonNull LocationAnimatorCoordinator locationAnimatorCoordinator,
|
|
@NonNull StaleStateManager staleStateManager,
|
|
@NonNull CompassEngine compassEngine,
|
|
@NonNull InternalLocationEngineProvider internalLocationEngineProvider) {
|
|
this.mapboxMap = mapboxMap;
|
|
this.transform = transform;
|
|
developerAnimationListeners.add(developerAnimationListener);
|
|
this.currentLocationEngineListener = currentListener;
|
|
this.lastLocationEngineListener = lastListener;
|
|
this.locationLayerController = locationLayerController;
|
|
this.locationCameraController = locationCameraController;
|
|
this.locationAnimatorCoordinator = locationAnimatorCoordinator;
|
|
this.staleStateManager = staleStateManager;
|
|
this.compassEngine = compassEngine;
|
|
this.internalLocationEngineProvider = internalLocationEngineProvider;
|
|
isComponentInitialized = true;
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
* <p>
|
|
* <strong>Note</strong>: This method will initialize and use an internal {@link LocationEngine} when enabled.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style) {
|
|
activateLocationComponent(context, style,
|
|
LocationComponentOptions.createFromAttributes(context, R.style.mapbox_LocationComponent));
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if
|
|
* there should be no location engine initialized
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
boolean useDefaultLocationEngine) {
|
|
if (useDefaultLocationEngine) {
|
|
activateLocationComponent(context, style, R.style.mapbox_LocationComponent);
|
|
} else {
|
|
activateLocationComponent(context, style, null, R.style.mapbox_LocationComponent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if
|
|
* there should be no location engine initialized
|
|
* @param locationEngineRequest the location request
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
boolean useDefaultLocationEngine,
|
|
@NonNull LocationEngineRequest locationEngineRequest) {
|
|
setLocationEngineRequest(locationEngineRequest);
|
|
if (useDefaultLocationEngine) {
|
|
activateLocationComponent(context, style, R.style.mapbox_LocationComponent);
|
|
} else {
|
|
activateLocationComponent(context, style, null, R.style.mapbox_LocationComponent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if
|
|
* there should be no location engine initialized
|
|
* @param locationEngineRequest the location request
|
|
* @param options the options
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
boolean useDefaultLocationEngine,
|
|
@NonNull LocationEngineRequest locationEngineRequest,
|
|
@NonNull LocationComponentOptions options) {
|
|
setLocationEngineRequest(locationEngineRequest);
|
|
if (useDefaultLocationEngine) {
|
|
activateLocationComponent(context, style, options);
|
|
} else {
|
|
activateLocationComponent(context, style, null, options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
* <p>
|
|
* <strong>Note</strong>: This method will initialize and use an internal {@link LocationEngine} when enabled.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param styleRes the LocationComponent style res
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style, @StyleRes int styleRes) {
|
|
activateLocationComponent(context, style, LocationComponentOptions.createFromAttributes(context, styleRes));
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
* <p>
|
|
* <strong>Note</strong>: This method will initialize and use an internal {@link LocationEngine} when enabled.
|
|
* </p>
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param options the options
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@NonNull LocationComponentOptions options) {
|
|
initialize(context, style, false, options);
|
|
initializeLocationEngine(context);
|
|
applyStyle(options);
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param locationEngine the engine, or null if you'd like to only force location updates
|
|
* @param styleRes the LocationComponent style res
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine, @StyleRes int styleRes) {
|
|
activateLocationComponent(context, style, locationEngine,
|
|
LocationComponentOptions.createFromAttributes(context, styleRes));
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param locationEngine the engine, or null if you'd like to only force location updates
|
|
* @param locationEngineRequest the location request
|
|
* @param styleRes the LocationComponent style res
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine,
|
|
@NonNull LocationEngineRequest locationEngineRequest, @StyleRes int styleRes) {
|
|
activateLocationComponent(context, style, locationEngine, locationEngineRequest,
|
|
LocationComponentOptions.createFromAttributes(context, styleRes));
|
|
}
|
|
|
|
/**
|
|
* This method will show the location icon and enable the camera tracking the location.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param locationEngine the engine
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine) {
|
|
activateLocationComponent(context, style, locationEngine, R.style.mapbox_LocationComponent);
|
|
}
|
|
|
|
/**
|
|
* This method will show the location icon and enable the camera tracking the location.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param locationEngine the engine
|
|
* @param locationEngineRequest the location request
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine,
|
|
@NonNull LocationEngineRequest locationEngineRequest) {
|
|
activateLocationComponent(context, style, locationEngine, locationEngineRequest, R.style.mapbox_LocationComponent);
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param locationEngine the engine, or null if you'd like to only force location updates
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param options the options
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine,
|
|
@NonNull LocationComponentOptions options) {
|
|
initialize(context, style, false, options);
|
|
setLocationEngine(locationEngine);
|
|
applyStyle(options);
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param context the context
|
|
* @param style the proxy object for current map style. More info at {@link Style}
|
|
* @param locationEngine the engine, or null if you'd like to only force location updates
|
|
* @param locationEngineRequest the location request
|
|
* @param options the options
|
|
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
|
|
*/
|
|
@Deprecated
|
|
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
|
|
@Nullable LocationEngine locationEngine,
|
|
@NonNull LocationEngineRequest locationEngineRequest,
|
|
@NonNull LocationComponentOptions options) {
|
|
initialize(context, style, false, options);
|
|
setLocationEngineRequest(locationEngineRequest);
|
|
setLocationEngine(locationEngine);
|
|
applyStyle(options);
|
|
}
|
|
|
|
/**
|
|
* This method initializes the component and needs to be called before any other operations are performed.
|
|
* Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}.
|
|
*
|
|
* @param activationOptions a fully built {@link LocationComponentActivationOptions} object
|
|
*/
|
|
public void activateLocationComponent(@NonNull LocationComponentActivationOptions activationOptions) {
|
|
LocationComponentOptions options = activationOptions.locationComponentOptions();
|
|
if (options == null) {
|
|
int styleRes = activationOptions.styleRes();
|
|
if (styleRes == 0) {
|
|
styleRes = R.style.mapbox_LocationComponent;
|
|
}
|
|
options = LocationComponentOptions.createFromAttributes(activationOptions.context(), styleRes);
|
|
}
|
|
|
|
// Initialize the LocationComponent with Context, the map's `Style`, and either custom LocationComponentOptions
|
|
// or backup options created from default/custom attributes
|
|
initialize(activationOptions.context(), activationOptions.style(),
|
|
activationOptions.useSpecializedLocationLayer(), options);
|
|
|
|
// Apply the LocationComponent styling
|
|
// TODO avoid doubling style initialization
|
|
applyStyle(options);
|
|
|
|
// Set the LocationEngine request if one was given to LocationComponentActivationOptions
|
|
LocationEngineRequest locationEngineRequest = activationOptions.locationEngineRequest();
|
|
if (locationEngineRequest != null) {
|
|
setLocationEngineRequest(locationEngineRequest);
|
|
}
|
|
|
|
// Set the LocationEngine if one was given to LocationComponentActivationOptions
|
|
LocationEngine locationEngine = activationOptions.locationEngine();
|
|
if (locationEngine != null) {
|
|
setLocationEngine(locationEngine);
|
|
} else {
|
|
if (activationOptions.useDefaultLocationEngine()) {
|
|
initializeLocationEngine(activationOptions.context());
|
|
} else {
|
|
setLocationEngine(null);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Manage component's visibility after activation.
|
|
*
|
|
* @param isEnabled true if the plugin should be visible and listen for location updates, false otherwise.
|
|
*/
|
|
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
|
|
public void setLocationComponentEnabled(boolean isEnabled) {
|
|
checkActivationState();
|
|
if (isEnabled) {
|
|
enableLocationComponent();
|
|
} else {
|
|
disableLocationComponent();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether the plugin is enabled, meaning that location can be displayed and camera modes can be used.
|
|
*
|
|
* @return true if the plugin is enabled, false otherwise
|
|
*/
|
|
public boolean isLocationComponentEnabled() {
|
|
checkActivationState();
|
|
return isEnabled;
|
|
}
|
|
|
|
/**
|
|
* Sets the camera mode, which determines how the map camera will track the rendered location.
|
|
* <p>
|
|
* When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or
|
|
* {@link #tiltWhileTracking(double)}.
|
|
* Use {@link OnLocationCameraTransitionListener} to listen for the transition state.
|
|
* <p>
|
|
* <ul>
|
|
* <li>{@link CameraMode#NONE}: No camera tracking</li>
|
|
* <li>{@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing</li>
|
|
* <li>{@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing</li>
|
|
* <li>{@link CameraMode#TRACKING}: Camera tracks the user location</li>
|
|
* <li>{@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north</li>
|
|
* </ul>
|
|
*
|
|
* @param cameraMode one of the modes found in {@link CameraMode}
|
|
*/
|
|
public void setCameraMode(@CameraMode.Mode int cameraMode) {
|
|
setCameraMode(cameraMode, null);
|
|
}
|
|
|
|
/**
|
|
* Sets the camera mode, which determines how the map camera will track the rendered location.
|
|
* <p>
|
|
* When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or
|
|
* {@link #tiltWhileTracking(double)}.
|
|
* Use {@link OnLocationCameraTransitionListener} to listen for the transition state.
|
|
* <p>
|
|
* <ul>
|
|
* <li>{@link CameraMode#NONE}: No camera tracking</li>
|
|
* <li>{@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing</li>
|
|
* <li>{@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing</li>
|
|
* <li>{@link CameraMode#TRACKING}: Camera tracks the user location</li>
|
|
* <li>{@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north</li>
|
|
* </ul>
|
|
*
|
|
* @param cameraMode one of the modes found in {@link CameraMode}
|
|
* @param transitionListener callback that's going to be invoked when the transition animation finishes
|
|
*/
|
|
public void setCameraMode(@CameraMode.Mode int cameraMode,
|
|
@Nullable OnLocationCameraTransitionListener transitionListener) {
|
|
setCameraMode(cameraMode, TRANSITION_ANIMATION_DURATION_MS, null, null, null, transitionListener);
|
|
}
|
|
|
|
/**
|
|
* Sets the camera mode, which determines how the map camera will track the rendered location.
|
|
* <p>
|
|
* When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or
|
|
* {@link #tiltWhileTracking(double)}.
|
|
* Use {@link OnLocationCameraTransitionListener} to listen for the transition state.
|
|
* <p>
|
|
* Set values of zoom, bearing and tilt that the camera will transition to. If null is passed to any of those,
|
|
* current value will be used for that parameter instead.
|
|
* If the camera is already tracking, provided values are ignored.
|
|
* <p>
|
|
* <ul>
|
|
* <li>{@link CameraMode#NONE}: No camera tracking</li>
|
|
* <li>{@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing</li>
|
|
* <li>{@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing</li>
|
|
* <li>{@link CameraMode#TRACKING}: Camera tracks the user location</li>
|
|
* <li>{@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing</li>
|
|
* <li>{@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north</li>
|
|
* </ul>
|
|
*
|
|
* @param cameraMode one of the modes found in {@link CameraMode}
|
|
* @param transitionDuration duration of the transition in milliseconds
|
|
* @param zoom target zoom, set to null to use current camera position
|
|
* @param bearing target bearing, set to null to use current camera position
|
|
* @param tilt target tilt, set to null to use current camera position
|
|
* @param transitionListener callback that's going to be invoked when the transition animation finishes
|
|
*/
|
|
public void setCameraMode(@CameraMode.Mode int cameraMode,
|
|
long transitionDuration,
|
|
@Nullable Double zoom, @Nullable Double bearing, @Nullable Double tilt,
|
|
@Nullable OnLocationCameraTransitionListener transitionListener) {
|
|
checkActivationState();
|
|
locationCameraController.setCameraMode(
|
|
cameraMode, lastLocation, transitionDuration, zoom, bearing, tilt,
|
|
new CameraTransitionListener(transitionListener));
|
|
updateCompassListenerState(true);
|
|
}
|
|
|
|
/**
|
|
* Used to reset camera animators and notify listeners when the transition finishes.
|
|
*/
|
|
private class CameraTransitionListener implements OnLocationCameraTransitionListener {
|
|
|
|
private final OnLocationCameraTransitionListener externalListener;
|
|
|
|
private CameraTransitionListener(OnLocationCameraTransitionListener externalListener) {
|
|
this.externalListener = externalListener;
|
|
}
|
|
|
|
@Override
|
|
public void onLocationCameraTransitionFinished(int cameraMode) {
|
|
if (externalListener != null) {
|
|
externalListener.onLocationCameraTransitionFinished(cameraMode);
|
|
}
|
|
reset(cameraMode);
|
|
}
|
|
|
|
@Override
|
|
public void onLocationCameraTransitionCanceled(int cameraMode) {
|
|
if (externalListener != null) {
|
|
externalListener.onLocationCameraTransitionCanceled(cameraMode);
|
|
}
|
|
reset(cameraMode);
|
|
}
|
|
|
|
private void reset(@CameraMode.Mode int cameraMode) {
|
|
locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(),
|
|
cameraMode == CameraMode.TRACKING_GPS_NORTH);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Provides the current camera mode being used to track the location or compass updates.
|
|
*
|
|
* @return the current camera mode
|
|
*/
|
|
@CameraMode.Mode
|
|
public int getCameraMode() {
|
|
checkActivationState();
|
|
return locationCameraController.getCameraMode();
|
|
}
|
|
|
|
/**
|
|
* Sets the render mode, which determines how the location updates will be rendered on the map.
|
|
* <p>
|
|
* <ul>
|
|
* <li>{@link RenderMode#NORMAL}: Shows user location, bearing ignored</li>
|
|
* <li>{@link RenderMode#COMPASS}: Shows user location with bearing considered from compass</li>
|
|
* <li>{@link RenderMode#GPS}: Shows user location with bearing considered from location</li>
|
|
* </ul>
|
|
*
|
|
* @param renderMode one of the modes found in {@link RenderMode}
|
|
*/
|
|
public void setRenderMode(@RenderMode.Mode int renderMode) {
|
|
checkActivationState();
|
|
locationLayerController.setRenderMode(renderMode);
|
|
updateLayerOffsets(true);
|
|
updateCompassListenerState(true);
|
|
}
|
|
|
|
/**
|
|
* Provides the current render mode being used to show
|
|
* the location and/or compass updates on the map.
|
|
*
|
|
* @return the current render mode
|
|
*/
|
|
@RenderMode.Mode
|
|
public int getRenderMode() {
|
|
checkActivationState();
|
|
return locationLayerController.getRenderMode();
|
|
}
|
|
|
|
/**
|
|
* Returns the current location options being used.
|
|
*
|
|
* @return the current {@link LocationComponentOptions}
|
|
*/
|
|
public LocationComponentOptions getLocationComponentOptions() {
|
|
checkActivationState();
|
|
return options;
|
|
}
|
|
|
|
/**
|
|
* Apply a new component style with a style resource.
|
|
*
|
|
* @param styleRes a XML style overriding some or all the options
|
|
*/
|
|
public void applyStyle(@NonNull Context context, @StyleRes int styleRes) {
|
|
checkActivationState();
|
|
applyStyle(LocationComponentOptions.createFromAttributes(context, styleRes));
|
|
}
|
|
|
|
/**
|
|
* Apply a new component style with location component options.
|
|
*
|
|
* @param options to update the current style
|
|
*/
|
|
public void applyStyle(@NonNull final LocationComponentOptions options) {
|
|
checkActivationState();
|
|
LocationComponent.this.options = options;
|
|
if (mapboxMap.getStyle() != null) {
|
|
locationLayerController.applyStyle(options);
|
|
locationCameraController.initializeOptions(options);
|
|
staleStateManager.setEnabled(options.enableStaleState());
|
|
staleStateManager.setDelayTime(options.staleStateTimeout());
|
|
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options.trackingAnimationDurationMultiplier());
|
|
locationAnimatorCoordinator.setCompassAnimationEnabled(options.compassAnimationEnabled());
|
|
locationAnimatorCoordinator.setAccuracyAnimationEnabled(options.accuracyAnimationEnabled());
|
|
updateMapWithOptions(options);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Zooms to the desired zoom level.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param zoomLevel The desired zoom level.
|
|
* @param animationDuration The zoom animation duration.
|
|
* @param callback The callback with finish/cancel information
|
|
*/
|
|
public void zoomWhileTracking(double zoomLevel, long animationDuration,
|
|
@Nullable MapboxMap.CancelableCallback callback) {
|
|
checkActivationState();
|
|
if (!isLayerReady) {
|
|
notifyUnsuccessfulCameraOperation(callback, null);
|
|
return;
|
|
} else if (getCameraMode() == CameraMode.NONE) {
|
|
notifyUnsuccessfulCameraOperation(callback, String.format("%s%s",
|
|
"LocationComponent#zoomWhileTracking method can only be used",
|
|
" when a camera mode other than CameraMode#NONE is engaged."));
|
|
return;
|
|
} else if (locationCameraController.isTransitioning()) {
|
|
notifyUnsuccessfulCameraOperation(callback,
|
|
"LocationComponent#zoomWhileTracking method call is ignored because the camera mode is transitioning");
|
|
return;
|
|
}
|
|
locationAnimatorCoordinator.feedNewZoomLevel(zoomLevel, mapboxMap.getCameraPosition(), animationDuration, callback);
|
|
}
|
|
|
|
/**
|
|
* Zooms to the desired zoom level.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param zoomLevel The desired zoom level.
|
|
* @param animationDuration The zoom animation duration.
|
|
*/
|
|
public void zoomWhileTracking(double zoomLevel, long animationDuration) {
|
|
checkActivationState();
|
|
zoomWhileTracking(zoomLevel, animationDuration, null);
|
|
}
|
|
|
|
/**
|
|
* Zooms to the desired zoom level.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the zoom change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param zoomLevel The desired zoom level.
|
|
*/
|
|
public void zoomWhileTracking(double zoomLevel) {
|
|
checkActivationState();
|
|
zoomWhileTracking(zoomLevel, DEFAULT_TRACKING_ZOOM_ANIM_DURATION, null);
|
|
}
|
|
|
|
/**
|
|
* Cancels animation started by {@link #zoomWhileTracking(double, long, MapboxMap.CancelableCallback)}.
|
|
*/
|
|
public void cancelZoomWhileTrackingAnimation() {
|
|
checkActivationState();
|
|
locationAnimatorCoordinator.cancelZoomAnimation();
|
|
}
|
|
|
|
/**
|
|
* Tilts the camera.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param tilt The desired camera tilt.
|
|
* @param animationDuration The tilt animation duration.
|
|
* @param callback The callback with finish/cancel information
|
|
*/
|
|
public void tiltWhileTracking(double tilt, long animationDuration,
|
|
@Nullable MapboxMap.CancelableCallback callback) {
|
|
checkActivationState();
|
|
if (!isLayerReady) {
|
|
notifyUnsuccessfulCameraOperation(callback, null);
|
|
return;
|
|
} else if (getCameraMode() == CameraMode.NONE) {
|
|
notifyUnsuccessfulCameraOperation(callback, String.format("%s%s",
|
|
"LocationComponent#tiltWhileTracking method can only be used",
|
|
" when a camera mode other than CameraMode#NONE is engaged."));
|
|
return;
|
|
} else if (locationCameraController.isTransitioning()) {
|
|
notifyUnsuccessfulCameraOperation(callback,
|
|
"LocationComponent#tiltWhileTracking method call is ignored because the camera mode is transitioning");
|
|
return;
|
|
}
|
|
locationAnimatorCoordinator.feedNewTilt(tilt, mapboxMap.getCameraPosition(), animationDuration, callback);
|
|
}
|
|
|
|
/**
|
|
* Tilts the camera.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param tilt The desired camera tilt.
|
|
* @param animationDuration The tilt animation duration.
|
|
*/
|
|
public void tiltWhileTracking(double tilt, long animationDuration) {
|
|
checkActivationState();
|
|
tiltWhileTracking(tilt, animationDuration, null);
|
|
}
|
|
|
|
/**
|
|
* Tilts the camera.
|
|
* This API can only be used in pair with camera modes other than {@link CameraMode#NONE}.
|
|
* If you are not using any of {@link CameraMode} modes,
|
|
* use one of {@link MapboxMap#moveCamera(CameraUpdate)},
|
|
* {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead.
|
|
* <p>
|
|
* If the camera is transitioning when the tilt change is requested, the call is going to be ignored.
|
|
* Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument.
|
|
* </p>
|
|
*
|
|
* @param tilt The desired camera tilt.
|
|
*/
|
|
public void tiltWhileTracking(double tilt) {
|
|
checkActivationState();
|
|
tiltWhileTracking(tilt, DEFAULT_TRACKING_TILT_ANIM_DURATION, null);
|
|
}
|
|
|
|
/**
|
|
* Cancels animation started by {@link #tiltWhileTracking(double, long, MapboxMap.CancelableCallback)}.
|
|
*/
|
|
public void cancelTiltWhileTrackingAnimation() {
|
|
checkActivationState();
|
|
locationAnimatorCoordinator.cancelTiltAnimation();
|
|
}
|
|
|
|
/**
|
|
* Use to either force a location update or to manually control when the user location gets
|
|
* updated.
|
|
*
|
|
* @param location where the location icon is placed on the map
|
|
*/
|
|
public void forceLocationUpdate(@Nullable Location location) {
|
|
checkActivationState();
|
|
updateLocation(location, false);
|
|
}
|
|
|
|
/**
|
|
* Use to either force a location update or to manually control when the user location gets
|
|
* updated.
|
|
* <p>
|
|
* This method can be used to provide the list of locations where the last one is the target location
|
|
* and the rest are intermediate points used as the animation path.
|
|
* The puck and the camera will be animated between each of the points linearly until reaching the target.
|
|
*
|
|
* @param locations where the location icon is placed on the map
|
|
* @param lookAheadUpdate If set to true, the last location's timestamp has to be greater than current timestamp and
|
|
* should represent the time at which the animation should actually reach this position,
|
|
* cutting out the time interpolation delay.
|
|
*/
|
|
public void forceLocationUpdate(@Nullable List<Location> locations, boolean lookAheadUpdate) {
|
|
checkActivationState();
|
|
if (locations != null && locations.size() >= 1) {
|
|
updateLocation(
|
|
locations.get(locations.size() - 1), // target location
|
|
locations.subList(0, locations.size() - 1), // intermediate locations
|
|
false,
|
|
lookAheadUpdate);
|
|
} else {
|
|
updateLocation(null, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set max FPS at which location animators can output updates. The throttling will only impact the location puck
|
|
* and camera tracking smooth animations.
|
|
* <p>
|
|
* Setting this <b>will not impact</b> any other animations schedule with {@link MapboxMap}, gesture animations or
|
|
* {@link #zoomWhileTracking(double)}/{@link #tiltWhileTracking(double)}.
|
|
* <p>
|
|
* Use this setting to limit animation rate of the location puck on higher zoom levels to decrease the stress on
|
|
* the device's CPU which can directly improve battery life, without sacrificing UX.
|
|
* <p>
|
|
* Example usage:
|
|
* <pre>
|
|
* {@code
|
|
* mapboxMap.addOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() {
|
|
* {@literal @}Override
|
|
* public void onCameraIdle() {
|
|
* double zoom = mapboxMap.getCameraPosition().zoom;
|
|
* int maxAnimationFps;
|
|
* if (zoom < 5) {
|
|
* maxAnimationFps = 3;
|
|
* } else if (zoom < 10) {
|
|
* maxAnimationFps = 5;
|
|
* } else if (zoom < 15) {
|
|
* maxAnimationFps = 7;
|
|
* } else if (zoom < 18) {
|
|
* maxAnimationFps = 15;
|
|
* } else {
|
|
* maxAnimationFps = Integer.MAX_VALUE;
|
|
* }
|
|
* locationComponent.setMaxAnimationFps(maxAnimationFps);
|
|
* }
|
|
* });
|
|
* }
|
|
* </pre>
|
|
* <p>
|
|
* If you're looking for a way to throttle the FPS of the whole map, including other animations and gestures, see
|
|
* {@link MapView#setMaximumFps(int)}.
|
|
*
|
|
* @param maxAnimationFps max location animation FPS
|
|
*/
|
|
public void setMaxAnimationFps(int maxAnimationFps) {
|
|
checkActivationState();
|
|
locationAnimatorCoordinator.setMaxAnimationFps(maxAnimationFps);
|
|
}
|
|
|
|
/**
|
|
* Set the location engine to update the current user location.
|
|
* <p>
|
|
* If {@code null} is passed in, all updates will have to occur through the
|
|
* {@link LocationComponent#forceLocationUpdate(Location)} method.
|
|
*
|
|
* @param locationEngine a {@link LocationEngine} this component should use to handle updates
|
|
*/
|
|
@SuppressLint("MissingPermission")
|
|
public void setLocationEngine(@Nullable LocationEngine locationEngine) {
|
|
checkActivationState();
|
|
if (this.locationEngine != null) {
|
|
// If internal location engines being used, extra steps need to be taken to deconstruct the instance.
|
|
this.locationEngine.removeLocationUpdates(currentLocationEngineListener);
|
|
this.locationEngine = null;
|
|
}
|
|
|
|
if (locationEngine != null) {
|
|
fastestInterval = locationEngineRequest.getFastestInterval();
|
|
this.locationEngine = locationEngine;
|
|
if (isLayerReady && isEnabled) {
|
|
setLastLocation();
|
|
locationEngine.requestLocationUpdates(
|
|
locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper());
|
|
}
|
|
} else {
|
|
fastestInterval = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the location request that's going to be used when requesting location updates.
|
|
*
|
|
* @param locationEngineRequest the location request
|
|
*/
|
|
public void setLocationEngineRequest(@NonNull LocationEngineRequest locationEngineRequest) {
|
|
checkActivationState();
|
|
this.locationEngineRequest = locationEngineRequest;
|
|
|
|
// reset internal LocationEngine ref to re-request location updates if needed
|
|
setLocationEngine(locationEngine);
|
|
}
|
|
|
|
/**
|
|
* Get the location request that's going to be used when requesting location updates.
|
|
*/
|
|
@NonNull
|
|
public LocationEngineRequest getLocationEngineRequest() {
|
|
checkActivationState();
|
|
return locationEngineRequest;
|
|
}
|
|
|
|
/**
|
|
* Returns the current {@link LocationEngine} being used for updating the user location.
|
|
*
|
|
* @return the {@link LocationEngine} being used to update the user location
|
|
*/
|
|
@Nullable
|
|
public LocationEngine getLocationEngine() {
|
|
checkActivationState();
|
|
return locationEngine;
|
|
}
|
|
|
|
/**
|
|
* Sets the compass engine used to provide compass heading values.
|
|
*
|
|
* @param compassEngine to be used
|
|
*/
|
|
public void setCompassEngine(@Nullable CompassEngine compassEngine) {
|
|
checkActivationState();
|
|
if (this.compassEngine != null) {
|
|
updateCompassListenerState(false);
|
|
}
|
|
this.compassEngine = compassEngine;
|
|
updateCompassListenerState(true);
|
|
}
|
|
|
|
/**
|
|
* Returns the compass engine used to provide compass heading values.
|
|
*
|
|
* @return compass engine currently being used
|
|
*/
|
|
@Nullable
|
|
public CompassEngine getCompassEngine() {
|
|
checkActivationState();
|
|
return compassEngine;
|
|
}
|
|
|
|
/**
|
|
* Get the last know location of the location component.
|
|
*
|
|
* @return the last known location
|
|
*/
|
|
@Nullable
|
|
public Location getLastKnownLocation() {
|
|
checkActivationState();
|
|
return lastLocation;
|
|
}
|
|
|
|
/**
|
|
* Adds a listener that gets invoked when the user clicks the displayed location.
|
|
* <p>
|
|
* If there are registered location click listeners and the location is clicked,
|
|
* only {@link OnLocationClickListener#onLocationComponentClick()} is going to be delivered,
|
|
* {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener#onMapClick(LatLng)} is going to be consumed
|
|
* and not pushed to the listeners registered after the component's activation.
|
|
*
|
|
* @param listener The location click listener that is invoked when the
|
|
* location is clicked
|
|
*/
|
|
public void addOnLocationClickListener(@NonNull OnLocationClickListener listener) {
|
|
onLocationClickListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes the passed listener from the current list of location click listeners.
|
|
*
|
|
* @param listener to be removed
|
|
*/
|
|
public void removeOnLocationClickListener(@NonNull OnLocationClickListener listener) {
|
|
onLocationClickListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Adds a listener that gets invoked when the user long clicks the displayed location.
|
|
* <p>
|
|
* If there are registered location long click listeners and the location is long clicked,
|
|
* only {@link OnLocationLongClickListener#onLocationComponentLongClick()} is going to be delivered,
|
|
* {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnMapLongClickListener#onMapLongClick(LatLng)} is going to be consumed
|
|
* and not pushed to the listeners registered after the component's activation.
|
|
*
|
|
* @param listener The location click listener that is invoked when the
|
|
* location is clicked
|
|
*/
|
|
public void addOnLocationLongClickListener(@NonNull OnLocationLongClickListener listener) {
|
|
onLocationLongClickListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes the passed listener from the current list of location long click listeners.
|
|
*
|
|
* @param listener to be removed
|
|
*/
|
|
public void removeOnLocationLongClickListener(@NonNull OnLocationLongClickListener listener) {
|
|
onLocationLongClickListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Adds a listener that gets invoked when camera tracking state changes.
|
|
*
|
|
* @param listener Listener that gets invoked when camera tracking state changes.
|
|
*/
|
|
public void addOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) {
|
|
onCameraTrackingChangedListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes a listener that gets invoked when camera tracking state changes.
|
|
*
|
|
* @param listener Listener that gets invoked when camera tracking state changes.
|
|
*/
|
|
public void removeOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) {
|
|
onCameraTrackingChangedListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Adds a listener that gets invoked when render mode changes.
|
|
*
|
|
* @param listener Listener that gets invoked when render mode changes.
|
|
*/
|
|
public void addOnRenderModeChangedListener(@NonNull OnRenderModeChangedListener listener) {
|
|
onRenderModeChangedListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes a listener that gets invoked when render mode changes.
|
|
*
|
|
* @param listener Listener that gets invoked when render mode changes.
|
|
*/
|
|
public void removeRenderModeChangedListener(@NonNull OnRenderModeChangedListener listener) {
|
|
onRenderModeChangedListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Adds the passed listener that gets invoked when user updates have stopped long enough for the last update
|
|
* to be considered stale.
|
|
* <p>
|
|
* This timeout is set by {@link LocationComponentOptions#staleStateTimeout()}.
|
|
*
|
|
* @param listener invoked when last update is considered stale
|
|
*/
|
|
public void addOnLocationStaleListener(@NonNull OnLocationStaleListener listener) {
|
|
onLocationStaleListeners.add(listener);
|
|
}
|
|
|
|
/**
|
|
* Removes the passed listener from the current list of stale listeners.
|
|
*
|
|
* @param listener to be removed from the list
|
|
*/
|
|
public void removeOnLocationStaleListener(@NonNull OnLocationStaleListener listener) {
|
|
onLocationStaleListeners.remove(listener);
|
|
}
|
|
|
|
/**
|
|
* Internal use.
|
|
*/
|
|
public void onStart() {
|
|
isComponentStarted = true;
|
|
onLocationLayerStart();
|
|
}
|
|
|
|
/**
|
|
* Internal use.
|
|
*/
|
|
public void onStop() {
|
|
onLocationLayerStop();
|
|
isComponentStarted = false;
|
|
}
|
|
|
|
/**
|
|
* Internal use.
|
|
*/
|
|
public void onDestroy() {
|
|
}
|
|
|
|
/**
|
|
* Internal use.
|
|
*/
|
|
public void onStartLoadingMap() {
|
|
onLocationLayerStop();
|
|
}
|
|
|
|
/**
|
|
* Internal use.
|
|
*/
|
|
public void onFinishLoadingStyle() {
|
|
if (isComponentInitialized) {
|
|
style = mapboxMap.getStyle();
|
|
locationLayerController.initializeComponents(style, options);
|
|
locationCameraController.initializeOptions(options);
|
|
onLocationLayerStart();
|
|
}
|
|
}
|
|
|
|
@SuppressLint("MissingPermission")
|
|
private void onLocationLayerStart() {
|
|
if (!isComponentInitialized || !isComponentStarted || mapboxMap.getStyle() == null) {
|
|
return;
|
|
}
|
|
|
|
if (!isLayerReady) {
|
|
isLayerReady = true;
|
|
mapboxMap.addOnCameraMoveListener(onCameraMoveListener);
|
|
mapboxMap.addOnCameraIdleListener(onCameraIdleListener);
|
|
if (options.enableStaleState()) {
|
|
staleStateManager.onStart();
|
|
}
|
|
}
|
|
|
|
if (isEnabled) {
|
|
if (locationEngine != null) {
|
|
try {
|
|
locationEngine.requestLocationUpdates(
|
|
locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper());
|
|
} catch (SecurityException se) {
|
|
Logger.e(TAG, "Unable to request location updates", se);
|
|
}
|
|
}
|
|
setCameraMode(locationCameraController.getCameraMode());
|
|
setLastLocation();
|
|
updateCompassListenerState(true);
|
|
setLastCompassHeading();
|
|
}
|
|
}
|
|
|
|
private void onLocationLayerStop() {
|
|
if (!isComponentInitialized || !isLayerReady || !isComponentStarted) {
|
|
return;
|
|
}
|
|
|
|
isLayerReady = false;
|
|
staleStateManager.onStop();
|
|
if (compassEngine != null) {
|
|
updateCompassListenerState(false);
|
|
}
|
|
locationAnimatorCoordinator.cancelAllAnimations();
|
|
if (locationEngine != null) {
|
|
locationEngine.removeLocationUpdates(currentLocationEngineListener);
|
|
}
|
|
mapboxMap.removeOnCameraMoveListener(onCameraMoveListener);
|
|
mapboxMap.removeOnCameraIdleListener(onCameraIdleListener);
|
|
}
|
|
|
|
private void initialize(@NonNull final Context context, @NonNull Style style, boolean useSpecializedLocationLayer,
|
|
@NonNull final LocationComponentOptions options) {
|
|
if (isComponentInitialized) {
|
|
return;
|
|
}
|
|
isComponentInitialized = true;
|
|
|
|
if (!style.isFullyLoaded()) {
|
|
throw new IllegalStateException("Style is invalid, provide the most recently loaded one.");
|
|
}
|
|
|
|
this.style = style;
|
|
this.options = options;
|
|
this.useSpecializedLocationLayer = useSpecializedLocationLayer;
|
|
|
|
mapboxMap.addOnMapClickListener(onMapClickListener);
|
|
mapboxMap.addOnMapLongClickListener(onMapLongClickListener);
|
|
|
|
LayerSourceProvider sourceProvider = new LayerSourceProvider();
|
|
LayerFeatureProvider featureProvider = new LayerFeatureProvider();
|
|
LayerBitmapProvider bitmapProvider = new LayerBitmapProvider(context);
|
|
locationLayerController = new LocationLayerController(mapboxMap, style, sourceProvider, featureProvider,
|
|
bitmapProvider, options, renderModeChangedListener, useSpecializedLocationLayer);
|
|
locationCameraController = new LocationCameraController(
|
|
context, mapboxMap, transform, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener);
|
|
|
|
locationAnimatorCoordinator = new LocationAnimatorCoordinator(
|
|
mapboxMap.getProjection(),
|
|
MapboxAnimatorSetProvider.getInstance(),
|
|
MapboxAnimatorProvider.getInstance()
|
|
);
|
|
locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options
|
|
.trackingAnimationDurationMultiplier());
|
|
|
|
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
|
if (windowManager != null && sensorManager != null) {
|
|
compassEngine = new LocationComponentCompassEngine(windowManager, sensorManager);
|
|
}
|
|
staleStateManager = new StaleStateManager(onLocationStaleListener, options);
|
|
|
|
updateMapWithOptions(options);
|
|
|
|
setRenderMode(RenderMode.NORMAL);
|
|
setCameraMode(CameraMode.NONE);
|
|
|
|
onLocationLayerStart();
|
|
}
|
|
|
|
private void initializeLocationEngine(@NonNull Context context) {
|
|
if (this.locationEngine != null) {
|
|
this.locationEngine.removeLocationUpdates(currentLocationEngineListener);
|
|
}
|
|
setLocationEngine(internalLocationEngineProvider.getBestLocationEngine(context, false));
|
|
}
|
|
|
|
private void updateCompassListenerState(boolean canListen) {
|
|
if (compassEngine != null) {
|
|
if (!canListen) {
|
|
// We shouldn't listen, simply unregistering
|
|
removeCompassListener(compassEngine);
|
|
return;
|
|
}
|
|
|
|
if (!isComponentInitialized || !isComponentStarted || !isEnabled || !isLayerReady) {
|
|
return;
|
|
}
|
|
|
|
if (locationCameraController.isConsumingCompass() || locationLayerController.isConsumingCompass()) {
|
|
// If we have a consumer, and not yet listening, then start listening
|
|
if (!isListeningToCompass) {
|
|
isListeningToCompass = true;
|
|
compassEngine.addCompassListener(compassListener);
|
|
}
|
|
} else {
|
|
// If we have no consumers, stop listening
|
|
removeCompassListener(compassEngine);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void removeCompassListener(@NonNull CompassEngine engine) {
|
|
if (isListeningToCompass) {
|
|
isListeningToCompass = false;
|
|
engine.removeCompassListener(compassListener);
|
|
}
|
|
}
|
|
|
|
private void enableLocationComponent() {
|
|
isEnabled = true;
|
|
onLocationLayerStart();
|
|
}
|
|
|
|
private void disableLocationComponent() {
|
|
isEnabled = false;
|
|
locationLayerController.hide();
|
|
onLocationLayerStop();
|
|
}
|
|
|
|
private void updateMapWithOptions(@NonNull LocationComponentOptions options) {
|
|
int[] padding = options.padding();
|
|
if (padding != null) {
|
|
mapboxMap.setPadding(
|
|
padding[0], padding[1], padding[2], padding[3]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the user location icon.
|
|
*
|
|
* @param location the latest user location
|
|
*/
|
|
private void updateLocation(@Nullable final Location location, boolean fromLastLocation) {
|
|
updateLocation(location, null, fromLastLocation, false);
|
|
}
|
|
|
|
private void updateLocation(@Nullable final Location location, @Nullable List<Location> intermediatePoints,
|
|
boolean fromLastLocation, boolean lookAheadUpdate) {
|
|
if (location == null) {
|
|
return;
|
|
} else if (!isLayerReady) {
|
|
lastLocation = location;
|
|
return;
|
|
} else {
|
|
long currentTime = SystemClock.elapsedRealtime();
|
|
if (currentTime - lastUpdateTime < fastestInterval) {
|
|
return;
|
|
} else {
|
|
lastUpdateTime = currentTime;
|
|
}
|
|
}
|
|
|
|
showLocationLayerIfHidden();
|
|
|
|
if (!fromLastLocation) {
|
|
staleStateManager.updateLatestLocationTime();
|
|
}
|
|
CameraPosition currentCameraPosition = mapboxMap.getCameraPosition();
|
|
boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH;
|
|
if (intermediatePoints != null) {
|
|
locationAnimatorCoordinator.feedNewLocation(
|
|
getTargetLocationWithIntermediates(location, intermediatePoints),
|
|
currentCameraPosition,
|
|
isGpsNorth,
|
|
lookAheadUpdate);
|
|
} else {
|
|
locationAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth);
|
|
}
|
|
updateAccuracyRadius(location, false);
|
|
lastLocation = location;
|
|
}
|
|
|
|
private Location[] getTargetLocationWithIntermediates(Location location, List<Location> intermediatePoints) {
|
|
Location[] locations = new Location[intermediatePoints.size() + 1];
|
|
locations[locations.length - 1] = location;
|
|
for (int i = 0; i < intermediatePoints.size(); i++) {
|
|
locations[i] = intermediatePoints.get(i);
|
|
}
|
|
return locations;
|
|
}
|
|
|
|
private void showLocationLayerIfHidden() {
|
|
boolean isLocationLayerHidden = locationLayerController.isHidden();
|
|
if (isEnabled && isComponentStarted && isLocationLayerHidden) {
|
|
locationLayerController.show();
|
|
}
|
|
}
|
|
|
|
private void updateCompassHeading(float heading) {
|
|
locationAnimatorCoordinator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition());
|
|
}
|
|
|
|
/**
|
|
* If the locationEngine contains a last location value, we use it for the initial location layer
|
|
* position.
|
|
*/
|
|
@SuppressLint("MissingPermission")
|
|
private void setLastLocation() {
|
|
if (locationEngine != null) {
|
|
locationEngine.getLastLocation(lastLocationEngineListener);
|
|
} else {
|
|
updateLocation(getLastKnownLocation(), true);
|
|
}
|
|
}
|
|
|
|
private void setLastCompassHeading() {
|
|
updateCompassHeading(compassEngine != null ? compassEngine.getLastHeading() : 0);
|
|
}
|
|
|
|
@SuppressLint("MissingPermission")
|
|
private void updateLayerOffsets(boolean forceUpdate) {
|
|
if (useSpecializedLocationLayer) {
|
|
return;
|
|
}
|
|
|
|
CameraPosition position = mapboxMap.getCameraPosition();
|
|
if (lastCameraPosition == null || forceUpdate) {
|
|
lastCameraPosition = position;
|
|
locationLayerController.cameraBearingUpdated(position.bearing);
|
|
locationLayerController.cameraTiltUpdated(position.tilt);
|
|
updateAccuracyRadius(getLastKnownLocation(), true);
|
|
return;
|
|
}
|
|
|
|
if (position.bearing != lastCameraPosition.bearing) {
|
|
locationLayerController.cameraBearingUpdated(position.bearing);
|
|
}
|
|
if (position.tilt != lastCameraPosition.tilt) {
|
|
locationLayerController.cameraTiltUpdated(position.tilt);
|
|
}
|
|
if (position.zoom != lastCameraPosition.zoom) {
|
|
updateAccuracyRadius(getLastKnownLocation(), true);
|
|
}
|
|
lastCameraPosition = position;
|
|
}
|
|
|
|
private void updateAccuracyRadius(Location location, boolean noAnimation) {
|
|
float radius;
|
|
if (location == null) {
|
|
radius = 0;
|
|
} else if (useSpecializedLocationLayer) {
|
|
radius = location.getAccuracy();
|
|
} else {
|
|
radius = Utils.calculateZoomLevelRadius(mapboxMap, location);
|
|
}
|
|
locationAnimatorCoordinator.feedNewAccuracyRadius(radius, noAnimation);
|
|
}
|
|
|
|
private void updateAnimatorListenerHolders() {
|
|
Set<AnimatorListenerHolder> animationsValueChangeListeners = new HashSet<>();
|
|
animationsValueChangeListeners.addAll(locationLayerController.getAnimationListeners());
|
|
animationsValueChangeListeners.addAll(locationCameraController.getAnimationListeners());
|
|
locationAnimatorCoordinator.updateAnimatorListenerHolders(animationsValueChangeListeners);
|
|
locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(),
|
|
locationCameraController.getCameraMode() == CameraMode.TRACKING_GPS_NORTH);
|
|
locationAnimatorCoordinator.resetAllLayerAnimations();
|
|
}
|
|
|
|
@NonNull
|
|
private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() {
|
|
@Override
|
|
public void onCameraMove() {
|
|
updateLayerOffsets(false);
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private OnCameraIdleListener onCameraIdleListener = new OnCameraIdleListener() {
|
|
@Override
|
|
public void onCameraIdle() {
|
|
updateLayerOffsets(false);
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private OnMapClickListener onMapClickListener = new OnMapClickListener() {
|
|
@Override
|
|
public boolean onMapClick(@NonNull LatLng point) {
|
|
if (!onLocationClickListeners.isEmpty() && locationLayerController.onMapClick(point)) {
|
|
for (OnLocationClickListener listener : onLocationClickListeners) {
|
|
listener.onLocationComponentClick();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private MapboxMap.OnMapLongClickListener onMapLongClickListener = new MapboxMap.OnMapLongClickListener() {
|
|
@Override
|
|
public boolean onMapLongClick(@NonNull LatLng point) {
|
|
if (!onLocationLongClickListeners.isEmpty() && locationLayerController.onMapClick(point)) {
|
|
for (OnLocationLongClickListener listener : onLocationLongClickListeners) {
|
|
listener.onLocationComponentLongClick();
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private OnLocationStaleListener onLocationStaleListener = new OnLocationStaleListener() {
|
|
@Override
|
|
public void onStaleStateChange(boolean isStale) {
|
|
locationLayerController.setLocationsStale(isStale);
|
|
|
|
for (OnLocationStaleListener listener : onLocationStaleListeners) {
|
|
listener.onStaleStateChange(isStale);
|
|
}
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = new OnCameraMoveInvalidateListener() {
|
|
@Override
|
|
public void onInvalidateCameraMove() {
|
|
onCameraMoveListener.onCameraMove();
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private CompassListener compassListener = new CompassListener() {
|
|
@Override
|
|
public void onCompassChanged(float userHeading) {
|
|
updateCompassHeading(userHeading);
|
|
}
|
|
|
|
@Override
|
|
public void onCompassAccuracyChange(int compassStatus) {
|
|
// Currently don't handle this inside SDK
|
|
}
|
|
};
|
|
|
|
@VisibleForTesting
|
|
static final class CurrentLocationEngineCallback implements LocationEngineCallback<LocationEngineResult> {
|
|
private final WeakReference<LocationComponent> componentWeakReference;
|
|
|
|
CurrentLocationEngineCallback(LocationComponent component) {
|
|
this.componentWeakReference = new WeakReference<>(component);
|
|
}
|
|
|
|
@Override
|
|
public void onSuccess(LocationEngineResult result) {
|
|
LocationComponent component = componentWeakReference.get();
|
|
if (component != null) {
|
|
component.updateLocation(result.getLastLocation(), false);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull Exception exception) {
|
|
Logger.e(TAG, "Failed to obtain location update", exception);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
static final class LastLocationEngineCallback implements LocationEngineCallback<LocationEngineResult> {
|
|
private final WeakReference<LocationComponent> componentWeakReference;
|
|
|
|
LastLocationEngineCallback(LocationComponent component) {
|
|
this.componentWeakReference = new WeakReference<>(component);
|
|
}
|
|
|
|
@Override
|
|
public void onSuccess(LocationEngineResult result) {
|
|
LocationComponent component = componentWeakReference.get();
|
|
if (component != null) {
|
|
component.updateLocation(result.getLastLocation(), true);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull Exception exception) {
|
|
Logger.e(TAG, "Failed to obtain last location update", exception);
|
|
}
|
|
}
|
|
|
|
@NonNull
|
|
@VisibleForTesting
|
|
OnCameraTrackingChangedListener cameraTrackingChangedListener = new OnCameraTrackingChangedListener() {
|
|
@Override
|
|
public void onCameraTrackingDismissed() {
|
|
for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) {
|
|
listener.onCameraTrackingDismissed();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onCameraTrackingChanged(int currentMode) {
|
|
locationAnimatorCoordinator.cancelZoomAnimation();
|
|
locationAnimatorCoordinator.cancelTiltAnimation();
|
|
updateAnimatorListenerHolders();
|
|
for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) {
|
|
listener.onCameraTrackingChanged(currentMode);
|
|
}
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
@VisibleForTesting
|
|
OnRenderModeChangedListener renderModeChangedListener = new OnRenderModeChangedListener() {
|
|
@Override
|
|
public void onRenderModeChanged(int currentMode) {
|
|
updateAnimatorListenerHolders();
|
|
for (OnRenderModeChangedListener listener : onRenderModeChangedListeners) {
|
|
listener.onRenderModeChanged(currentMode);
|
|
}
|
|
}
|
|
};
|
|
|
|
@NonNull
|
|
private final MapboxMap.OnDeveloperAnimationListener developerAnimationListener =
|
|
new MapboxMap.OnDeveloperAnimationListener() {
|
|
@Override
|
|
public void onDeveloperAnimationStarted() {
|
|
if (isComponentInitialized && isEnabled) {
|
|
setCameraMode(CameraMode.NONE);
|
|
}
|
|
}
|
|
};
|
|
|
|
static class InternalLocationEngineProvider {
|
|
LocationEngine getBestLocationEngine(@NonNull Context context, boolean background) {
|
|
return LocationEngineProvider.getBestLocationEngine(context, background);
|
|
}
|
|
}
|
|
|
|
private void checkActivationState() {
|
|
if (!isComponentInitialized) {
|
|
throw new LocationComponentNotInitializedException();
|
|
}
|
|
}
|
|
|
|
private void notifyUnsuccessfulCameraOperation(@Nullable MapboxMap.CancelableCallback callback,
|
|
@Nullable String msg) {
|
|
if (msg != null) {
|
|
Logger.e(TAG, msg);
|
|
}
|
|
|
|
if (callback != null) {
|
|
callback.onCancel();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns whether the location component is activated.
|
|
*
|
|
* @return true if the component is activated, false otherwise
|
|
*/
|
|
public boolean isLocationComponentActivated() {
|
|
return isComponentInitialized;
|
|
}
|
|
}
|