diff options
author | danesfeder <dan.nesfeder@mapbox.com> | 2017-10-11 11:15:10 -0400 |
---|---|---|
committer | danesfeder <dan.nesfeder@mapbox.com> | 2017-10-11 11:15:10 -0400 |
commit | 68c78d5b5b886b2da9dfc45a211116e62bd17f55 (patch) | |
tree | 504c146e37b904a9cc5f380f9c4a103fd439516e | |
parent | 98a47884f06a8f165a2c15a54f82b356c8ef23d8 (diff) | |
download | qtlocation-mapboxgl-68c78d5b5b886b2da9dfc45a211116e62bd17f55.tar.gz |
Add navigation view example
7 files changed, 396 insertions, 4 deletions
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle index 67939b5144..2e14478fb5 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/build.gradle +++ b/platform/android/MapboxGLAndroidSDKTestApp/build.gradle @@ -60,6 +60,17 @@ dependencies { compile rootProject.ext.dep.supportAppcompatV7 compile rootProject.ext.dep.supportRecyclerView + // Navigation SDK UI + compile ('com.mapbox.mapboxsdk:mapbox-android-navigation-ui:0.7.0-SNAPSHOT@aar') { + transitive = true + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + + // Location Layer Plugin + compile ('com.mapbox.mapboxsdk:mapbox-android-plugin-locationlayer:0.1.0@aar') { + exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' + } + // Leak Canary debugCompile rootProject.ext.dep.leakCanaryDebug releaseCompile rootProject.ext.dep.leakCanaryRelease diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index bf97749b9e..ced18e012e 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -24,6 +24,17 @@ </intent-filter> </activity> <activity + android:name=".activity.NavigationViewActivity" + android:description="@string/navigation_view" + android:label="@string/navigation_view"> + <meta-data + android:name="@string/category" + android:value="@string/category_basic"/> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".activity.FeatureOverviewActivity"/> + </activity> + <activity android:name=".activity.infowindow.InfoWindowActivity" android:description="@string/description_info_window" android:label="@string/activity_info_window"> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/NavigationViewActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/NavigationViewActivity.java new file mode 100644 index 0000000000..72ac7c894e --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/NavigationViewActivity.java @@ -0,0 +1,317 @@ +package com.mapbox.mapboxsdk.testapp.activity; + +import android.location.Location; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.design.widget.BaseTransientBottomBar; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.view.View; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.ProgressBar; +import android.widget.Switch; +import android.widget.Toast; + +import com.mapbox.mapboxsdk.Mapbox; +import com.mapbox.mapboxsdk.annotations.Marker; +import com.mapbox.mapboxsdk.annotations.MarkerViewOptions; +import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; +import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.location.LocationSource; +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.testapp.R; +import com.mapbox.services.Constants; +import com.mapbox.services.android.navigation.ui.v5.NavigationLauncher; +import com.mapbox.services.android.navigation.ui.v5.route.NavigationMapRoute; +import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute; +import com.mapbox.services.android.telemetry.location.LocationEngine; +import com.mapbox.services.android.telemetry.location.LocationEngineListener; +import com.mapbox.services.api.directions.v5.models.DirectionsResponse; +import com.mapbox.services.api.directions.v5.models.DirectionsRoute; +import com.mapbox.services.commons.geojson.LineString; +import com.mapbox.services.commons.models.Position; + +import java.util.ArrayList; +import java.util.List; + +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; +import timber.log.Timber; + +import static com.mapbox.services.android.telemetry.location.LocationEnginePriority.HIGH_ACCURACY; + +public class NavigationViewActivity extends AppCompatActivity implements OnMapReadyCallback, + MapboxMap.OnMapLongClickListener, LocationEngineListener, Callback<DirectionsResponse> { + + private static final int CAMERA_ANIMATION_DURATION = 1000; + + private LocationLayerPlugin locationLayer; + private LocationEngine locationEngine; + private NavigationMapRoute mapRoute; + private MapboxMap mapboxMap; + + private MapView mapView; + private Button launchRouteBtn; + private ProgressBar loading; + private Switch demoSwitch; + + private Marker currentMarker; + private Position currentPosition; + private Position destination; + private DirectionsRoute route; + + private boolean locationFound; + private boolean shouldSimulateRoute; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_navigation_view); + bind(); + mapView.onCreate(savedInstanceState); + mapView.getMapAsync(this); + demoSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { + shouldSimulateRoute = checked; + } + }); + launchRouteBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + launchNavigationWithRoute(); + } + }); + } + + private void bind() { + mapView = findViewById(R.id.mapView); + launchRouteBtn = findViewById(R.id.launchRouteBtn); + loading = findViewById(R.id.loading); + demoSwitch = findViewById(R.id.demoSwitch); + } + + @SuppressWarnings({"MissingPermission"}) + @Override + protected void onStart() { + super.onStart(); + mapView.onStart(); + if (locationLayer != null) { + locationLayer.onStart(); + } + } + + @SuppressWarnings({"MissingPermission"}) + @Override + public void onResume() { + super.onResume(); + mapView.onResume(); + if (locationEngine != null) { + locationEngine.addLocationEngineListener(this); + if (!locationEngine.isConnected()) { + locationEngine.activate(); + } + } + } + + @Override + public void onPause() { + super.onPause(); + mapView.onPause(); + if (locationEngine != null) { + locationEngine.removeLocationEngineListener(this); + } + } + + @Override + public void onLowMemory() { + super.onLowMemory(); + mapView.onLowMemory(); + } + + @Override + protected void onStop() { + super.onStop(); + mapView.onStop(); + if (locationLayer != null) { + locationLayer.onStop(); + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mapView.onDestroy(); + if (locationEngine != null) { + locationEngine.removeLocationUpdates(); + locationEngine.deactivate(); + } + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + mapView.onSaveInstanceState(outState); + } + + @Override + public void onMapReady(MapboxMap mapboxMap) { + this.mapboxMap = mapboxMap; + this.mapboxMap.setOnMapLongClickListener(this); + initLocationEngine(); + initLocationLayer(); + initMapRoute(); + } + + @Override + public void onMapLongClick(@NonNull LatLng point) { + destination = Position.fromCoordinates(point.getLongitude(), point.getLatitude()); + launchRouteBtn.setEnabled(false); + loading.setVisibility(View.VISIBLE); + setCurrentMarkerPosition(point); + if (currentPosition != null) { + fetchRoute(); + } + } + + @SuppressWarnings({"MissingPermission"}) + @Override + public void onConnected() { + locationEngine.requestLocationUpdates(); + } + + @Override + public void onLocationChanged(Location location) { + currentPosition = Position.fromCoordinates(location.getLongitude(), location.getLatitude()); + onLocationFound(location); + } + + @Override + public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) { + if (validRouteResponse(response)) { + route = response.body().getRoutes().get(0); + launchRouteBtn.setEnabled(true); + mapRoute.addRoute(route); + boundCameraToRoute(); + hideLoading(); + } + } + + @Override + public void onFailure(Call<DirectionsResponse> call, Throwable throwable) { + Timber.e(throwable.getMessage()); + } + + @SuppressWarnings({"MissingPermission"}) + private void initLocationEngine() { + locationEngine = new LocationSource(this); + locationEngine.setPriority(HIGH_ACCURACY); + locationEngine.setInterval(0); + locationEngine.setFastestInterval(1000); + locationEngine.addLocationEngineListener(this); + locationEngine.activate(); + + if (locationEngine.getLastLocation() != null) { + Location lastLocation = locationEngine.getLastLocation(); + currentPosition = Position.fromCoordinates(lastLocation.getLongitude(), lastLocation.getLatitude()); + } + } + + @SuppressWarnings({"MissingPermission"}) + private void initLocationLayer() { + locationLayer = new LocationLayerPlugin(mapView, mapboxMap, locationEngine); + locationLayer.setLocationLayerEnabled(LocationLayerMode.COMPASS); + } + + private void initMapRoute() { + mapRoute = new NavigationMapRoute(mapView, mapboxMap); + } + + private void fetchRoute() { + NavigationRoute.builder() + .accessToken(Mapbox.getAccessToken()) + .origin(currentPosition) + .destination(destination) + .build() + .getRoute(this); + loading.setVisibility(View.VISIBLE); + } + + private void launchNavigationWithRoute() { + if (route != null) { + NavigationLauncher.startNavigation(this, route, + null, shouldSimulateRoute); + } + } + + private boolean validRouteResponse(Response<DirectionsResponse> response) { + return response.body() != null + && response.body().getRoutes() != null + && response.body().getRoutes().size() > 0; + } + + private void hideLoading() { + if (loading.getVisibility() == View.VISIBLE) { + loading.setVisibility(View.INVISIBLE); + } + } + + private void onLocationFound(Location location) { + if (!locationFound) { + animateCamera(new LatLng(location.getLatitude(), location.getLongitude())); + Snackbar.make(mapView, "Long press map to place waypoint", BaseTransientBottomBar.LENGTH_LONG).show(); + locationFound = true; + hideLoading(); + } + } + + public void boundCameraToRoute() { + if (route != null) { + List<Position> routeCoords = LineString.fromPolyline(route.getGeometry(), + Constants.PRECISION_6).getCoordinates(); + List<LatLng> bboxPoints = new ArrayList<>(); + for (Position position : routeCoords) { + bboxPoints.add(new LatLng(position.getLatitude(), position.getLongitude())); + } + if (bboxPoints.size() > 1) { + try { + LatLngBounds bounds = new LatLngBounds.Builder().includes(bboxPoints).build(); + animateCameraBbox(bounds, CAMERA_ANIMATION_DURATION, new int[] {50, 500, 50, 335}); + } catch (InvalidLatLngBoundsException exception) { + Toast.makeText(this, "Valid route not found.", Toast.LENGTH_SHORT).show(); + } + } + } + } + + private void animateCameraBbox(LatLngBounds bounds, int animationTime, int[] padding) { + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, + padding[0], padding[1], padding[2], padding[3]), animationTime); + } + + private void animateCamera(LatLng point) { + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(point, 16), CAMERA_ANIMATION_DURATION); + } + + private void setCurrentMarkerPosition(LatLng position) { + if (position != null) { + if (currentMarker == null) { + MarkerViewOptions markerViewOptions = new MarkerViewOptions() + .position(position); + currentMarker = mapboxMap.addMarker(markerViewOptions); + } else { + currentMarker.setPosition(position); + } + } + } +} + diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java index da3c78b07a..df52ccf727 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/userlocation/MockLocationEngine.java @@ -87,6 +87,11 @@ public class MockLocationEngine extends LocationEngine { } } + @Override + public Type obtainType() { + return Type.MOCK; + } + private static class LocationAnimator extends AnimatorListenerAdapter { private static final long DURATION_ANIMATION = 10000; diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_view.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_view.xml new file mode 100644 index 0000000000..52908d17c0 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_navigation_view.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<android.support.design.widget.CoordinatorLayout + xmlns:android="http://schemas.android.com/apk/res/android" + 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"/> + + <ProgressBar + android:id="@+id/loading" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:indeterminate="true" + android:visibility="visible"/> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="8dp" + android:padding="8dp" + android:background="@drawable/demo_switch_background" + android:orientation="horizontal" + android:elevation="2dp" + android:layout_gravity="center_horizontal|top"> + + <Switch + android:id="@+id/demoSwitch" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Simulate Route"/> + + <Button + android:id="@+id/launchRouteBtn" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="8dp" + android:layout_marginStart="8dp" + android:enabled="false" + android:text="Launch Navigation"/> + + </LinearLayout> + +</android.support.design.widget.CoordinatorLayout> diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index 4d1f7eac38..d00d0120ba 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -15,6 +15,7 @@ <string name="description_camera_zoom">Different types of zoom methods</string> <string name="description_minmax_zoom">Configure a max and min zoomlevel</string> <string name="description_info_window">Learn how to handle the InfoWindow</string> + <string name="navigation_view">Navigation Drop-In UI</string> <string name="description_add_bulk_markers">Add Markers In Bulk to a Map</string> <string name="description_camera_animation_types">Showcase the different animation types</string> <string name="description_visible_bounds">Center the camera around a bounds</string> diff --git a/platform/android/dependencies.gradle b/platform/android/dependencies.gradle index 521084559f..252b9bae6d 100644 --- a/platform/android/dependencies.gradle +++ b/platform/android/dependencies.gradle @@ -1,14 +1,14 @@ ext { minSdkVersion = 15 - targetSdkVersion = 25 - compileSdkVersion = 25 - buildToolsVersion = "25.0.2" + targetSdkVersion = 26 + compileSdkVersion = 26 + buildToolsVersion = "26.0.1" versionCode = 11 versionName = "5.0.0" mapboxServicesVersion = "2.2.3" - supportLibVersion = "25.4.0" + supportLibVersion = "26.1.0" espressoVersion = '3.0.1' testRunnerVersion = '1.0.1' leakCanaryVersion = '1.5.1' |