package net.osmand.plus.helpers; import android.content.DialogInterface; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.app.AlertDialog; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import net.osmand.CallbackWithObject; import net.osmand.Location; import net.osmand.ResultMatcher; import net.osmand.binary.RouteDataObject; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.routing.RoutingHelper; import net.osmand.plus.views.AnimateDraggingMapThread; import net.osmand.plus.views.ContextMenuLayer; import net.osmand.router.RoutingConfiguration; import net.osmand.util.MapUtils; import java.util.ArrayList; import java.util.List; public class AvoidSpecificRoads { private List<RouteDataObject> missingRoads; private OsmandApplication app; public AvoidSpecificRoads(OsmandApplication app) { this.app = app; } public void initPreservedData() { List<LatLon> impassibleRoads = app.getSettings().getImpassableRoadPoints(); for (LatLon impassibleRoad : impassibleRoads) { addImpassableRoad(null, impassibleRoad, false, null, true); } } public List<RouteDataObject> getMissingRoads() { if (missingRoads == null) { missingRoads = app.getDefaultRoutingConfig().getImpassableRoads(); } return missingRoads; } protected net.osmand.router.RoutingConfiguration.Builder getBuilder() { return RoutingConfiguration.getDefault(); } public ArrayAdapter<RouteDataObject> createAdapter(final MapActivity ctx) { final ArrayList<RouteDataObject> points = new ArrayList<>(); points.addAll(getMissingRoads()); final LatLon mapLocation = ctx.getMapLocation(); return new ArrayAdapter<RouteDataObject>(ctx, R.layout.waypoint_reached, R.id.title, points) { @Override public View getView(final int position, View convertView, ViewGroup parent) { // User super class to create the View View v = convertView; if (v == null || v.findViewById(R.id.info_close) == null) { v = ctx.getLayoutInflater().inflate(R.layout.waypoint_reached, parent, false); } final RouteDataObject obj = getItem(position); v.findViewById(R.id.all_points).setVisibility(View.GONE); ((ImageView) v.findViewById(R.id.waypoint_icon)).setImageDrawable( app.getIconsCache().getThemedIcon(R.drawable.ic_action_road_works_dark)); double dist = MapUtils.getDistance(mapLocation, MapUtils.get31LatitudeY(obj.getPoint31YTile(0)), MapUtils.get31LongitudeX(obj.getPoint31XTile(0))); ((TextView) v.findViewById(R.id.waypoint_dist)).setText(OsmAndFormatter.getFormattedDistance((float) dist, app)); ((TextView) v.findViewById(R.id.waypoint_text)).setText(getText(obj)); ImageButton remove = (ImageButton) v.findViewById(R.id.info_close); remove.setVisibility(View.VISIBLE); remove.setImageDrawable(app.getIconsCache().getThemedIcon( R.drawable.ic_action_remove_dark)); remove.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { remove(obj); removeImpassableRoad(obj); notifyDataSetChanged(); RoutingHelper rh = app.getRoutingHelper(); if (rh.isRouteCalculated() || rh.isRouteBeingCalculated()) { rh.recalculateRouteDueToSettingsChange(); } } }); return v; } }; } public void removeImpassableRoad(RouteDataObject obj) { app.getSettings().removeImpassableRoad(getLocation(obj)); getBuilder().removeImpassableRoad(obj); } protected String getText(RouteDataObject obj) { return RoutingHelper.formatStreetName(obj.getName(app.getSettings().MAP_PREFERRED_LOCALE.get(), app.getSettings().MAP_TRANSLITERATE_NAMES.get()), obj.getRef(app.getSettings().MAP_PREFERRED_LOCALE.get(), app.getSettings().MAP_TRANSLITERATE_NAMES.get(), true), obj.getDestinationName(app.getSettings().MAP_PREFERRED_LOCALE.get(), app.getSettings().MAP_TRANSLITERATE_NAMES.get(), true), app.getString(R.string.towards)); } public void showDialog(@NonNull final MapActivity mapActivity) { AlertDialog.Builder bld = new AlertDialog.Builder(mapActivity); bld.setTitle(R.string.impassable_road); if (getMissingRoads().size() == 0) { bld.setMessage(R.string.avoid_roads_msg); } else { final ArrayAdapter<?> listAdapter = createAdapter(mapActivity); bld.setAdapter(listAdapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { RouteDataObject obj = getMissingRoads().get(which); double lat = MapUtils.get31LatitudeY(obj.getPoint31YTile(0)); double lon = MapUtils.get31LongitudeX(obj.getPoint31XTile(0)); showOnMap(mapActivity, lat, lon, getText(obj), dialog); } }); } bld.setPositiveButton(R.string.shared_string_select_on_map, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { selectFromMap(mapActivity); } }); bld.setNegativeButton(R.string.shared_string_close, null); bld.show(); } protected void selectFromMap(final MapActivity mapActivity) { ContextMenuLayer cm = mapActivity.getMapLayers().getContextMenuLayer(); cm.setSelectOnMap(new CallbackWithObject<LatLon>() { @Override public boolean processResult(LatLon result) { addImpassableRoad(mapActivity, result, true, null, false); return true; } }); } public void addImpassableRoad(@Nullable final MapActivity activity, @NonNull final LatLon loc, final boolean showDialog, @Nullable final AvoidSpecificRoadsCallback callback, final boolean skipWritingSettings) { final Location ll = new Location(""); ll.setLatitude(loc.getLatitude()); ll.setLongitude(loc.getLongitude()); app.getLocationProvider().getRouteSegment(ll, new ResultMatcher<RouteDataObject>() { @Override public boolean publish(RouteDataObject object) { if (object == null) { if (activity != null) { Toast.makeText(activity, R.string.error_avoid_specific_road, Toast.LENGTH_LONG).show(); } if (callback != null) { callback.onAddImpassableRoad(false, null); } } else { addImpassableRoadInternal(object, ll, showDialog, activity, loc); if (callback != null) { callback.onAddImpassableRoad(true, object); } } return true; } @Override public boolean isCancelled() { return callback != null && callback.isCancelled(); } }); if (!skipWritingSettings) { app.getSettings().addImpassableRoad(loc.getLatitude(), loc.getLongitude()); } } public void replaceImpassableRoad(final MapActivity activity, final RouteDataObject currentObject, final LatLon loc, final boolean showDialog, final AvoidSpecificRoadsCallback callback) { LatLon latLon = getLocation(currentObject); app.getSettings().moveImpassableRoad(latLon, loc); final Location ll = new Location(""); ll.setLatitude(loc.getLatitude()); ll.setLongitude(loc.getLongitude()); app.getLocationProvider().getRouteSegment(ll, new ResultMatcher<RouteDataObject>() { @Override public boolean publish(RouteDataObject object) { if (object == null) { Toast.makeText(activity, R.string.error_avoid_specific_road, Toast.LENGTH_LONG).show(); if (callback != null) { callback.onAddImpassableRoad(false, null); } } else { getBuilder().removeImpassableRoad(currentObject); addImpassableRoadInternal(object, ll, showDialog, activity, loc); if (callback != null) { callback.onAddImpassableRoad(true, object); } } return true; } @Override public boolean isCancelled() { return callback != null && callback.isCancelled(); } }); } private void addImpassableRoadInternal(@NonNull RouteDataObject object, @NonNull Location ll, boolean showDialog, @Nullable MapActivity activity, @NonNull LatLon loc) { if(!getBuilder().addImpassableRoad(object, ll)) { app.getSettings().removeImpassableRoad(getLocation(object)); } RoutingHelper rh = app.getRoutingHelper(); if (rh.isRouteCalculated() || rh.isRouteBeingCalculated()) { rh.recalculateRouteDueToSettingsChange(); } if (activity != null) { if (showDialog) { showDialog(activity); } MapContextMenu menu = activity.getContextMenu(); if (menu.isActive() && menu.getLatLon().equals(loc)) { menu.close(); } activity.refreshMap(); } } private void showOnMap(MapActivity ctx, double lat, double lon, String name, DialogInterface dialog) { AnimateDraggingMapThread thread = ctx.getMapView().getAnimatedDraggingThread(); int fZoom = ctx.getMapView().getZoom() < 15 ? 15 : ctx.getMapView().getZoom(); if (thread.isAnimating()) { ctx.getMapView().setIntZoom(fZoom); ctx.getMapView().setLatLon(lat, lon); } else { thread.startMoving(lat, lon, fZoom, true); } ctx.getContextMenu().show(new LatLon(lat, lon), new PointDescription("", name), null); dialog.dismiss(); } private LatLon getLocation(RouteDataObject object) { Location location = app.getDefaultRoutingConfig().getImpassableRoadLocations().get(object.getId()); return new LatLon(location.getLatitude(), location.getLongitude()); } public interface AvoidSpecificRoadsCallback { void onAddImpassableRoad(boolean success, RouteDataObject newObject); boolean isCancelled(); } }