[android] Adding builder pattern for LocationComponent activation (#13941)

* initial additions of activation builder

* [android] refactoring LocationComponentActivationOptions based on code review

* [android] initial addition of LocationComponentActivationOptionsTest unit tests

* [android] more refactoring based on code review

* [android] refactoring test app location examples

* [android] espresso test refactor with LocationComponentActivationOptions usage

* [android] locationComponent builder instrumentation test tweaks and log removal

* [android] method and docs refactoring based @lukaspaczos 's review

* [android] locationEngine setting logic refactor

* [android] add null locationEngine to stillStaleAfterResuming test

* added .locationEngine(null) to other tests

* [android] javadocs, method, and test adjustments based on @lukaspaczos review

* [android] test tweaks based on Lukasz review
This commit is contained in:
Langston Smith
2019-03-05 11:05:06 -08:00
committed by GitHub
parent b29704cd1f
commit 7e4824960c
14 changed files with 984 additions and 147 deletions

View File

@@ -217,8 +217,10 @@ public final class LocationComponent {
*
* @param context the context
* @param style the proxy object for current map style. More info at {@link Style}
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style) {
activateLocationComponent(context, style,
LocationComponentOptions.createFromAttributes(context, R.style.mapbox_LocationComponent));
@@ -232,8 +234,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
boolean useDefaultLocationEngine) {
if (useDefaultLocationEngine) {
@@ -252,8 +256,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
boolean useDefaultLocationEngine,
@NonNull LocationEngineRequest locationEngineRequest) {
@@ -275,8 +281,10 @@ public final class LocationComponent {
* there should be no location engine initialized
* @param locationEngineRequest the location request
* @param options the options
* @deprecated use {@link LocationComponentActivationOptions.Builder} instead
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
boolean useDefaultLocationEngine,
@NonNull LocationEngineRequest locationEngineRequest,
@@ -298,8 +306,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style, @StyleRes int styleRes) {
activateLocationComponent(context, style, LocationComponentOptions.createFromAttributes(context, styleRes));
}
@@ -314,8 +324,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@NonNull LocationComponentOptions options) {
initialize(context, style, options);
@@ -331,8 +343,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine, @StyleRes int styleRes) {
activateLocationComponent(context, style, locationEngine,
@@ -348,8 +362,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine,
@NonNull LocationEngineRequest locationEngineRequest, @StyleRes int styleRes) {
@@ -363,8 +379,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine) {
activateLocationComponent(context, style, locationEngine, R.style.mapbox_LocationComponent);
@@ -377,8 +395,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine,
@NonNull LocationEngineRequest locationEngineRequest) {
@@ -392,8 +412,10 @@ public final class LocationComponent {
* @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
*/
@RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION})
@Deprecated
public void activateLocationComponent(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine,
@NonNull LocationComponentOptions options) {
@@ -411,7 +433,9 @@ public final class LocationComponent {
* @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,
@@ -422,6 +446,50 @@ public final class LocationComponent {
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(), 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.
*

View File

@@ -0,0 +1,244 @@
package com.mapbox.mapboxsdk.location;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.android.core.location.LocationEngineRequest;
import com.mapbox.mapboxsdk.maps.Style;
/**
* A class which holds the various options for activating the Maps SDK's {@link LocationComponent} to eventually
* show the device location on the map.
*/
public class LocationComponentActivationOptions {
private final Context context;
private final Style style;
private final LocationEngine locationEngine;
private final LocationEngineRequest locationEngineRequest;
private final LocationComponentOptions locationComponentOptions;
private final int styleRes;
private final boolean useDefaultLocationEngine;
private LocationComponentActivationOptions(@NonNull Context context, @NonNull Style style,
@Nullable LocationEngine locationEngine,
@Nullable LocationEngineRequest locationEngineRequest,
@Nullable LocationComponentOptions locationComponentOptions,
int styleRes,
boolean useDefaultLocationEngine) {
this.context = context;
this.style = style;
this.locationEngine = locationEngine;
this.locationEngineRequest = locationEngineRequest;
this.locationComponentOptions = locationComponentOptions;
this.styleRes = styleRes;
this.useDefaultLocationEngine = useDefaultLocationEngine;
}
/**
* Convenience method to retrieve a {@link LocationComponentActivationOptions} object which is ready to build with
*
* @return a builder object
*/
@NonNull
public static Builder builder(@NonNull Context context, @NonNull Style fullyLoadedMapStyle) {
return new LocationComponentActivationOptions.Builder(context, fullyLoadedMapStyle);
}
/**
* The application's current context
*
* @return the application's current context
*/
@NonNull
public Context context() {
return context;
}
/**
* The proxy object for current map style. More info at {@link Style}
*
* @return the map's fully loaded Style object
*/
@NonNull
public Style style() {
return style;
}
/**
* The {@link LocationEngine} which the {@link LocationComponent} should use
*
* @return the engine
*/
@Nullable
public LocationEngine locationEngine() {
return locationEngine;
}
/**
* The location request which the {@link LocationComponent} should use
*
* @return the LocationEngineRequest object
*/
@Nullable
public LocationEngineRequest locationEngineRequest() {
return locationEngineRequest;
}
/**
* A built {@link LocationComponentOptions} object, which holds the various {@link LocationComponent} styling options
*
* @return the options for styling the actual LocationComponent
*/
@Nullable
public LocationComponentOptions locationComponentOptions() {
return locationComponentOptions;
}
/**
* The LocationComponent style resource (e.g. R.style.style_name)
*
* @return the style resource.
*/
public int styleRes() {
return styleRes;
}
/**
* True if you want to initialize and use the built-in location engine or false if there should be no
* location engine initialized
*
* @return whether the default LocationEngine is used
*/
public boolean useDefaultLocationEngine() {
return useDefaultLocationEngine;
}
/**
* Builder class for constructing a new instance of {@link LocationComponentActivationOptions}.
*/
public static class Builder {
private final Context context;
private final Style style;
private LocationEngine locationEngine;
private LocationEngineRequest locationEngineRequest;
private LocationComponentOptions locationComponentOptions;
private int styleRes;
/**
* Set to true as the default in case a true/false value isn't declared via the builder's
* {@link LocationComponentActivationOptions#useDefaultLocationEngine()} method.
* <p>
* Please be aware that this activation boolean is ignored when a non-null
* {@link LocationEngine} is set via the builder's `locationEngine()` method.
*/
private boolean useDefaultLocationEngine = true;
/**
* Constructor for the {@link LocationComponentActivationOptions} builder class.
* While other activation options are optional, the activation process always requires
* context and a fully-loaded map {@link Style} object, which is why the two are in this
* constructor.
*/
public Builder(@NonNull Context context, @NonNull Style style) {
this.context = context;
this.style = style;
}
/**
* Deliver your own {@link LocationEngine} to the LocationComponent.
* <p>
* The true/false
* {@link LocationComponentActivationOptions#builder(Context, Style)#useDefaultLocationEngine}
* activation option is ignored when a non-null {@link LocationEngine} is set via
* this `locationEngine()` method.
*
* @param locationEngine a {@link LocationEngine} object
* @return the {@link Builder} object being constructed
*/
@NonNull
public Builder locationEngine(@Nullable LocationEngine locationEngine) {
this.locationEngine = locationEngine;
return this;
}
/**
* @param locationEngineRequest the location request which the
* {@link LocationComponent} should use
* @return the {@link Builder} object being constructed
*/
public Builder locationEngineRequest(LocationEngineRequest locationEngineRequest) {
this.locationEngineRequest = locationEngineRequest;
return this;
}
/**
* @param locationComponentOptions a built {@link LocationComponentOptions} object,
* which holds the various {@link LocationComponent}
* styling options
* @return the {@link Builder} object being constructed
*/
public Builder locationComponentOptions(LocationComponentOptions locationComponentOptions) {
this.locationComponentOptions = locationComponentOptions;
return this;
}
/**
* @param styleRes the LocationComponent style resource (e.g. R.style.style_name)
* @return the {@link Builder} object being constructed
*/
public Builder styleRes(int styleRes) {
this.styleRes = styleRes;
return this;
}
/**
* @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
* This is ignored when null is set as the parameter
* for {@link LocationComponentActivationOptions#builder
* (Context, Style)#locationEngine()}.
* @return the {@link Builder} object being constructed
*/
public Builder useDefaultLocationEngine(boolean useDefaultLocationEngine) {
this.useDefaultLocationEngine = useDefaultLocationEngine;
return this;
}
/**
* Method which actually builds the {@link LocationComponentActivationOptions} object while
* taking the various options into account.
*
* @return a built {@link LocationComponentActivationOptions} object
*/
public LocationComponentActivationOptions build() {
if (styleRes != 0 && locationComponentOptions != null) {
throw new IllegalArgumentException(
"You've provided both a style resource and a LocationComponentOptions object to the "
+ "LocationComponentActivationOptions builder. You can't use both and "
+ "you must choose one of the two to style the LocationComponent.");
}
if (context == null) {
throw new NullPointerException(
"Context in LocationComponentActivationOptions is null.");
}
if (style == null) {
throw new NullPointerException(
"Style in LocationComponentActivationOptions is null. Make sure the "
+ "Style object isn't null. Wait for the map to fully load before passing "
+ "the Style object to LocationComponentActivationOptions.");
}
if (!style.isFullyLoaded()) {
throw new IllegalArgumentException(
"Style in LocationComponentActivationOptions isn't fully loaded. Wait for the "
+ "map to fully load before passing the Style object to "
+ "LocationComponentActivationOptions.");
}
return new LocationComponentActivationOptions(context, style, locationEngine,
locationEngineRequest, locationComponentOptions, styleRes, useDefaultLocationEngine);
}
}
}