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:
Langston Smith
2020-04-30 03:28:36 -07:00
committed by GitHub
parent 6fd498ed16
commit 9bfe77df40
32 changed files with 1772 additions and 213 deletions

View File

@@ -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 {

View File

@@ -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()
}
}

View File

@@ -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"

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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 {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>