Skip to content

Commit 655e842

Browse files
committed
Add dynamic offline routing to NavigationView
1 parent 14c3775 commit 655e842

File tree

24 files changed

+1074
-403
lines changed

24 files changed

+1074
-403
lines changed

app/src/main/java/com/mapbox/services/android/navigation/testapp/activity/navigationui/EmbeddedNavigationActivity.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.content.res.Configuration;
55
import android.location.Location;
66
import android.os.Bundle;
7+
import android.os.Environment;
78
import android.preference.PreferenceManager;
89
import android.support.annotation.IdRes;
910
import android.support.annotation.NonNull;
@@ -39,8 +40,11 @@
3940
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
4041
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
4142

43+
import java.io.File;
44+
4245
import retrofit2.Call;
4346
import retrofit2.Response;
47+
import timber.log.Timber;
4448

4549
public class EmbeddedNavigationActivity extends AppCompatActivity implements OnNavigationReadyCallback,
4650
NavigationListener, ProgressChangeListener, InstructionListListener, SpeechAnnouncementListener,
@@ -194,13 +198,29 @@ private void startNavigation(DirectionsRoute directionsRoute) {
194198
.progressChangeListener(this)
195199
.instructionListListener(this)
196200
.speechAnnouncementListener(this)
197-
.bannerInstructionsListener(this);
201+
.bannerInstructionsListener(this)
202+
.offlineRoutingTilesPath(obtainOfflineDirectory())
203+
.offlineRoutingTilesVersion(obtainOfflineTileVersion());
198204
setBottomSheetCallback(options);
199205
setupNightModeFab();
200206

201207
navigationView.startNavigation(options.build());
202208
}
203209

210+
private String obtainOfflineDirectory() {
211+
File offline = Environment.getExternalStoragePublicDirectory("Offline");
212+
if (!offline.exists()) {
213+
Timber.d("Offline directory does not exist");
214+
offline.mkdirs();
215+
}
216+
return offline.getAbsolutePath();
217+
}
218+
219+
private String obtainOfflineTileVersion() {
220+
return PreferenceManager.getDefaultSharedPreferences(this)
221+
.getString(getString(R.string.offline_version_key), "");
222+
}
223+
204224
private void fetchRoute() {
205225
NavigationRoute.builder(this)
206226
.accessToken(Mapbox.getAccessToken())
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.mapbox.services.android.navigation.ui.v5;
2+
3+
import android.support.annotation.NonNull;
4+
import android.telephony.TelephonyManager;
5+
6+
import java.util.HashMap;
7+
8+
class ConnectivityStatusMap extends HashMap<Integer, Boolean> {
9+
10+
ConnectivityStatusMap() {
11+
put(TelephonyManager.NETWORK_TYPE_1xRTT, false);
12+
put(TelephonyManager.NETWORK_TYPE_GPRS, false);
13+
put(TelephonyManager.NETWORK_TYPE_CDMA, false);
14+
put(TelephonyManager.NETWORK_TYPE_EDGE, false);
15+
put(TelephonyManager.NETWORK_TYPE_IDEN, false);
16+
put(TelephonyManager.NETWORK_TYPE_UNKNOWN, false);
17+
18+
put(TelephonyManager.NETWORK_TYPE_EVDO_0, true);
19+
put(TelephonyManager.NETWORK_TYPE_EVDO_A, true);
20+
put(TelephonyManager.NETWORK_TYPE_HSDPA, true);
21+
put(TelephonyManager.NETWORK_TYPE_HSPA, true);
22+
put(TelephonyManager.NETWORK_TYPE_HSUPA, true);
23+
put(TelephonyManager.NETWORK_TYPE_UMTS, true);
24+
put(TelephonyManager.NETWORK_TYPE_EHRPD, true);
25+
put(TelephonyManager.NETWORK_TYPE_EVDO_B, true);
26+
put(TelephonyManager.NETWORK_TYPE_HSPAP, true);
27+
put(TelephonyManager.NETWORK_TYPE_LTE, true);
28+
}
29+
30+
@NonNull
31+
@Override
32+
public Boolean get(Object key) {
33+
Boolean isConnectionFast = super.get(key);
34+
if (isConnectionFast == null) {
35+
isConnectionFast = false;
36+
}
37+
return isConnectionFast;
38+
}
39+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.mapbox.services.android.navigation.ui.v5;
2+
3+
import android.annotation.SuppressLint;
4+
import android.content.Context;
5+
import android.net.ConnectivityManager;
6+
import android.net.NetworkInfo;
7+
8+
class ConnectivityStatusProvider {
9+
10+
private final Context context;
11+
private final ConnectivityStatusMap statusMap;
12+
13+
ConnectivityStatusProvider(Context context) {
14+
this.context = context;
15+
this.statusMap = new ConnectivityStatusMap();
16+
}
17+
18+
boolean isConnected() {
19+
NetworkInfo info = getNetworkInfo(context);
20+
return (info != null && info.isConnected());
21+
}
22+
23+
boolean isConnectedFast() {
24+
NetworkInfo info = getNetworkInfo(context);
25+
return (info != null && info.isConnected() && isConnectionFast(info.getType(), info.getSubtype()));
26+
}
27+
28+
@SuppressLint("MissingPermission")
29+
private NetworkInfo getNetworkInfo(Context context) {
30+
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
31+
return cm.getActiveNetworkInfo();
32+
}
33+
34+
private boolean isConnectionFast(int type, int subType) {
35+
if (type == ConnectivityManager.TYPE_WIFI) {
36+
return true;
37+
} else if (type == ConnectivityManager.TYPE_MOBILE) {
38+
return statusMap.get(subType);
39+
} else {
40+
return false;
41+
}
42+
}
43+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.mapbox.services.android.navigation.ui.v5.route;
1+
package com.mapbox.services.android.navigation.ui.v5;
22

33
import java.util.HashMap;
44
import java.util.Map;

libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/NavigationViewModel.java

Lines changed: 16 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import android.arch.lifecycle.MutableLiveData;
66
import android.content.Context;
77
import android.location.Location;
8-
import android.net.ConnectivityManager;
9-
import android.net.NetworkInfo;
108
import android.support.annotation.NonNull;
119
import android.support.annotation.Nullable;
1210
import android.text.TextUtils;
@@ -21,9 +19,6 @@
2119
import com.mapbox.services.android.navigation.ui.v5.feedback.FeedbackItem;
2220
import com.mapbox.services.android.navigation.ui.v5.instruction.BannerInstructionModel;
2321
import com.mapbox.services.android.navigation.ui.v5.instruction.InstructionModel;
24-
import com.mapbox.services.android.navigation.ui.v5.route.OffRouteEvent;
25-
import com.mapbox.services.android.navigation.ui.v5.route.ViewRouteFetcher;
26-
import com.mapbox.services.android.navigation.ui.v5.route.ViewRouteListener;
2722
import com.mapbox.services.android.navigation.ui.v5.summary.SummaryModel;
2823
import com.mapbox.services.android.navigation.ui.v5.voice.NavigationSpeechPlayer;
2924
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
@@ -42,6 +37,7 @@
4237
import com.mapbox.services.android.navigation.v5.navigation.metrics.FeedbackEvent;
4338
import com.mapbox.services.android.navigation.v5.offroute.OffRouteListener;
4439
import com.mapbox.services.android.navigation.v5.route.FasterRouteListener;
40+
import com.mapbox.services.android.navigation.v5.route.RouteFetcher;
4541
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
4642
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
4743
import com.mapbox.services.android.navigation.v5.utils.DistanceFormatter;
@@ -69,14 +65,13 @@ public class NavigationViewModel extends AndroidViewModel {
6965
private final MutableLiveData<Point> destination = new MutableLiveData<>();
7066

7167
private MapboxNavigation navigation;
72-
private ViewRouteFetcher routeFetcher;
68+
private NavigationViewRouter router;
7369
private LocationEngineConductor locationEngineConductor;
7470
private NavigationViewEventDispatcher navigationViewEventDispatcher;
7571
private SpeechPlayer speechPlayer;
7672
private VoiceInstructionLoader voiceInstructionLoader;
7773
private VoiceInstructionCache voiceInstructionCache;
7874
private int voiceInstructionsToAnnounce = 0;
79-
private ConnectivityManager connectivityManager;
8075
private RouteProgress routeProgress;
8176
private String feedbackId;
8277
private String screenshot;
@@ -93,9 +88,8 @@ public class NavigationViewModel extends AndroidViewModel {
9388
public NavigationViewModel(Application application) {
9489
super(application);
9590
this.accessToken = Mapbox.getAccessToken();
96-
initializeConnectivityManager(application);
97-
initializeNavigationRouteEngine();
98-
initializeNavigationLocationEngine();
91+
initializeLocationEngine();
92+
initializeRouter();
9993
routeUtils = new RouteUtils();
10094
localeUtils = new LocaleUtils();
10195
}
@@ -121,7 +115,7 @@ public NavigationViewModel(Application application) {
121115
public void onDestroy(boolean isChangingConfigurations) {
122116
this.isChangingConfigurations = isChangingConfigurations;
123117
if (!isChangingConfigurations) {
124-
routeFetcher.onDestroy();
118+
router.onDestroy();
125119
endNavigation();
126120
deactivateInstructionPlayer();
127121
isRunning = false;
@@ -208,7 +202,7 @@ MapboxNavigation initialize(NavigationViewOptions options) {
208202
initializeVoiceInstructionCache();
209203
initializeNavigationSpeechPlayer(options);
210204
}
211-
routeFetcher.extractRouteOptions(options);
205+
router.extractRouteOptions(options);
212206
return navigation;
213207
}
214208

@@ -265,15 +259,13 @@ MutableLiveData<Point> retrieveDestination() {
265259
return destination;
266260
}
267261

268-
private void initializeConnectivityManager(Application application) {
269-
connectivityManager = (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
262+
private void initializeRouter() {
263+
RouteFetcher onlineRouter = new RouteFetcher(getApplication(), accessToken);
264+
ConnectivityStatusProvider connectivityStatus = new ConnectivityStatusProvider(getApplication());
265+
router = new NavigationViewRouter(onlineRouter, connectivityStatus, routeEngineListener);
270266
}
271267

272-
private void initializeNavigationRouteEngine() {
273-
routeFetcher = new ViewRouteFetcher(getApplication(), accessToken, routeEngineListener);
274-
}
275-
276-
private void initializeNavigationLocationEngine() {
268+
private void initializeLocationEngine() {
277269
locationEngineConductor = new LocationEngineConductor();
278270
}
279271

@@ -366,7 +358,7 @@ private void addMilestones(NavigationViewOptions options) {
366358
@Override
367359
public void onProgressChange(Location location, RouteProgress routeProgress) {
368360
NavigationViewModel.this.routeProgress = routeProgress;
369-
routeFetcher.updateLocation(location);
361+
router.updateLocation(location);
370362
instructionModel.setValue(new InstructionModel(distanceFormatter, routeProgress));
371363
summaryModel.setValue(new SummaryModel(getApplication(), distanceFormatter, routeProgress, timeFormatType));
372364
navigationLocation.setValue(location);
@@ -377,11 +369,9 @@ public void onProgressChange(Location location, RouteProgress routeProgress) {
377369
private OffRouteListener offRouteListener = new OffRouteListener() {
378370
@Override
379371
public void userOffRoute(Location location) {
380-
if (hasNetworkConnection()) {
381-
speechPlayer.onOffRoute();
382-
Point newOrigin = Point.fromLngLat(location.getLongitude(), location.getLatitude());
383-
handleOffRouteEvent(newOrigin);
384-
}
372+
speechPlayer.onOffRoute();
373+
Point newOrigin = Point.fromLngLat(location.getLongitude(), location.getLatitude());
374+
handleOffRouteEvent(newOrigin);
385375
}
386376
};
387377

@@ -470,15 +460,6 @@ private void updateBannerInstruction(RouteProgress routeProgress, Milestone mile
470460
}
471461
}
472462

473-
@SuppressWarnings( {"MissingPermission"})
474-
private boolean hasNetworkConnection() {
475-
if (connectivityManager == null) {
476-
return false;
477-
}
478-
NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
479-
return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
480-
}
481-
482463
private void sendEventFeedback(FeedbackItem feedbackItem) {
483464
if (navigationViewEventDispatcher != null) {
484465
navigationViewEventDispatcher.onFeedbackSent(feedbackItem);
@@ -494,8 +475,7 @@ private void sendEventArrival(RouteProgress routeProgress) {
494475
private void handleOffRouteEvent(Point newOrigin) {
495476
if (navigationViewEventDispatcher != null && navigationViewEventDispatcher.allowRerouteFrom(newOrigin)) {
496477
navigationViewEventDispatcher.onOffRoute(newOrigin);
497-
OffRouteEvent event = new OffRouteEvent(newOrigin, routeProgress);
498-
routeFetcher.fetchRouteFromOffRouteEvent(event);
478+
router.findRouteFrom(routeProgress);
499479
isOffRoute.setValue(true);
500480
}
501481
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.mapbox.services.android.navigation.ui.v5;
2+
3+
import com.mapbox.services.android.navigation.v5.navigation.MapboxOfflineRouter;
4+
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute;
5+
import com.mapbox.services.android.navigation.v5.navigation.OfflineRoute;
6+
7+
class NavigationViewOfflineRouter {
8+
9+
private final MapboxOfflineRouter offlineRouter;
10+
private final NavigationViewRouter router;
11+
private boolean isConfigured;
12+
private String tileVersion;
13+
14+
NavigationViewOfflineRouter(MapboxOfflineRouter offlineRouter, NavigationViewRouter router) {
15+
this.offlineRouter = offlineRouter;
16+
this.router = router;
17+
}
18+
19+
void configure(String tileVersion) {
20+
if (!isConfigured || isNew(tileVersion)) {
21+
offlineRouter.configure(tileVersion, new OfflineRouterConfiguredCallback(this));
22+
}
23+
this.tileVersion = tileVersion;
24+
}
25+
26+
void setIsConfigured(boolean isConfigured) {
27+
this.isConfigured = isConfigured;
28+
}
29+
30+
boolean isConfigured() {
31+
return isConfigured;
32+
}
33+
34+
void findRouteWith(NavigationRoute.Builder builder) {
35+
if (!isConfigured) {
36+
return;
37+
}
38+
39+
OfflineRoute offlineRoute = OfflineRoute.builder(builder).build();
40+
offlineRouter.findRoute(offlineRoute, new OfflineRouteFoundCallback(router));
41+
}
42+
43+
private boolean isNew(String tileVersion) {
44+
return !this.tileVersion.equals(tileVersion);
45+
}
46+
}

libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/NavigationViewOptions.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ public abstract class NavigationViewOptions extends NavigationUiOptions {
6161
@Nullable
6262
public abstract LocationEngine locationEngine();
6363

64+
@Nullable
65+
public abstract String offlineRoutingTilesPath();
66+
67+
@Nullable
68+
public abstract String offlineRoutingTilesVersion();
69+
6470
@AutoValue.Builder
6571
public abstract static class Builder {
6672

@@ -100,6 +106,28 @@ public abstract static class Builder {
100106

101107
public abstract Builder locationEngine(LocationEngine locationEngine);
102108

109+
/**
110+
* Add an offline path for loading offline routing data.
111+
* <p>
112+
* When added, the {@link NavigationView} will try to initialize and use this data
113+
* for offline routing when no or poor internet connection is found.
114+
*
115+
* @param offlinePath to offline data on device
116+
* @return this builder
117+
*/
118+
public abstract Builder offlineRoutingTilesPath(String offlinePath);
119+
120+
/**
121+
* Add an offline tile version. When providing a routing tile path, this version
122+
* is also required for configuration.
123+
* <p>
124+
* This version should directly correspond to the data in the offline path also provided.
125+
*
126+
* @param offlineVersion of data in tile path
127+
* @return this builder
128+
*/
129+
public abstract Builder offlineRoutingTilesVersion(String offlineVersion);
130+
103131
public abstract NavigationViewOptions build();
104132
}
105133

libandroid-navigation-ui/src/main/java/com/mapbox/services/android/navigation/ui/v5/NavigationViewRouteEngineListener.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.mapbox.api.directions.v5.models.DirectionsRoute;
44
import com.mapbox.geojson.Point;
5-
import com.mapbox.services.android.navigation.ui.v5.route.ViewRouteListener;
65

76

87
class NavigationViewRouteEngineListener implements ViewRouteListener {
@@ -19,9 +18,8 @@ public void onRouteUpdate(DirectionsRoute directionsRoute) {
1918
}
2019

2120
@Override
22-
public void onRouteRequestError(Throwable throwable) {
21+
public void onRouteRequestError(String errorMessage) {
2322
if (navigationViewModel.isOffRoute()) {
24-
String errorMessage = throwable.getMessage();
2523
navigationViewModel.sendEventFailedReroute(errorMessage);
2624
}
2725
}

0 commit comments

Comments
 (0)