I can't seem to get the navigation to start from a fragment.
All is fine on the routing front, draws the route. But it seems that the line below is where nothing happens:
No Errors, nothing back from the navigation listeners.
package au.com.gardenbagsbrisbane.gardenbagsdriver.fragment;
import android.content.Context;
import android.graphics.Color;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.support.annotation.NonNull;
import android.support.design.widget.BaseTransientBottomBar;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import au.com.gardenbagsbrisbane.gardenbagsdriver.R;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import timber.log.Timber;
import com.mapbox.mapboxsdk.Mapbox;
import com.mapbox.mapboxsdk.annotations.Marker;
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
import com.mapbox.mapboxsdk.annotations.Polyline;
import com.mapbox.mapboxsdk.annotations.PolylineOptions;
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
import com.mapbox.mapboxsdk.constants.MyLocationTracking;
import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
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.services.Constants;
import com.mapbox.services.android.navigation.v5.MapboxNavigation;
import com.mapbox.services.android.navigation.v5.NavigationConstants;
import com.mapbox.services.android.navigation.v5.RouteProgress;
import com.mapbox.services.android.navigation.v5.listeners.AlertLevelChangeListener;
import com.mapbox.services.android.navigation.v5.listeners.NavigationEventListener;
import com.mapbox.services.android.navigation.v5.listeners.OffRouteListener;
import com.mapbox.services.android.navigation.v5.listeners.ProgressChangeListener;
import com.mapbox.services.android.telemetry.location.LocationEngine;
import com.mapbox.services.android.telemetry.location.LocationEngineListener;
import com.mapbox.services.android.telemetry.location.LocationEnginePriority;
import com.mapbox.services.android.telemetry.permissions.PermissionsListener;
import com.mapbox.services.android.telemetry.permissions.PermissionsManager;
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;
/**
* A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the
* {@link HomeFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {@link HomeFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class HomeFragment extends Fragment implements PermissionsListener, OnMapReadyCallback,MapboxMap.OnMapClickListener,
ProgressChangeListener, NavigationEventListener, AlertLevelChangeListener, OffRouteListener {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private MapView mapView = null;
private MapboxMap map;
private FloatingActionButton floatingActionButton;
private LocationEngineListener locationEngineListener;
private PermissionsManager permissionsManager;
private Marker destinationMarker;
// Navigation related variables
private LocationEngine locationEngine;
private MapboxNavigation navigation;
private FloatingActionButton startRouteButton;
private DirectionsRoute route;
private Position destination;
private Polyline routeLine;
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public HomeFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment HomeFragment.
*/
// TODO: Rename and change types and number of parameters
public static HomeFragment newInstance(String param1, String param2) {
HomeFragment fragment = new HomeFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
Mapbox.getInstance(getActivity(), getString(R.string.access_token));
View layout = inflater.inflate(R.layout.fragment_home, container, false);
mapView = (MapView) layout.findViewById(R.id.mapboxMapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
//locationEngine.activate();
locationEngine = LocationSource.getLocationEngine(getActivity());
navigation = new MapboxNavigation(getActivity(), Mapbox.getAccessToken());
locationEngine.setInterval(0);
locationEngine.setSmallestDisplacement(3.0f);
locationEngine.setPriority(LocationEnginePriority.HIGH_ACCURACY);
locationEngine.setFastestInterval(1000);
navigation.setLocationEngine(locationEngine);
floatingActionButton = (FloatingActionButton) layout.findViewById(R.id.location_toggle_fab);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (map != null) {
toggleGps(!map.isMyLocationEnabled());
}
}
});
startRouteButton = (FloatingActionButton) layout.findViewById(R.id.startRouteButton);
startRouteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (navigation != null && route != null) {
// Hide the start button
startRouteButton.setVisibility(View.INVISIBLE);
// Attach all of our navigation listeners.
navigation.addNavigationEventListener(HomeFragment.this);
navigation.addProgressChangeListener(HomeFragment.this);
navigation.addAlertLevelChangeListener(HomeFragment.this);
// Adjust location engine to force a gps reading every second. This isn't required but gives an overall
// better navigation experience for users. The updating only occurs if the user moves 3 meters or further
// from the last update.
navigation.startNavigation(route);
}
else
{
Timber.e("No valid navigation object or route");
}
}
});
return layout;
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
@Override
public void onMapReady(MapboxMap mapboxMap) {
map = mapboxMap;
map.setOnMapClickListener(HomeFragment.this);
Snackbar.make(mapView, "Tap map to place destination", BaseTransientBottomBar.LENGTH_LONG).show();
map.moveCamera(CameraUpdateFactory.zoomBy(12));
if (PermissionsManager.areLocationPermissionsGranted(getActivity())) {
mapboxMap.setMyLocationEnabled(true);
mapboxMap.getTrackingSettings().setMyLocationTrackingMode(MyLocationTracking.TRACKING_FOLLOW);
}
}
@Override
public void onMapClick(@NonNull LatLng point) {
if (destinationMarker != null) {
map.removeMarker(destinationMarker);
}
destinationMarker = map.addMarker(new MarkerOptions().position(point));
startRouteButton.setVisibility(View.VISIBLE);
destination = Position.fromCoordinates(point.getLongitude(), point.getLatitude());
calculateRoute();
}
private void drawRouteLine(DirectionsRoute route) {
List<Position> positions = LineString.fromPolyline(route.getGeometry(), Constants.PRECISION_6).getCoordinates();
List<LatLng> latLngs = new ArrayList<>();
for (Position position : positions) {
latLngs.add(new LatLng(position.getLatitude(), position.getLongitude()));
}
// Remove old route if currently being shown on map.
if (routeLine != null) {
map.removePolyline(routeLine);
}
routeLine = map.addPolyline(new PolylineOptions()
.addAll(latLngs)
.color(Color.parseColor("#56b881"))
.width(5f));
}
private void calculateRoute() {
Location userLocation = map.getMyLocation();
if (userLocation == null) {
Timber.d("calculateRoute: User location is null, therefore, origin can't be set.");
return;
}
Position origin = (Position.fromCoordinates(userLocation.getLongitude(), userLocation.getLatitude()));
navigation.getRoute(origin, destination, new Callback<DirectionsResponse>() {
@Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
DirectionsRoute route = response.body().getRoutes().get(0);
HomeFragment.this.route = route;
drawRouteLine(route);
navigation.addNavigationEventListener(HomeFragment.this);
navigation.addProgressChangeListener(HomeFragment.this);
navigation.addAlertLevelChangeListener(HomeFragment.this);
// Adjust location engine to force a gps reading every second. This isn't required but gives an overall
// better navigation experience for users. The updating only occurs if the user moves 3 meters or further
// from the last update.
navigation.startNavigation(route);
}
@Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Timber.e("onFailure: navigation.getRoute()", throwable);
}
});
}
/*
* Navigation listeners
*/
@Override
public void onRunning(boolean running) {
if (running) {
Timber.d("onRunning: Started");
} else {
Timber.d("onRunning: Stopped");
}
}
@Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
Timber.d("onProgressChange: fraction of route traveled: %f", routeProgress.getFractionTraveled());
}
@Override
public void onAlertLevelChange(int alertLevel, RouteProgress routeProgress) {
switch (alertLevel) {
case NavigationConstants.HIGH_ALERT_LEVEL:
Toast.makeText(getActivity(), "HIGH", Toast.LENGTH_LONG).show();
break;
case NavigationConstants.MEDIUM_ALERT_LEVEL:
Toast.makeText(getActivity(), "MEDIUM", Toast.LENGTH_LONG).show();
break;
case NavigationConstants.LOW_ALERT_LEVEL:
Toast.makeText(getActivity(), "LOW", Toast.LENGTH_LONG).show();
break;
case NavigationConstants.ARRIVE_ALERT_LEVEL:
Toast.makeText(getActivity(), "ARRIVE", Toast.LENGTH_LONG).show();
break;
case NavigationConstants.DEPART_ALERT_LEVEL:
Toast.makeText(getActivity(), "DEPART", Toast.LENGTH_LONG).show();
break;
default:
case NavigationConstants.NONE_ALERT_LEVEL:
Toast.makeText(getActivity(), "NONE", Toast.LENGTH_LONG).show();
break;
}
}
@Override
public void userOffRoute(Location location) {
Position newOrigin = Position.fromCoordinates(location.getLongitude(), location.getLatitude());
navigation.updateRoute(newOrigin, destination, new Callback<DirectionsResponse>() {
@Override
public void onResponse(Call<DirectionsResponse> call, Response<DirectionsResponse> response) {
DirectionsRoute route = response.body().getRoutes().get(0);
HomeFragment.this.route = route;
// Remove old route line from map and draw the new one.
map.removePolyline(routeLine);
drawRouteLine(route);
}
@Override
public void onFailure(Call<DirectionsResponse> call, Throwable throwable) {
Timber.e("onFailure: navigation.getRoute()", throwable);
}
});
}
/*
* Activity lifecycle methods
*/
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onStart() {
super.onStart();
mapView.onStart();
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
@Override
public void onPause() {
super.onPause();
mapView.onPause();
}
@Override
public void onStop() {
super.onStop();
mapView.onStop();
navigation.onStop();
}
@Override
public void onResume() {
super.onResume();
mapView.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
mapView.onDestroy();
if (locationEngineListener != null) {
locationEngine.removeLocationEngineListener(locationEngineListener);
}
// Remove all navigation listeners
navigation.removeAlertLevelChangeListener(this);
navigation.removeNavigationEventListener(this);
navigation.removeProgressChangeListener(this);
navigation.removeOffRouteListener(this);
// End the navigation session
navigation.endNavigation();
}
@Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
private void toggleGps(boolean enableGps) {
if (enableGps) {
// Check if user has granted location permission
permissionsManager = new PermissionsManager(this);
if (!PermissionsManager.areLocationPermissionsGranted(getActivity())) {
Log.v("GPS","Requesting permissions");
permissionsManager.requestLocationPermissions(getActivity());
} else {
enableLocation(true);
}
} else {
enableLocation(false);
}
}
private void enableLocation(boolean enabled) {
if (enabled) {
// If we have the last location of the user, we can move the camera to that position.
Location lastLocation = locationEngine.getLastLocation();
if (lastLocation != null) {
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lastLocation), 16));
}
locationEngineListener = new LocationEngineListener() {
@Override
public void onConnected() {
// No action needed here.
}
@Override
public void onLocationChanged(Location location) {
if (location != null) {
// Move the map camera to where the user location is and then remove the
// listener so the camera isn't constantly updating when the user location
// changes. When the user disables and then enables the location again, this
// listener is registered again and will adjust the camera once again.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(location), 16));
locationEngine.removeLocationEngineListener(this);
}
}
};
locationEngine.addLocationEngineListener(locationEngineListener);
floatingActionButton.setImageResource(R.drawable.ic_location_disabled_24dp);
} else {
floatingActionButton.setImageResource(R.drawable.ic_my_location_24dp);
}
// Enable or disable the location layer on the map
map.setMyLocationEnabled(enabled);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
permissionsManager.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onExplanationNeeded(List<String> permissionsToExplain) {
Toast.makeText(getActivity(), "This app needs location permissions in order to show its functionality.",
Toast.LENGTH_LONG).show();
}
@Override
public void onPermissionResult(boolean granted) {
if (granted) {
enableLocation(true);
} else {
Toast.makeText(getActivity(), "You didn't grant location permissions.",
Toast.LENGTH_LONG).show();
getActivity().finish();
}
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}