Skip to content

Instantly share code, notes, and snippets.

@andfs
Last active May 26, 2020 18:37
Show Gist options
  • Save andfs/dc833e70b6d9cfcb1fd3b85865c704c0 to your computer and use it in GitHub Desktop.
Save andfs/dc833e70b6d9cfcb1fd3b85865c704c0 to your computer and use it in GitHub Desktop.
Class that overrides NavigationView from Mapbox and is returned to React Native
package com.navdemo;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.api.directions.v5.models.BannerInstructions;
import com.mapbox.api.directions.v5.models.DirectionsResponse;
import com.mapbox.api.directions.v5.models.DirectionsRoute;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.services.android.navigation.ui.v5.NavigationViewOptions;
import com.mapbox.services.android.navigation.ui.v5.OnNavigationReadyCallback;
import com.mapbox.services.android.navigation.ui.v5.listeners.BannerInstructionsListener;
import com.mapbox.services.android.navigation.ui.v5.listeners.SpeechAnnouncementListener;
import com.mapbox.services.android.navigation.ui.v5.map.NavigationMapboxMap;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
import com.mapbox.services.android.navigation.v5.navigation.MapboxNavigationOptions;
import com.mapbox.services.android.navigation.v5.navigation.NavigationRoute;
import com.mapbox.services.android.navigation.v5.routeprogress.ProgressChangeListener;
import com.mapbox.services.android.navigation.v5.routeprogress.RouteProgress;
import com.mapbox.services.android.navigation.ui.v5.NavigationView;
import java.util.Locale;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MapboxNavigationView extends NavigationView implements OnNavigationReadyCallback, ProgressChangeListener, SpeechAnnouncementListener {
private ThemedReactContext context;
private DirectionsRoute directionsRoute;
private Point origin = null;
private Point destination = null;
private boolean shouldSimulateRoute = true;
private boolean isMuted = false;
private boolean isNavigating = false;
public MapboxNavigationView(ThemedReactContext context) {
super(Objects.requireNonNull(context.getCurrentActivity()));
this.context = context;
onCreate(null);
initialize((OnNavigationReadyCallback) this);
onResume();
//I had to do this for instructions list work. Othewise it become visible only after I change the orientation
// of the phone
findViewById(R.id.instructionListLayout).setVisibility(INVISIBLE);
//Has no effect like it did in instructionListLayout
// View routeInformation = findViewById(R.id.routeInfoBottomSheetView);
// routeInformation.setVisibility(VISIBLE);
// TextView v = routeInformation.findViewById(R.id.summaryPeekLayout).findViewById(R.id.timeRemainingText);
// v.setVisibility(VISIBLE);
//Make buttons do appear in the screen
retrieveFeedbackButton().show();
retrieveSoundButton().show();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void onNavigationReady(boolean isRunning) {
fetchRoute();
}
@Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
WritableMap event = Arguments.createMap();
event.putDouble("longitude", location.getLongitude());
event.putDouble("latitude", location.getLatitude());
event.putDouble("distanceRemaining", routeProgress.distanceRemaining());
event.putDouble("durationRemaining", routeProgress.durationRemaining());
// context.getJSModule(RCTEventEmitter.class).receiveEvent(getId(), "onProgressChange", event);
}
@Override
public SpeechAnnouncement willVoice(SpeechAnnouncement announcement) {
if (isMuted) {
return null;
}
return announcement;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void setOrigin(Point origin) {
this.origin = origin;
fetchRoute();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void setDestination(Point destination) {
this.destination = destination;
fetchRoute();
}
public void setShouldSimulateRoute(boolean shouldSimulateRoute) {
this.shouldSimulateRoute = shouldSimulateRoute;
}
public void setIsMuted(boolean isMuted) {
this.isMuted = isMuted;
}
public void onDropViewInstance() {
if (isNavigating) {
stopNavigation();
}
}
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
this.onPause();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
private void fetchRoute() {
String accessToken = Mapbox.getAccessToken();
if (accessToken == null || origin == null || destination == null) {
return;
}
System.out.println(origin);
NavigationRoute.builder(getContext())
.accessToken(accessToken)
.origin(origin)
.destination(destination)
.language(Locale.forLanguageTag("pt-BR"))
.voiceUnits(DirectionsCriteria.METRIC)
.build()
.getRoute(new Callback<DirectionsResponse>() {
@Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
DirectionsResponse responseBody = response.body();
if (responseBody == null || responseBody.routes().size() == 0) {
return;
}
directionsRoute = responseBody.routes().get(0);
start();
}
@Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
}
});
}
private void start() {
if (directionsRoute == null) {
return;
}
NavigationMapboxMap navigationMapboxMap = retrieveNavigationMapboxMap();
if (navigationMapboxMap == null) {
return;
}
MapboxNavigationOptions mapboxNavigationOptions = MapboxNavigationOptions.builder()
.defaultMilestonesEnabled(true)
.enableAutoIncrementLegIndex(true)
.enableFasterRouteDetection(true)
.enableRefreshRoute(true)
.build();
NavigationViewOptions options = NavigationViewOptions.builder()
.waynameChipEnabled(true)
.directionsRoute(directionsRoute)
.shouldSimulateRoute(shouldSimulateRoute)
.progressChangeListener(this)
.speechAnnouncementListener((SpeechAnnouncementListener) this)
.navigationOptions(mapboxNavigationOptions)
.bannerInstructionsListener(new BannerInstructionsListener() {
@Override
public BannerInstructions willDisplay(BannerInstructions instructions) {
return instructions;
}
})
.build();
//It works as expected but covers all the screen, and I need some space to present other informations
// NavigationLauncherOptions navigationLauncherOptions = NavigationLauncherOptions.builder()
// .directionsRoute(directionsRoute)
// .shouldSimulateRoute(shouldSimulateRoute).build();
// NavigationLauncher.startNavigation(context.getCurrentActivity(), navigationLauncherOptions);
startNavigation(options);
onStart();
isNavigating = true;
}
}
package com.navdemo;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ViewGroupManager;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.mapbox.geojson.Point;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.services.android.navigation.ui.v5.NavigationView;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MapboxViewManager extends ViewGroupManager<NavigationView> {
public static final String REACT_CLASS = "MapNav";
ReactApplicationContext mCallerContext;
// NavigationView navigationView;
// Point origin;
// Point destination;
public MapboxViewManager(ReactApplicationContext reactContext) {
mCallerContext = reactContext;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Mapbox.getInstance(reactContext, reactContext.getString(R.string.MAPBOX_ACCESS_TOKEN));
}
});
}
@Nonnull
@Override
public String getName() {
return REACT_CLASS;
}
@Nonnull
@Override
protected NavigationView createViewInstance(@Nonnull ThemedReactContext reactContext) {
//return new MapboxNavigationView(context, Fresco.newDraweeControllerBuilder(), null, mCallerContext);
return new MapboxNavigationView(reactContext);
}
@Override
public void onDropViewInstance(@NonNull NavigationView view) {
view.onStop();
view.onDestroy();
super.onDropViewInstance(view);
}
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.of("onProgressChange", MapBuilder.of("registrationName", "onProgressChange"));
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@ReactProp(name = "origin")
public void setOrigin(MapboxNavigationView view, @Nullable ReadableArray sources) {
if (sources == null) {
view.setOrigin(null);
// origin = Point.fromLngLat(sources.getDouble(1), sources.getDouble(0));
return;
}
view.setOrigin(Point.fromLngLat(sources.getDouble(1), sources.getDouble(0)));
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@ReactProp(name = "destination")
public void setDestination(MapboxNavigationView view, @Nullable ReadableArray sources) {
if (sources == null) {
view.setDestination(null);
// destination = Point.fromLngLat(sources.getDouble(1), sources.getDouble(0));
return;
}
view.setDestination(Point.fromLngLat(sources.getDouble(1), sources.getDouble(0)));
}
@ReactProp(name = "shouldSimulateRoute")
public void setShouldSimulateRoute(MapboxNavigationView view, boolean shouldSimulateRoute) {
view.setShouldSimulateRoute(shouldSimulateRoute);
}
@ReactProp(name = "isMuted")
public void setIsMuted(MapboxNavigationView view, boolean isMuted) {
view.setIsMuted(isMuted);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment