Skip to content

Commit 9e75cbc

Browse files
danesfederGuardiola31337
authored andcommitted
MapboxNavigator Offline Routing Integration (#1300)
* Offline Routing Integration * add offline route activity, fix offline integration / apis and fix voice instruction milestones being fired off twice
1 parent 4321da4 commit 9e75cbc

File tree

11 files changed

+636
-126
lines changed

11 files changed

+636
-126
lines changed

app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
package="com.mapbox.services.android.navigation.testapp">
44

55
<uses-permission android:name="android.permission.VIBRATE"/>
6+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
7+
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
68

79
<application
810
android:name=".NavigationApplication"
@@ -36,6 +38,14 @@
3638
android:value=".MainActivity"/>
3739
</activity>
3840

41+
<activity
42+
android:name=".activity.OfflineRerouteActivity"
43+
android:label="@string/title_offline_reroute">
44+
<meta-data
45+
android:name="android.support.PARENT_ACTIVITY"
46+
android:value=".MainActivity"/>
47+
</activity>
48+
3949
<activity
4050
android:name=".activity.navigationui.NavigationMapRouteActivity"
4151
android:label="@string/title_navigation_route_ui">

app/src/main/java/com/mapbox/services/android/navigation/testapp/MainActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import com.mapbox.android.core.permissions.PermissionsManager;
1717
import com.mapbox.services.android.navigation.testapp.activity.MockNavigationActivity;
1818
import com.mapbox.services.android.navigation.testapp.activity.RerouteActivity;
19+
import com.mapbox.services.android.navigation.testapp.activity.OfflineRerouteActivity;
1920
import com.mapbox.services.android.navigation.testapp.activity.navigationui.ComponentNavigationActivity;
2021
import com.mapbox.services.android.navigation.testapp.activity.navigationui.DualNavigationMapActivity;
2122
import com.mapbox.services.android.navigation.testapp.activity.navigationui.EmbeddedNavigationActivity;
@@ -64,6 +65,11 @@ protected void onCreate(Bundle savedInstanceState) {
6465
getString(R.string.description_reroute),
6566
RerouteActivity.class
6667
),
68+
new SampleItem(
69+
getString(R.string.title_offline_reroute),
70+
getString(R.string.description_offline_reroute),
71+
OfflineRerouteActivity.class
72+
),
6773
new SampleItem(
6874
getString(R.string.title_navigation_route_ui),
6975
getString(R.string.description_navigation_route_ui),
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
package com.mapbox.services.android.navigation.testapp.activity;
2+
3+
import android.annotation.SuppressLint;
4+
import android.graphics.Color;
5+
import android.location.Location;
6+
import android.os.Bundle;
7+
import android.os.Environment;
8+
import android.support.annotation.NonNull;
9+
import android.support.design.widget.Snackbar;
10+
import android.support.v7.app.AppCompatActivity;
11+
import android.view.View;
12+
13+
import com.mapbox.android.core.location.LocationEngineListener;
14+
import com.mapbox.api.directions.v5.models.DirectionsRoute;
15+
import com.mapbox.core.constants.Constants;
16+
import com.mapbox.geojson.LineString;
17+
import com.mapbox.geojson.Point;
18+
import com.mapbox.mapboxsdk.Mapbox;
19+
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
20+
import com.mapbox.mapboxsdk.annotations.Polyline;
21+
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
22+
import com.mapbox.mapboxsdk.camera.CameraPosition;
23+
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
24+
import com.mapbox.mapboxsdk.geometry.LatLng;
25+
import com.mapbox.mapboxsdk.maps.MapView;
26+
import com.mapbox.mapboxsdk.maps.MapboxMap;
27+
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
28+
import com.mapbox.mapboxsdk.plugins.locationlayer.LocationLayerPlugin;
29+
import com.mapbox.mapboxsdk.plugins.locationlayer.modes.RenderMode;
30+
import com.mapbox.services.android.navigation.testapp.R;
31+
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionView;
32+
import com.mapbox.services.android.navigation.v5.location.replay.ReplayRouteLocationEngine;
33+
import com.mapbox.services.android.navigation.v5.milestone.Milestone;
34+
import com.mapbox.services.android.navigation.v5.milestone.MilestoneEventListener;
35+
import com.mapbox.services.android.navigation.v5.milestone.VoiceInstructionMilestone;
36+
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigation;
37+
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions;
38+
import com.mapbox.services.android.navigation.v5.navigation.NavigationEventListener;
39+
import com.mapbox.services.android.navigation.v5.offroute.OffRouteListener;
40+
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
41+
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
42+
43+
import java.io.File;
44+
import java.util.ArrayList;
45+
import java.util.List;
46+
47+
import butterknife.BindView;
48+
import butterknife.ButterKnife;
49+
import timber.log.Timber;
50+
51+
public class OfflineRerouteActivity extends AppCompatActivity implements OnMapReadyCallback, LocationEngineListener,
52+
MapboxMap.OnMapClickListener, NavigationEventListener, OffRouteListener,
53+
ProgressChangeListener, MilestoneEventListener {
54+
55+
@BindView(R.id.mapView)
56+
MapView mapView;
57+
@BindView(android.R.id.content)
58+
View contentLayout;
59+
@BindView(R.id.instructionView)
60+
InstructionView instructionView;
61+
62+
private Point origin = Point.fromLngLat(-1.220722, 51.757772);
63+
private Point destination = Point.fromLngLat(-1.2206, 51.757);
64+
private Polyline polyline;
65+
66+
private LocationLayerPlugin locationLayerPlugin;
67+
private ReplayRouteLocationEngine mockLocationEngine;
68+
private MapboxNavigation navigation;
69+
private MapboxMap mapboxMap;
70+
private boolean running;
71+
private boolean tracking;
72+
private DirectionsRoute route;
73+
74+
@Override
75+
protected void onCreate(Bundle savedInstanceState) {
76+
setTheme(R.style.NavigationViewLight);
77+
super.onCreate(savedInstanceState);
78+
setContentView(R.layout.activity_reroute);
79+
ButterKnife.bind(this);
80+
81+
mapView.onCreate(savedInstanceState);
82+
83+
// Initialize MapboxNavigation and add listeners
84+
MapboxNavigationOptions options = MapboxNavigationOptions.builder().isDebugLoggingEnabled(true).build();
85+
navigation = new MapboxNavigation(getApplicationContext(), Mapbox.getAccessToken(), options);
86+
navigation.addNavigationEventListener(this);
87+
navigation.addMilestoneEventListener(this);
88+
89+
String tilesDirPath = obtainOfflineDirectoryFor("tiles");
90+
Timber.d("Tiles directory path: %s", tilesDirPath);
91+
String translationsDirPath = obtainOfflineDirectoryFor("translations");
92+
Timber.d("Translations directory path: %s", translationsDirPath);
93+
94+
navigation.initializeOfflineData(tilesDirPath, translationsDirPath);
95+
route = navigation.findOfflineRouteFor(origin, destination);
96+
checkRoute();
97+
98+
mapView.getMapAsync(this);
99+
}
100+
101+
@Override
102+
public void onResume() {
103+
super.onResume();
104+
mapView.onResume();
105+
}
106+
107+
@SuppressLint("MissingPermission")
108+
@Override
109+
protected void onStart() {
110+
super.onStart();
111+
mapView.onStart();
112+
if (locationLayerPlugin != null) {
113+
locationLayerPlugin.onStart();
114+
}
115+
}
116+
117+
@Override
118+
protected void onSaveInstanceState(Bundle outState) {
119+
super.onSaveInstanceState(outState);
120+
mapView.onSaveInstanceState(outState);
121+
}
122+
123+
@Override
124+
protected void onStop() {
125+
super.onStop();
126+
mapView.onStop();
127+
128+
shutdownLocationEngine();
129+
130+
if (locationLayerPlugin != null) {
131+
locationLayerPlugin.onStop();
132+
}
133+
134+
if (navigation != null) {
135+
// End the navigation session
136+
navigation.stopNavigation();
137+
}
138+
}
139+
140+
@Override
141+
public void onPause() {
142+
super.onPause();
143+
mapView.onPause();
144+
}
145+
146+
@Override
147+
public void onLowMemory() {
148+
super.onLowMemory();
149+
mapView.onLowMemory();
150+
}
151+
152+
@Override
153+
protected void onDestroy() {
154+
super.onDestroy();
155+
mapView.onDestroy();
156+
shutdownNavigation();
157+
}
158+
159+
@SuppressLint("MissingPermission")
160+
@Override
161+
public void onMapReady(MapboxMap mapboxMap) {
162+
this.mapboxMap = mapboxMap;
163+
mapboxMap.addOnMapClickListener(this);
164+
165+
locationLayerPlugin = new LocationLayerPlugin(mapView, mapboxMap);
166+
locationLayerPlugin.setRenderMode(RenderMode.NORMAL);
167+
168+
// Setup the mockLocationEngine
169+
mockLocationEngine = new ReplayRouteLocationEngine();
170+
mockLocationEngine.addLocationEngineListener(this);
171+
navigation.setLocationEngine(mockLocationEngine);
172+
173+
handleNewRoute(route);
174+
}
175+
176+
@Override
177+
public void onConnected() {
178+
// No-op - mock automatically begins pushing updates
179+
}
180+
181+
@Override
182+
public void onLocationChanged(Location location) {
183+
if (!tracking) {
184+
locationLayerPlugin.forceLocationUpdate(location);
185+
}
186+
}
187+
188+
@Override
189+
public void onMapClick(@NonNull LatLng point) {
190+
if (!running || mapboxMap == null) {
191+
return;
192+
}
193+
194+
mapboxMap.addMarker(new MarkerOptions().position(point));
195+
mapboxMap.removeOnMapClickListener(this);
196+
197+
Point newDestination = Point.fromLngLat(point.getLongitude(), point.getLatitude());
198+
mockLocationEngine.moveTo(newDestination);
199+
destination = Point.fromLngLat(point.getLongitude(), point.getLatitude());
200+
tracking = false;
201+
}
202+
203+
@Override
204+
public void onRunning(boolean running) {
205+
this.running = running;
206+
if (running) {
207+
navigation.addOffRouteListener(this);
208+
navigation.addProgressChangeListener(this);
209+
}
210+
}
211+
212+
@Override
213+
public void userOffRoute(Location location) {
214+
Snackbar.make(contentLayout, "User Off Route", Snackbar.LENGTH_SHORT).show();
215+
mapboxMap.addMarker(new MarkerOptions().position(new LatLng(location.getLatitude(), location.getLongitude())));
216+
route = navigation.findOfflineRouteFor(location, destination);
217+
checkRoute();
218+
handleNewRoute(route);
219+
}
220+
221+
@Override
222+
public void onProgressChange(Location location, RouteProgress routeProgress) {
223+
if (tracking) {
224+
locationLayerPlugin.forceLocationUpdate(location);
225+
animateCameraFor(location);
226+
}
227+
instructionView.update(routeProgress);
228+
}
229+
230+
@Override
231+
public void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {
232+
if (milestone instanceof VoiceInstructionMilestone) {
233+
Snackbar.make(contentLayout, instruction, Snackbar.LENGTH_SHORT).show();
234+
}
235+
}
236+
237+
private void checkRoute() {
238+
if (route != null) {
239+
Snackbar.make(contentLayout, "Offline route found", Snackbar.LENGTH_SHORT).show();
240+
} else {
241+
Snackbar.make(contentLayout, "Offline route not found", Snackbar.LENGTH_SHORT).show();
242+
}
243+
}
244+
245+
private String obtainOfflineDirectoryFor(String fileName) {
246+
File offline = Environment.getExternalStoragePublicDirectory("Offline");
247+
if (!offline.exists()) {
248+
Timber.d("Offline directory does not exist");
249+
}
250+
File file = new File(offline, fileName);
251+
return file.getAbsolutePath();
252+
}
253+
254+
private void startNavigation(DirectionsRoute route) {
255+
navigation.startNavigation(route);
256+
mapboxMap.addOnMapClickListener(this);
257+
tracking = true;
258+
}
259+
260+
private void drawRoute(DirectionsRoute route) {
261+
List<LatLng> points = new ArrayList<>();
262+
List<Point> coords = LineString.fromPolyline(route.geometry(), Constants.PRECISION_6).coordinates();
263+
264+
for (Point point : coords) {
265+
points.add(new LatLng(point.latitude(), point.longitude()));
266+
}
267+
268+
if (!points.isEmpty()) {
269+
if (polyline != null) {
270+
mapboxMap.removePolyline(polyline);
271+
}
272+
polyline = mapboxMap.addPolyline(new PolylineOptions()
273+
.addAll(points)
274+
.color(Color.parseColor(getString(R.string.blue)))
275+
.width(5));
276+
}
277+
}
278+
279+
private void handleNewRoute(DirectionsRoute route) {
280+
if (route == null) {
281+
return;
282+
}
283+
drawRoute(route);
284+
resetLocationEngine(route);
285+
startNavigation(route);
286+
}
287+
288+
private void animateCameraFor(Location location) {
289+
CameraPosition cameraPosition = new CameraPosition.Builder()
290+
.zoom(15)
291+
.target(new LatLng(location.getLatitude(), location.getLongitude()))
292+
.bearing(location.getBearing())
293+
.build();
294+
mapboxMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), 2000);
295+
}
296+
297+
private void resetLocationEngine(DirectionsRoute directionsRoute) {
298+
mockLocationEngine.deactivate();
299+
mockLocationEngine.assign(directionsRoute);
300+
}
301+
302+
private void shutdownLocationEngine() {
303+
if (mockLocationEngine != null) {
304+
mockLocationEngine.removeLocationEngineListener(this);
305+
mockLocationEngine.removeLocationUpdates();
306+
mockLocationEngine.deactivate();
307+
}
308+
}
309+
310+
private void shutdownNavigation() {
311+
navigation.removeNavigationEventListener(this);
312+
navigation.removeProgressChangeListener(this);
313+
navigation.onDestroy();
314+
}
315+
}

0 commit comments

Comments
 (0)