diff --git a/Makefile b/Makefile index fd1a68a85..2e2e48d99 100644 --- a/Makefile +++ b/Makefile @@ -3,18 +3,23 @@ checkstyle: test: cd plugins; ./gradlew :traffic:test + cd plugins; ./gradlew :locationlayer:test build-release: cd plugins; ./gradlew :traffic:assembleRelease + cd plugins; ./gradlew :locationlayer:assembleRelease javadoc: # Android modules # Output is ./mapbox/*/build/docs/javadoc/release cd plugins; ./gradlew :traffic:javadocrelease + cd plugins; ./gradlew :locationlayer:javadocrelease publish: cd plugins; export IS_LOCAL_DEVELOPMENT=false; ./gradlew :traffic:uploadArchives + cd plugins; export IS_LOCAL_DEVELOPMENT=false; ./gradlew :locationlayer:uploadArchives publish-local: # This publishes to ~/.m2/repository/com/mapbox/mapboxsdk cd plugins; export IS_LOCAL_DEVELOPMENT=true; ./gradlew :traffic:uploadArchives + cd plugins; export IS_LOCAL_DEVELOPMENT=true; ./gradlew :locationlayer:uploadArchives diff --git a/plugins/app/build.gradle b/plugins/app/build.gradle index 96daefcf6..f2fbf71a1 100644 --- a/plugins/app/build.gradle +++ b/plugins/app/build.gradle @@ -65,6 +65,7 @@ dependencies { // Plugin modules compile project(':traffic') + compile project(':locationlayer') } apply from: '../checkstyle.gradle' diff --git a/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginAction.java b/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginAction.java new file mode 100644 index 000000000..8308def81 --- /dev/null +++ b/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginAction.java @@ -0,0 +1,46 @@ +package com.mapbox.mapboxsdk.plugins.locationlayer; + +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; +import android.view.View; + +import com.mapbox.mapboxsdk.maps.MapboxMap; + +import org.hamcrest.Matcher; + +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; + +class LocationLayerPluginAction implements ViewAction { + + private MapboxMap mapboxMap; + private LocationLayerPlugin locationLayerPlugin; + private onPerformLocationLayerAction onPerformLocationLayerAction; + + LocationLayerPluginAction(MapboxMap mapboxMap, LocationLayerPlugin locationLayerPlugin, + onPerformLocationLayerAction onPerformLocationLayerAction) { + this.locationLayerPlugin = locationLayerPlugin; + this.mapboxMap = mapboxMap; + this.onPerformLocationLayerAction = onPerformLocationLayerAction; + } + + @Override + public Matcher getConstraints() { + return isDisplayed(); + } + + @Override + public String getDescription() { + return getClass().getSimpleName(); + } + + @Override + public void perform(UiController uiController, View view) { + if (onPerformLocationLayerAction != null) { + onPerformLocationLayerAction.onLocationLayerAction(locationLayerPlugin, mapboxMap, uiController); + } + } + + interface onPerformLocationLayerAction { + void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, UiController uiController); + } +} \ No newline at end of file diff --git a/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.java b/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.java new file mode 100644 index 000000000..39df984d3 --- /dev/null +++ b/plugins/app/src/androidTest/java/com/mapbox/mapboxsdk/plugins/locationlayer/LocationLayerPluginTest.java @@ -0,0 +1,174 @@ +package com.mapbox.mapboxsdk.plugins.locationlayer; + +import android.support.test.espresso.Espresso; +import android.support.test.espresso.IdlingResourceTimeoutException; +import android.support.test.espresso.UiController; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import com.mapbox.mapboxsdk.constants.Style; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.plugins.testapp.activity.location.LocationLayerModesActivity; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.utils.OnMapReadyIdlingResource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import timber.log.Timber; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +@SuppressWarnings( {"MissingPermission"}) +public class LocationLayerPluginTest { + + private static final double DELTA = 1E-10; + + @Rule + public ActivityTestRule rule = new ActivityTestRule<>(LocationLayerModesActivity.class); + + private OnMapReadyIdlingResource idlingResource; + private MapboxMap mapboxMap; + private LocationLayerPlugin locationLayerPlugin; + + @Before + public void beforeTest() { + try { + Timber.e("@Before: register idle resource"); + idlingResource = new OnMapReadyIdlingResource(rule.getActivity()); + Espresso.registerIdlingResources(idlingResource); + onView(withId(android.R.id.content)).check(matches(isDisplayed())); + mapboxMap = idlingResource.getMapboxMap(); + locationLayerPlugin = rule.getActivity().getLocationLayerPlugin(); + } catch (IdlingResourceTimeoutException idlingResourceTimeoutException) { + Timber.e("Idling resource timed out. Couldn't not validate if map is ready."); + throw new RuntimeException("Could not start executeLocationLayerTest for " + + this.getClass().getSimpleName() + ".\n The ViewHierarchy doesn't contain a view with resource id =" + + "R.id.mapView or \n the Activity doesn't contain an instance variable with a name equal to mapboxMap.\n"); + } + } + + @Test + public void sanity() throws Exception { + assertTrue(mapboxMap != null); + assertTrue(locationLayerPlugin != null); + } + + @Test + public void locationSourceAdded() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getSource(LocationLayerConstants.LOCATION_SOURCE) != null); + } + }); + } + + @Test + public void locationAccuracySourceAdded() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getSource(LocationLayerConstants.LOCATION_ACCURACY_SOURCE) != null); + } + }); + } + + @Test + public void locationTrackingLayersAdded() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_ACCURACY_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_BACKGROUND_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER) != null); + } + }); + } + + @Test + public void locationBearingLayersAdded() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.COMPASS); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_ACCURACY_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_BACKGROUND_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_BEARING_LAYER) != null); + } + }); + } + + @Test + public void locationNavigationLayersAdded() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.COMPASS); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_NAVIGATION_LAYER) != null); + } + }); + } + + @Test + public void locationLayerModeCorrectlySetToNone() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER) != null); + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.NONE); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER).getVisibility().getValue() + .equals(Property.NONE)); + } + }); + } + + @Test + public void onMapChangeLocationLayerRedrawn() throws Exception { + executeLocationLayerTest(new LocationLayerPluginAction.onPerformLocationLayerAction() { + @Override + public void onLocationLayerAction(LocationLayerPlugin locationLayerPlugin, MapboxMap mapboxMap, + UiController uiController) { + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER) != null); + mapboxMap.setStyleUrl(Style.SATELLITE); + uiController.loopMainThreadForAtLeast(500); + assertEquals(locationLayerPlugin.getLocationLayerMode(), LocationLayerMode.TRACKING); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER) != null); + assertTrue(mapboxMap.getLayer(LocationLayerConstants.LOCATION_LAYER).getVisibility().getValue() + .equals(Property.VISIBLE)); + } + }); + } + + @After + public void afterTest() { + Timber.e("@After: unregister idle resource"); + Espresso.unregisterIdlingResources(idlingResource); + } + + public void executeLocationLayerTest(LocationLayerPluginAction.onPerformLocationLayerAction listener) { + onView(withId(android.R.id.content)).perform(new LocationLayerPluginAction(mapboxMap, locationLayerPlugin, + listener)); + } +} diff --git a/plugins/app/src/main/AndroidManifest.xml b/plugins/app/src/main/AndroidManifest.xml index 95bf382cc..0df434116 100644 --- a/plugins/app/src/main/AndroidManifest.xml +++ b/plugins/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/Utils.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/Utils.java new file mode 100644 index 000000000..3f4045322 --- /dev/null +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/Utils.java @@ -0,0 +1,60 @@ +package com.mapbox.mapboxsdk.plugins.testapp; + +import android.location.Location; + +import com.mapbox.mapboxsdk.constants.Style; + +import java.util.Random; + +import timber.log.Timber; + +/** + * Useful utilities used throughout the testapp. + */ +public class Utils { + + private static final String[] STYLES = new String[] { + Style.MAPBOX_STREETS, + Style.OUTDOORS, + Style.LIGHT, + Style.DARK, + Style.SATELLITE_STREETS, + Style.TRAFFIC_DAY, + Style.TRAFFIC_NIGHT + }; + + private static int index; + + /** + * Utility to cycle through map styles. Useful to test if runtime styling source and layers transfer over to new + * style. + * + * @return a string ID representing the map style + */ + public static String getNextStyle() { + index++; + if (index == STYLES.length) { + index = 0; + } + return STYLES[index]; + } + + /** + * Utility for getting a random coordinate inside a provided bounding box and creates a {@link Location} from it. + * + * @param bbox double array forming the bounding box in the order of {@code [minx, miny, maxx, maxy]} + * @return a {@link Location} object using the random coordinate + */ + public static Location getRandomLocation(double[] bbox) { + Random random = new Random(); + + double randomLat = bbox[1] + (bbox[3] - bbox[1]) * random.nextDouble(); + double randomLon = bbox[0] + (bbox[2] - bbox[0]) * random.nextDouble(); + + Location location = new Location("random-loc"); + location.setLongitude(randomLon); + location.setLatitude(randomLat); + Timber.d("getRandomLatLng: %s", location.toString()); + return location; + } +} \ No newline at end of file diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/FeatureOverviewActivity.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/FeatureOverviewActivity.java index 81fbd010c..d316d5be0 100644 --- a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/FeatureOverviewActivity.java +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/FeatureOverviewActivity.java @@ -24,8 +24,11 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import android.widget.Toast; import com.mapbox.mapboxsdk.plugins.testapp.R; +import com.mapbox.services.android.telemetry.permissions.PermissionsListener; +import com.mapbox.services.android.telemetry.permissions.PermissionsManager; import java.util.ArrayList; import java.util.Arrays; @@ -39,13 +42,14 @@ /** * Activity showing a RecyclerView with Activities generated from AndroidManifest.xml */ -public class FeatureOverviewActivity extends AppCompatActivity { +public class FeatureOverviewActivity extends AppCompatActivity implements PermissionsListener { private static final String KEY_STATE_FEATURES = "featureList"; private RecyclerView recyclerView; private FeatureSectionAdapter sectionAdapter; private List features; + private PermissionsManager permissionsManager; @Override protected void onCreate(Bundle savedInstanceState) { @@ -74,6 +78,13 @@ public void onItemClicked(RecyclerView recyclerView, int position, View view) { features = savedInstanceState.getParcelableArrayList(KEY_STATE_FEATURES); onFeaturesLoaded(features); } + + // Check for location permission + permissionsManager = new PermissionsManager(this); + if (!PermissionsManager.areLocationPermissionsGranted(this)) { + recyclerView.setEnabled(false); + permissionsManager.requestLocationPermissions(this); + } } private void loadFeatures() { @@ -112,6 +123,27 @@ private void startFeature(Feature feature) { startActivity(intent); } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + + @Override + public void onExplanationNeeded(List permissionsToExplain) { + Toast.makeText(this, "This app needs location permissions in order to show its functionality.", + Toast.LENGTH_LONG).show(); + } + + @Override + public void onPermissionResult(boolean granted) { + if (granted) { + recyclerView.setEnabled(true); + } else { + Toast.makeText(this, "You didn't grant location permissions.", + Toast.LENGTH_LONG).show(); + } + } + @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); @@ -350,7 +382,6 @@ public void onItemRangeRemoved(int positionStart, int itemCount) { }); } - @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) { if (typeView == SECTION_TYPE) { diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/TrafficActivity.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/TrafficActivity.java index 154e9cecd..ba90b2d55 100644 --- a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/TrafficActivity.java +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/TrafficActivity.java @@ -4,11 +4,11 @@ import android.support.design.widget.FloatingActionButton; import android.support.v7.app.AppCompatActivity; -import com.mapbox.mapboxsdk.constants.Style; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.plugins.testapp.R; +import com.mapbox.mapboxsdk.plugins.testapp.Utils; import com.mapbox.mapboxsdk.plugins.traffic.TrafficPlugin; import butterknife.BindView; @@ -32,7 +32,6 @@ public class TrafficActivity extends AppCompatActivity implements OnMapReadyCall private MapboxMap mapboxMap; private TrafficPlugin trafficPlugin; - private StyleCycle styleCycle = new StyleCycle(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -40,7 +39,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.activity_traffic); ButterKnife.bind(this); - mapView.setStyleUrl(styleCycle.getStyle()); + mapView.setStyleUrl(Utils.getNextStyle()); mapView.onCreate(savedInstanceState); mapView.getMapAsync(this); } @@ -63,7 +62,7 @@ public void onTrafficFabClick() { @OnClick(R.id.fabStyles) public void onStyleFabClick() { if (mapboxMap != null) { - mapboxMap.setStyleUrl(styleCycle.getNextStyle()); + mapboxMap.setStyleUrl(Utils.getNextStyle()); } } @@ -112,28 +111,4 @@ public void onLowMemory() { public TrafficPlugin getTrafficPlugin() { return trafficPlugin; } - - private static class StyleCycle { - private static final String[] STYLES = new String[] { - Style.MAPBOX_STREETS, - Style.OUTDOORS, - Style.LIGHT, - Style.DARK, - Style.SATELLITE_STREETS - }; - - private int index; - - private String getNextStyle() { - index++; - if (index == STYLES.length) { - index = 0; - } - return getStyle(); - } - - private String getStyle() { - return STYLES[index]; - } - } } \ No newline at end of file diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerMapChangeActivity.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerMapChangeActivity.java new file mode 100644 index 000000000..5907b7d35 --- /dev/null +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerMapChangeActivity.java @@ -0,0 +1,113 @@ +package com.mapbox.mapboxsdk.plugins.testapp.activity.location; + +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerMode; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.plugins.testapp.R; +import com.mapbox.mapboxsdk.plugins.testapp.Utils; +import com.mapbox.services.android.location.LostLocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class LocationLayerMapChangeActivity extends AppCompatActivity implements OnMapReadyCallback { + + @BindView(R.id.mapView) + MapView mapView; + @BindView(R.id.fabStyles) + FloatingActionButton stylesFab; + + private LocationLayerPlugin locationPlugin; + private LocationEngine locationEngine; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_location_layer_map_change); + ButterKnife.bind(this); + + mapView.setStyleUrl(Utils.getNextStyle()); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + locationEngine = LostLocationEngine.getLocationEngine(this); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngine.activate(); + locationPlugin = new LocationLayerPlugin(mapView, mapboxMap, locationEngine); + locationPlugin.setLocationLayerEnabled(LocationLayerMode.COMPASS); + } + + @OnClick(R.id.fabStyles) + public void onStyleFabClick() { + if (mapboxMap != null) { + mapboxMap.setStyleUrl(Utils.getNextStyle()); + locationPlugin.applyStyle(R.style.CustomLocationLayer); + } + } + + @Override + protected void onStart() { + super.onStart(); + if (locationPlugin != null) { + locationPlugin.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(); + locationPlugin.onStop(); + if (locationEngine != null) { + locationEngine.removeLocationUpdates(); + } + mapView.onStop(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + if (locationEngine != null) { + locationEngine.deactivate(); + } + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } +} \ No newline at end of file diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java new file mode 100644 index 000000000..d40123195 --- /dev/null +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/LocationLayerModesActivity.java @@ -0,0 +1,170 @@ +package com.mapbox.mapboxsdk.plugins.testapp.activity.location; + +import android.arch.lifecycle.LifecycleRegistry; +import android.arch.lifecycle.LifecycleRegistryOwner; +import android.location.Location; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerMode; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.plugins.testapp.R; +import com.mapbox.services.android.location.LostLocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; + +public class LocationLayerModesActivity extends AppCompatActivity implements OnMapReadyCallback, + LifecycleRegistryOwner, LocationEngineListener { + + @BindView(R.id.mapView) + MapView mapView; + + private LocationLayerPlugin locationLayerPlugin; + private LifecycleRegistry lifecycleRegistry; + private LocationEngine locationEngine; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_location_layer_mode); + ButterKnife.bind(this); + lifecycleRegistry = new LifecycleRegistry(this); + + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @SuppressWarnings( {"MissingPermission"}) + @OnClick(R.id.button_location_mode_none) + public void locationModeNone(View view) { + if (locationLayerPlugin == null) { + return; + } + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.NONE); + } + + @SuppressWarnings( {"MissingPermission"}) + @OnClick(R.id.button_location_mode_compass) + public void locationModeCompass(View view) { + if (locationLayerPlugin == null) { + return; + } + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.COMPASS); + } + + @SuppressWarnings( {"MissingPermission"}) + @OnClick(R.id.button_location_mode_tracking) + public void locationModeTracking(View view) { + if (locationLayerPlugin == null) { + return; + } + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + } + + @SuppressWarnings( {"MissingPermission"}) + @OnClick(R.id.button_location_mode_navigation) + public void locationModeNavigation(View view) { + if (locationLayerPlugin == null) { + return; + } + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.NAVIGATION); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + locationEngine = new LostLocationEngine(this); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngine.addLocationEngineListener(this); + locationEngine.activate(); + locationLayerPlugin = new LocationLayerPlugin(mapView, mapboxMap, locationEngine); + getLifecycle().addObserver(locationLayerPlugin); + } + + public LocationLayerPlugin getLocationLayerPlugin() { + return locationLayerPlugin; + } + + @Override + public LifecycleRegistry getLifecycle() { + return lifecycleRegistry; + } + + @Override + @SuppressWarnings( {"MissingPermission"}) + protected void onStart() { + super.onStart(); + mapView.onStart(); + if (locationEngine != null) { + locationEngine.requestLocationUpdates(); + locationEngine.addLocationEngineListener(this); + } + } + + @Override + protected void onResume() { + super.onResume(); + mapView.onResume(); + } + + @Override + protected void onPause() { + super.onPause(); + mapView.onPause(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + if (locationEngine != null) { + locationEngine.removeLocationEngineListener(this); + locationEngine.removeLocationUpdates(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + if (locationEngine != null) { + locationEngine.deactivate(); + } + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + @SuppressWarnings( {"MissingPermission"}) + public void onConnected() { + locationEngine.requestLocationUpdates(); + } + + @Override + public void onLocationChanged(Location location) { + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom( + new LatLng(location.getLatitude(), location.getLongitude()), 16)); + } +} \ No newline at end of file diff --git a/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/ManualLocationUpdatesActivity.java b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/ManualLocationUpdatesActivity.java new file mode 100644 index 000000000..782927ad2 --- /dev/null +++ b/plugins/app/src/main/java/com/mapbox/mapboxsdk/plugins/testapp/activity/location/ManualLocationUpdatesActivity.java @@ -0,0 +1,146 @@ +package com.mapbox.mapboxsdk.plugins.testapp.activity.location; + +import android.arch.lifecycle.LifecycleRegistry; +import android.arch.lifecycle.LifecycleRegistryOwner; +import android.location.Location; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v7.app.AppCompatActivity; +import android.view.View; + +import com.mapbox.mapboxsdk.maps.MapView; +import com.mapbox.mapboxsdk.maps.MapboxMap; +import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerMode; +import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin; +import com.mapbox.mapboxsdk.plugins.testapp.R; +import com.mapbox.mapboxsdk.plugins.testapp.Utils; +import com.mapbox.services.android.location.LostLocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.android.telemetry.location.LocationEnginePriority; + +import java.util.Locale; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import timber.log.Timber; + +public class ManualLocationUpdatesActivity extends AppCompatActivity implements OnMapReadyCallback, + LocationEngineListener, LifecycleRegistryOwner { + + @BindView(R.id.mapView) + MapView mapView; + @BindView(R.id.fabToggleManualLocation) + FloatingActionButton fabToggleManualLocation; + + private LocationLayerPlugin locationLayerPlugin; + private LifecycleRegistry lifecycleRegistry; + private LocationEngine locationEngine; + private MapboxMap mapboxMap; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_location_manual_update); + ButterKnife.bind(this); + lifecycleRegistry = new LifecycleRegistry(this); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + } + + @OnClick(R.id.fabToggleManualLocation) + public void toggleManualLocationFabClick(View view) { + toggleManualLocation(); + } + + @OnClick(R.id.fabManualLocationChange) + public void manualLocationChangeFabClick(View view) { + if (locationLayerPlugin != null) { + locationLayerPlugin.forceLocationUpdate( + Utils.getRandomLocation(new double[] {-77.1825, 38.7825, -76.9790, 39.0157})); + } + } + + private void toggleManualLocation() { + locationLayerPlugin.setLocationEngine(locationLayerPlugin.getLocationEngine() == null ? locationEngine : null); + fabToggleManualLocation.setImageResource(locationLayerPlugin.getLocationEngine() == null + ? R.drawable.ic_location : R.drawable.ic_location_disabled); + } + + @Override + @SuppressWarnings( {"MissingPermission"}) + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + locationEngine = new LostLocationEngine(this); + locationEngine.addLocationEngineListener(this); + locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY); + locationEngine.activate(); + locationLayerPlugin = new LocationLayerPlugin(mapView, mapboxMap, null); + locationLayerPlugin.setLocationLayerEnabled(LocationLayerMode.TRACKING); + getLifecycle().addObserver(locationLayerPlugin); + } + + public LocationLayerPlugin getLocationLayerPlugin() { + return locationLayerPlugin; + } + + @Override + public LifecycleRegistry getLifecycle() { + return lifecycleRegistry; + } + + @Override + @SuppressWarnings( {"MissingPermission"}) + public void onConnected() { + locationEngine.requestLocationUpdates(); + } + + @Override + public void onLocationChanged(Location location) { + Timber.d(String.format(Locale.US, "Location change occurred: %s", location.toString())); + } + + @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(); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } +} \ No newline at end of file diff --git a/plugins/app/src/main/res/drawable/custom_user_icon.xml b/plugins/app/src/main/res/drawable/custom_user_icon.xml new file mode 100644 index 000000000..37f1d4de0 --- /dev/null +++ b/plugins/app/src/main/res/drawable/custom_user_icon.xml @@ -0,0 +1,10 @@ + + + diff --git a/plugins/app/src/main/res/drawable/custom_user_stroke_icon.xml b/plugins/app/src/main/res/drawable/custom_user_stroke_icon.xml new file mode 100644 index 000000000..90946efee --- /dev/null +++ b/plugins/app/src/main/res/drawable/custom_user_stroke_icon.xml @@ -0,0 +1,10 @@ + + + diff --git a/plugins/app/src/main/res/drawable/ic_location.xml b/plugins/app/src/main/res/drawable/ic_location.xml new file mode 100644 index 000000000..a1e7c4a27 --- /dev/null +++ b/plugins/app/src/main/res/drawable/ic_location.xml @@ -0,0 +1,9 @@ + + + diff --git a/plugins/app/src/main/res/drawable/ic_location_disabled.xml b/plugins/app/src/main/res/drawable/ic_location_disabled.xml new file mode 100644 index 000000000..dad99eb25 --- /dev/null +++ b/plugins/app/src/main/res/drawable/ic_location_disabled.xml @@ -0,0 +1,9 @@ + + + diff --git a/plugins/app/src/main/res/layout/activity_location_layer_map_change.xml b/plugins/app/src/main/res/layout/activity_location_layer_map_change.xml new file mode 100644 index 000000000..d0e7c693d --- /dev/null +++ b/plugins/app/src/main/res/layout/activity_location_layer_map_change.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/plugins/app/src/main/res/layout/activity_location_layer_mode.xml b/plugins/app/src/main/res/layout/activity_location_layer_mode.xml new file mode 100644 index 000000000..0c9618992 --- /dev/null +++ b/plugins/app/src/main/res/layout/activity_location_layer_mode.xml @@ -0,0 +1,72 @@ + + + + + + + +