package net.osmand.plus.helpers; import android.app.Activity; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.ParcelFileDescriptor; import android.support.v7.app.AlertDialog; import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.data.FavouritePoint; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.ApplicationMode; import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.GPXUtilities; import net.osmand.plus.GPXUtilities.GPXFile; import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.MapMarkersHelper.MapMarker; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.TargetPointsHelper; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.audionotes.AudioVideoNotesPlugin; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.routing.RouteCalculationResult.NextDirectionInfo; import net.osmand.plus.routing.RouteDirectionInfo; import net.osmand.plus.routing.RoutingHelper; import net.osmand.router.TurnType; import net.osmand.util.Algorithms; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.util.Arrays; public class ExternalApiHelper { private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(ExternalApiHelper.class); public static final String API_CMD_SHOW_GPX = "show_gpx"; public static final String API_CMD_NAVIGATE_GPX = "navigate_gpx"; public static final String API_CMD_NAVIGATE = "navigate"; public static final String API_CMD_RECORD_AUDIO = "record_audio"; public static final String API_CMD_RECORD_VIDEO = "record_video"; public static final String API_CMD_RECORD_PHOTO = "record_photo"; public static final String API_CMD_STOP_AV_REC = "stop_av_rec"; public static final String API_CMD_GET_INFO = "get_info"; public static final String API_CMD_ADD_FAVORITE = "add_favorite"; public static final String API_CMD_ADD_MAP_MARKER = "add_map_marker"; public static final String API_CMD_START_GPX_REC = "start_gpx_rec"; public static final String API_CMD_STOP_GPX_REC = "stop_gpx_rec"; public static final String API_CMD_SUBSCRIBE_VOICE_NOTIFICATIONS = "subscribe_voice_notifications"; public static final int VERSION_CODE = 1; public static final String PARAM_NAME = "name"; public static final String PARAM_DESC = "desc"; public static final String PARAM_CATEGORY = "category"; public static final String PARAM_LAT = "lat"; public static final String PARAM_LON = "lon"; public static final String PARAM_COLOR = "color"; public static final String PARAM_VISIBLE = "visible"; public static final String PARAM_PATH = "path"; public static final String PARAM_URI = "uri"; public static final String PARAM_DATA = "data"; public static final String PARAM_FORCE = "force"; public static final String PARAM_START_NAME = "start_name"; public static final String PARAM_DEST_NAME = "dest_name"; public static final String PARAM_START_LAT = "start_lat"; public static final String PARAM_START_LON = "start_lon"; public static final String PARAM_DEST_LAT = "dest_lat"; public static final String PARAM_DEST_LON = "dest_lon"; public static final String PARAM_PROFILE = "profile"; public static final String PARAM_VERSION = "version"; public static final String PARAM_ETA = "eta"; public static final String PARAM_TIME_LEFT = "time_left"; public static final String PARAM_DISTANCE_LEFT = "time_distance_left"; public static final String PARAM_NT_DISTANCE = "turn_distance"; public static final String PARAM_NT_IMMINENT = "turn_imminent"; public static final String PARAM_NT_DIRECTION_NAME = "turn_name"; public static final String PARAM_NT_DIRECTION_TURN = "turn_type"; public static final String PARAM_NT_DIRECTION_LANES = "turn_lanes"; public static final String PARAM_CLOSE_AFTER_COMMAND = "close_after_command"; public static final ApplicationMode[] VALID_PROFILES = new ApplicationMode[]{ ApplicationMode.CAR, ApplicationMode.BICYCLE, ApplicationMode.PEDESTRIAN }; public static final ApplicationMode DEFAULT_PROFILE = ApplicationMode.CAR; // RESULT_OK == -1 // RESULT_CANCELED == 0 // RESULT_FIRST_USER == 1 // from Activity public static final int RESULT_CODE_ERROR_UNKNOWN = 1001; public static final int RESULT_CODE_ERROR_NOT_IMPLEMENTED = 1002; public static final int RESULT_CODE_ERROR_PLUGIN_INACTIVE = 1003; public static final int RESULT_CODE_ERROR_GPX_NOT_FOUND = 1004; public static final int RESULT_CODE_ERROR_INVALID_PROFILE = 1005; private MapActivity mapActivity; private int resultCode; private boolean finish; public int getResultCode() { return resultCode; } public boolean needFinish() { return finish; } public ExternalApiHelper(MapActivity mapActivity) { this.mapActivity = mapActivity; } public Intent processApiRequest(Intent intent) { Intent result = new Intent(); OsmandApplication app = (OsmandApplication) mapActivity.getApplication(); try { Uri uri = intent.getData(); String cmd = uri.getHost().toLowerCase(); if (API_CMD_SHOW_GPX.equals(cmd) || API_CMD_NAVIGATE_GPX.equals(cmd)) { boolean navigate = API_CMD_NAVIGATE_GPX.equals(cmd); String path = uri.getQueryParameter(PARAM_PATH); boolean force = uri.getBooleanQueryParameter(PARAM_FORCE, false); GPXFile gpx = null; if (path != null) { File f = new File(path); if (f.exists()) { gpx = GPXUtilities.loadGPXFile(mapActivity, f); } } else if (intent.getStringExtra(PARAM_DATA) != null) { String gpxStr = intent.getStringExtra(PARAM_DATA); if (!Algorithms.isEmpty(gpxStr)) { gpx = GPXUtilities.loadGPXFile(mapActivity, new ByteArrayInputStream(gpxStr.getBytes())); } } else if (uri.getBooleanQueryParameter(PARAM_URI, false)) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { LOG.debug("uriString=" + intent.getClipData().getItemAt(0).getUri()); Uri gpxUri = intent.getClipData().getItemAt(0).getUri(); ParcelFileDescriptor gpxParcelDescriptor = mapActivity.getContentResolver() .openFileDescriptor(gpxUri, "r"); if (gpxParcelDescriptor != null) { FileDescriptor fileDescriptor = gpxParcelDescriptor.getFileDescriptor(); gpx = GPXUtilities.loadGPXFile(mapActivity, new FileInputStream(fileDescriptor)); } else { finish = true; resultCode = RESULT_CODE_ERROR_GPX_NOT_FOUND; } } else { finish = true; resultCode = RESULT_CODE_ERROR_GPX_NOT_FOUND; } } else { finish = true; resultCode = RESULT_CODE_ERROR_GPX_NOT_FOUND; } if (gpx != null) { if (navigate) { final RoutingHelper routingHelper = app.getRoutingHelper(); if (routingHelper.isFollowingMode() && !force) { final GPXFile gpxFile = gpx; AlertDialog dlg = mapActivity.getMapActions().stopNavigationActionConfirm(); dlg.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (!routingHelper.isFollowingMode()) { startNavigation(gpxFile, null, null, null, null, null); } } }); } else { startNavigation(gpx, null, null, null, null, null); } } else { app.getSelectedGpxHelper().setGpxFileToDisplay(gpx); } resultCode = Activity.RESULT_OK; } else { finish = true; resultCode = RESULT_CODE_ERROR_GPX_NOT_FOUND; } } else if (API_CMD_NAVIGATE.equals(cmd)) { String profileStr = uri.getQueryParameter(PARAM_PROFILE); final ApplicationMode profile = ApplicationMode.valueOfStringKey(profileStr, DEFAULT_PROFILE); boolean validProfile = false; for (ApplicationMode mode : VALID_PROFILES) { if (mode == profile) { validProfile = true; break; } } if (!validProfile) { resultCode = RESULT_CODE_ERROR_INVALID_PROFILE; } else { String startName = uri.getQueryParameter(PARAM_START_NAME); if (Algorithms.isEmpty(startName)) { startName = ""; } String destName = uri.getQueryParameter(PARAM_DEST_NAME); if (Algorithms.isEmpty(destName)) { destName = ""; } final LatLon start; final PointDescription startDesc; String startLatStr = uri.getQueryParameter(PARAM_START_LAT); String startLonStr = uri.getQueryParameter(PARAM_START_LON); if (!Algorithms.isEmpty(startLatStr) && !Algorithms.isEmpty(startLonStr)) { double lat = Double.parseDouble(uri.getQueryParameter(PARAM_START_LAT)); double lon = Double.parseDouble(uri.getQueryParameter(PARAM_START_LON)); start = new LatLon(lat, lon); startDesc = new PointDescription(PointDescription.POINT_TYPE_LOCATION, startName); } else { start = null; startDesc = null; } double destLat = Double.parseDouble(uri.getQueryParameter(PARAM_DEST_LAT)); double destLon = Double.parseDouble(uri.getQueryParameter(PARAM_DEST_LON)); final LatLon dest = new LatLon(destLat, destLon); final PointDescription destDesc = new PointDescription(PointDescription.POINT_TYPE_LOCATION, destName); boolean force = uri.getBooleanQueryParameter(PARAM_FORCE, false); final RoutingHelper routingHelper = app.getRoutingHelper(); if (routingHelper.isFollowingMode() && !force) { AlertDialog dlg = mapActivity.getMapActions().stopNavigationActionConfirm(); dlg.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { if (!routingHelper.isFollowingMode()) { startNavigation(null, start, startDesc, dest, destDesc, profile); } } }); } else { startNavigation(null, start, startDesc, dest, destDesc, profile); } } } else if (API_CMD_RECORD_AUDIO.equals(cmd) || API_CMD_RECORD_VIDEO.equals(cmd) || API_CMD_RECORD_PHOTO.equals(cmd) || API_CMD_STOP_AV_REC.equals(cmd)) { AudioVideoNotesPlugin plugin = OsmandPlugin.getEnabledPlugin(AudioVideoNotesPlugin.class); if (plugin == null) { resultCode = RESULT_CODE_ERROR_PLUGIN_INACTIVE; finish = true; } else { if (API_CMD_STOP_AV_REC.equals(cmd)) { plugin.stopRecording(mapActivity, false); } else { double lat = Double.parseDouble(uri.getQueryParameter(PARAM_LAT)); double lon = Double.parseDouble(uri.getQueryParameter(PARAM_LON)); if (API_CMD_RECORD_AUDIO.equals(cmd)) { plugin.recordAudio(lat, lon, mapActivity); } else if (API_CMD_RECORD_VIDEO.equals(cmd)) { plugin.recordVideo(lat, lon, mapActivity); } else if (API_CMD_RECORD_PHOTO.equals(cmd)) { plugin.takePhoto(lat, lon, mapActivity, true); } } resultCode = Activity.RESULT_OK; } } else if (API_CMD_GET_INFO.equals(cmd)) { Location location = mapActivity.getMyApplication().getLocationProvider().getLastKnownLocation(); if (location != null) { result.putExtra(PARAM_LAT, location.getLatitude()); result.putExtra(PARAM_LON, location.getLongitude()); } final RoutingHelper routingHelper = app.getRoutingHelper(); if (routingHelper.isRouteCalculated()) { int time = routingHelper.getLeftTime(); result.putExtra(PARAM_TIME_LEFT, time); long eta = time + System.currentTimeMillis() / 1000; result.putExtra(PARAM_ETA, eta); result.putExtra(PARAM_DISTANCE_LEFT, routingHelper.getLeftDistance()); NextDirectionInfo ni = routingHelper.getNextRouteDirectionInfo(new NextDirectionInfo(), true); if (ni.distanceTo > 0) { updateTurnInfo("next_", result, ni); ni = routingHelper.getNextRouteDirectionInfoAfter(ni, new NextDirectionInfo(), true); if (ni.distanceTo > 0) { updateTurnInfo("after_next", result, ni); } } routingHelper.getNextRouteDirectionInfo(new NextDirectionInfo(), false); if (ni.distanceTo > 0) { updateTurnInfo("no_speak_next_", result, ni); } } result.putExtra(PARAM_VERSION, VERSION_CODE); finish = true; resultCode = Activity.RESULT_OK; } else if (API_CMD_ADD_FAVORITE.equals(cmd)) { String name = uri.getQueryParameter(PARAM_NAME); String desc = uri.getQueryParameter(PARAM_DESC); String category = uri.getQueryParameter(PARAM_CATEGORY); double lat = Double.parseDouble(uri.getQueryParameter(PARAM_LAT)); double lon = Double.parseDouble(uri.getQueryParameter(PARAM_LON)); String colorTag = uri.getQueryParameter(PARAM_COLOR); boolean visible = uri.getBooleanQueryParameter(PARAM_VISIBLE, true); if (name == null) { name = ""; } if (desc == null) { desc = ""; } if (category == null) { category = ""; } int color = 0; if (!Algorithms.isEmpty(colorTag)) { color = ColorDialogs.getColorByTag(colorTag); if (color == 0) { LOG.error("Wrong color tag: " + colorTag); } } FavouritePoint fav = new FavouritePoint(lat, lon, name, category); fav.setDescription(desc); fav.setColor(color); fav.setVisible(visible); FavouritesDbHelper helper = app.getFavorites(); helper.addFavourite(fav); showOnMap(lat, lon, fav, mapActivity.getMapLayers().getFavouritesLayer().getObjectName(fav)); resultCode = Activity.RESULT_OK; } else if (API_CMD_ADD_MAP_MARKER.equals(cmd)) { double lat = Double.parseDouble(uri.getQueryParameter(PARAM_LAT)); double lon = Double.parseDouble(uri.getQueryParameter(PARAM_LON)); String name = uri.getQueryParameter(PARAM_NAME); PointDescription pd = new PointDescription( PointDescription.POINT_TYPE_MAP_MARKER, name != null ? name : ""); MapMarkersHelper markersHelper = app.getMapMarkersHelper(); markersHelper.addMapMarker(new LatLon(lat, lon), pd); MapMarker marker = markersHelper.getFirstMapMarker(); if (marker != null) { showOnMap(lat, lon, marker, mapActivity.getMapLayers().getMapMarkersLayer().getObjectName(marker)); } resultCode = Activity.RESULT_OK; } else if (API_CMD_START_GPX_REC.equals(cmd)) { OsmandMonitoringPlugin plugin = OsmandPlugin.getEnabledPlugin(OsmandMonitoringPlugin.class); if (plugin == null) { resultCode = RESULT_CODE_ERROR_PLUGIN_INACTIVE; finish = true; } else { plugin.startGPXMonitoring(null); } if (uri.getBooleanQueryParameter(PARAM_CLOSE_AFTER_COMMAND, true)) { finish = true; } resultCode = Activity.RESULT_OK; } else if (API_CMD_STOP_GPX_REC.equals(cmd)) { OsmandMonitoringPlugin plugin = OsmandPlugin.getEnabledPlugin(OsmandMonitoringPlugin.class); if (plugin == null) { resultCode = RESULT_CODE_ERROR_PLUGIN_INACTIVE; finish = true; } else { plugin.stopRecording(); } if (uri.getBooleanQueryParameter(PARAM_CLOSE_AFTER_COMMAND, true)) { finish = true; } resultCode = Activity.RESULT_OK; } else if (API_CMD_SUBSCRIBE_VOICE_NOTIFICATIONS.equals(cmd)) { // not implemented yet resultCode = RESULT_CODE_ERROR_NOT_IMPLEMENTED; } } catch (Exception e) { LOG.error("Error processApiRequest:", e); resultCode = RESULT_CODE_ERROR_UNKNOWN; } return result; } private void updateTurnInfo(String prefix, Intent result, NextDirectionInfo ni) { result.putExtra(prefix + PARAM_NT_DISTANCE, ni.distanceTo); result.putExtra(prefix + PARAM_NT_IMMINENT, ni.imminent); if (ni.directionInfo != null && ni.directionInfo.getTurnType() != null) { TurnType tt = ni.directionInfo.getTurnType(); RouteDirectionInfo a = ni.directionInfo; result.putExtra(prefix + PARAM_NT_DIRECTION_NAME, RoutingHelper.formatStreetName(a.getStreetName(), a.getRef(), a.getDestinationName(), "")); result.putExtra(prefix + PARAM_NT_DIRECTION_TURN, tt.toXmlString()); if (tt.getLanes() != null) { result.putExtra(prefix + PARAM_NT_DIRECTION_LANES, Arrays.toString(tt.getLanes())); } } } private void showOnMap(double lat, double lon, Object object, PointDescription pointDescription) { MapContextMenu mapContextMenu = mapActivity.getContextMenu(); mapContextMenu.setMapCenter(new LatLon(lat, lon)); mapContextMenu.setMapPosition(mapActivity.getMapView().getMapPosition()); mapContextMenu.setCenterMarker(true); mapContextMenu.setMapZoom(15); mapContextMenu.show(new LatLon(lat, lon), pointDescription, object); } private void startNavigation(GPXFile gpx, LatLon from, PointDescription fromDesc, LatLon to, PointDescription toDesc, ApplicationMode mode) { OsmandApplication app = mapActivity.getMyApplication(); RoutingHelper routingHelper = app.getRoutingHelper(); if (gpx == null) { app.getSettings().APPLICATION_MODE.set(mode); final TargetPointsHelper targets = mapActivity.getMyApplication().getTargetPointsHelper(); targets.removeAllWayPoints(false, true); targets.navigateToPoint(to, true, -1, toDesc); } mapActivity.getMapActions().enterRoutePlanningModeGivenGpx(gpx, from, fromDesc, true, false); if (!app.getTargetPointsHelper().checkPointToNavigateShort()) { mapActivity.getMapLayers().getMapControlsLayer().getMapRouteInfoMenu().show(); } else { if (app.getSettings().APPLICATION_MODE.get() != routingHelper.getAppMode()) { app.getSettings().APPLICATION_MODE.set(routingHelper.getAppMode()); } mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); app.getSettings().FOLLOW_THE_ROUTE.set(true); routingHelper.setFollowingMode(true); routingHelper.setRoutePlanningMode(false); mapActivity.getMapViewTrackingUtilities().switchToRoutePlanningMode(); app.getRoutingHelper().notifyIfRouteIsCalculated(); routingHelper.setCurrentLocation(app.getLocationProvider().getLastKnownLocation(), false); } } public void testApi(OsmandApplication app, String command) { Uri uri = null; Intent intent = null; String lat = "44.98062"; String lon = "34.09258"; String destLat = "44.97799"; String destLon = "34.10286"; String gpxName = "xxx.gpx"; try { if (API_CMD_GET_INFO.equals(command)) { uri = Uri.parse("osmand.api://get_info"); } if (API_CMD_NAVIGATE.equals(command)) { // test navigate uri = Uri.parse("osmand.api://navigate" + "?start_lat=" + lat + "&start_lon=" + lon + "&start_name=Start" + "&dest_lat=" + destLat + "&dest_lon=" + destLon + "&dest_name=Finish" + "&profile=bicycle"); } if (API_CMD_RECORD_AUDIO.equals(command)) { // test record audio uri = Uri.parse("osmand.api://record_audio?lat=" + lat + "&lon=" + lon); } if (API_CMD_RECORD_VIDEO.equals(command)) { // test record video uri = Uri.parse("osmand.api://record_video?lat=" + lat + "&lon=" + lon); } if (API_CMD_RECORD_PHOTO.equals(command)) { // test take photo uri = Uri.parse("osmand.api://record_photo?lat=" + lat + "&lon=" + lon); } if (API_CMD_STOP_AV_REC.equals(command)) { // test record video uri = Uri.parse("osmand.api://stop_av_rec"); } if (API_CMD_ADD_MAP_MARKER.equals(command)) { // test marker uri = Uri.parse("osmand.api://add_map_marker?lat=" + lat + "&lon=" + lon + "&name=Marker"); } if (API_CMD_ADD_FAVORITE.equals(command)) { // test favorite uri = Uri.parse("osmand.api://add_favorite?lat=" + lat + "&lon=" + lon + "&name=Favorite&desc=Description&category=test2&color=red&visible=true"); } if (API_CMD_START_GPX_REC.equals(command)) { // test start gpx recording uri = Uri.parse("osmand.api://start_gpx_rec"); } if (API_CMD_STOP_GPX_REC.equals(command)) { // test stop gpx recording uri = Uri.parse("osmand.api://stop_gpx_rec"); } if (API_CMD_SHOW_GPX.equals(command)) { // test show gpx (path) //File gpx = new File(app.getAppPath(IndexConstants.GPX_INDEX_DIR), gpxName); //uri = Uri.parse("osmand.api://show_gpx?path=" + URLEncoder.encode(gpx.getAbsolutePath(), "UTF-8")); // test show gpx (data) uri = Uri.parse("osmand.api://show_gpx"); intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra("data", AndroidUtils.getFileAsString( new File(app.getAppPath(IndexConstants.GPX_INDEX_DIR), gpxName))); } if (API_CMD_NAVIGATE_GPX.equals(command)) { // test navigate gpx (path) //File gpx = new File(app.getAppPath(IndexConstants.GPX_INDEX_DIR), gpxName); //uri = Uri.parse("osmand.api://navigate_gpx?force=true&path=" + URLEncoder.encode(gpx.getAbsolutePath(), "UTF-8")); // test navigate gpx (data) uri = Uri.parse("osmand.api://navigate_gpx?force=true"); intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra("data", AndroidUtils.getFileAsString( new File(app.getAppPath(IndexConstants.GPX_INDEX_DIR), gpxName))); } if (intent == null && uri != null) { intent = new Intent(Intent.ACTION_VIEW, uri); } if (intent != null) { mapActivity.startActivity(intent); } } catch (Exception e) { LOG.error("Test failed", e); } } }