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