From ca48cf0835379a823a4d6342a6e6bbdc45d12fbf Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Thu, 24 Nov 2011 16:27:00 -0600 Subject: [PATCH 01/12] Take out of activity code to send poi edition to remote openstreetmap server --- OsmAnd/src/net/osmand/OpenstreetmapUtil.java | 408 ++++++++++++++++++ .../plus/activities/EditingPOIActivity.java | 401 +---------------- 2 files changed, 425 insertions(+), 384 deletions(-) create mode 100644 OsmAnd/src/net/osmand/OpenstreetmapUtil.java diff --git a/OsmAnd/src/net/osmand/OpenstreetmapUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapUtil.java new file mode 100644 index 0000000000..d0ead27a13 --- /dev/null +++ b/OsmAnd/src/net/osmand/OpenstreetmapUtil.java @@ -0,0 +1,408 @@ +package net.osmand; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import net.osmand.data.Amenity; +import net.osmand.osm.Entity; +import net.osmand.osm.Entity.EntityId; +import net.osmand.osm.Entity.EntityType; +import net.osmand.osm.EntityInfo; +import net.osmand.osm.MapUtils; +import net.osmand.osm.Node; +import net.osmand.osm.io.OsmBaseStorage; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; + +import org.apache.commons.logging.Log; +import org.apache.http.HttpResponse; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +import org.xml.sax.SAXException; +import org.xmlpull.v1.XmlSerializer; + +import android.util.Xml; +import android.widget.Toast; + +public class OpenstreetmapUtil { + +// private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; + private final static String SITE_API = "http://api.openstreetmap.org/"; //$NON-NLS-1$ + + public static enum Action {CREATE, MODIFY, DELETE}; + + private static final long NO_CHANGESET_ID = -1; + + private final MapActivity ctx; + private EntityInfo entityInfo; + + // reuse changeset + private long changeSetId = NO_CHANGESET_ID; + private long changeSetTimeStamp = NO_CHANGESET_ID; + + public final static Log log = LogUtil.getLog(OpenstreetmapUtil.class); + + public OpenstreetmapUtil(MapActivity uiContext){ + this.ctx = uiContext; + } + + public EntityInfo getEntityInfo() { + return entityInfo; + } + + protected String sendRequsetThroughHttpClient(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { + StringBuilder responseBody = new StringBuilder(); + try { + + HttpParams params = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(params, 15000); + DefaultHttpClient httpclient = new DefaultHttpClient(params); + if (doAuthenticate) { + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" //$NON-NLS-1$ + + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get()); + httpclient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), credentials); + } + HttpRequestBase method = null; + if (requestMethod.equals("GET")) { //$NON-NLS-1$ + method = new HttpGet(url); + } else if (requestMethod.equals("POST")) { //$NON-NLS-1$ + method = new HttpPost(url); + } else if (requestMethod.equals("PUT")) { //$NON-NLS-1$ + method = new HttpPut(url); + } else if (requestMethod.equals("DELETE")) { //$NON-NLS-1$ + method = new HttpDelete(url); + + } else { + throw new IllegalArgumentException(requestMethod + " is invalid method"); //$NON-NLS-1$ + } + if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + // TODO add when needed +// connection.setDoOutput(true); +// connection.setRequestProperty("Content-type", "text/xml"); +// OutputStream out = connection.getOutputStream(); +// if (requestBody != null) { +// BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); +// bwr.write(requestBody); +// bwr.flush(); +// } +// out.close(); + } + + HttpResponse response = httpclient.execute(method); + if(response.getStatusLine() == null || + response.getStatusLine().getStatusCode() != 200){ + + String msg; + if(response.getStatusLine() != null){ + msg = userOperation + " " +ctx.getString(R.string.failed_op); //$NON-NLS-1$ + } else { + msg = userOperation + " " + ctx.getString(R.string.failed_op) + response.getStatusLine().getStatusCode() + " : " + //$NON-NLS-1$//$NON-NLS-2$ + response.getStatusLine().getReasonPhrase(); + } + log.error(msg); + showWarning(msg); + } else { + InputStream is = response.getEntity().getContent(); + if (is != null) { + BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$ + String s; + while ((s = in.readLine()) != null) { + responseBody.append(s); + responseBody.append("\n"); //$NON-NLS-1$ + } + is.close(); + } + httpclient.getConnectionManager().shutdown(); + return responseBody.toString(); + } + } catch (MalformedURLException e) { + log.error(userOperation + " failed", e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } catch (IOException e) { + log.error(userOperation + " failed", e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } + return null; + + } + private String sendRequest(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { + log.info("Sending request " + url); //$NON-NLS-1$ +// if(true){ +// return sendRequsetThroughHttpClient(url, requestMethod, requestBody, userOperation, doAuthenticate); +// } + + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + + connection.setConnectTimeout(15000); + connection.setRequestMethod(requestMethod); + StringBuilder responseBody = new StringBuilder(); + if (doAuthenticate) { + String token = OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get(); //$NON-NLS-1$ + connection.addRequestProperty("Authorization", "Basic " + Base64.encode(token.getBytes("UTF-8"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + connection.setDoInput(true); + if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + connection.setDoOutput(true); + connection.setRequestProperty("Content-type", "text/xml"); //$NON-NLS-1$ //$NON-NLS-2$ + OutputStream out = connection.getOutputStream(); + if (requestBody != null) { + BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); //$NON-NLS-1$ + bwr.write(requestBody); + bwr.flush(); + } + out.close(); + } + connection.connect(); + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + String msg = userOperation + " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); //$NON-NLS-1$//$NON-NLS-2$ + log.error(msg); + showWarning(msg); + } else { + log.info("Response : " + connection.getResponseMessage()); //$NON-NLS-1$ + // populate return fields. + responseBody.setLength(0); + InputStream i = connection.getInputStream(); + if (i != null) { + BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); //$NON-NLS-1$ + String s; + boolean f = true; + while ((s = in.readLine()) != null) { + if(!f){ + responseBody.append("\n"); //$NON-NLS-1$ + } else { + f = false; + } + responseBody.append(s); + } + } + return responseBody.toString(); + } + } catch (NullPointerException e) { + // that's tricky case why NPE is thrown to fix that problem httpClient could be used + String msg = ctx.getString(R.string.auth_failed); + log.error(msg , e); + showWarning(msg); + } catch (MalformedURLException e) { + log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } catch (IOException e) { + log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_io_error_template), userOperation)); + } + + return null; + } + + + public long openChangeSet(String comment) { + long id = -1; + StringWriter writer = new StringWriter(256); + XmlSerializer ser = Xml.newSerializer(); + try { + ser.setOutput(writer); + ser.startDocument("UTF-8", true); //$NON-NLS-1$ + ser.startTag(null, "osm"); //$NON-NLS-1$ + ser.startTag(null, "changeset"); //$NON-NLS-1$ + + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", "comment"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "v", comment); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", "created_by"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "v", Version.APP_NAME_VERSION); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + ser.endTag(null, "changeset"); //$NON-NLS-1$ + ser.endTag(null, "osm"); //$NON-NLS-1$ + ser.endDocument(); + writer.close(); + } catch (IOException e) { + log.error("Unhandled exception", e); //$NON-NLS-1$ + } + String response = sendRequest(SITE_API + "api/0.6/changeset/create/", "PUT", writer.getBuffer().toString(), ctx.getString(R.string.opening_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ + if (response != null && response.length() > 0) { + id = Long.parseLong(response); + } + + return id; + } + + public void closeChangeSet(long id){ + String response = sendRequest(SITE_API+"api/0.6/changeset/"+id+"/close", "PUT", "", ctx.getString(R.string.closing_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + log.info("Response : " + response); //$NON-NLS-1$ + } + + private void writeNode(Node n, EntityInfo i, XmlSerializer ser, long changeSetId, String user) throws IllegalArgumentException, IllegalStateException, IOException{ + ser.startTag(null, "node"); //$NON-NLS-1$ + ser.attribute(null, "id", n.getId()+""); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "lat", n.getLatitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "lon", n.getLongitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ + if (i != null) { + // ser.attribute(null, "timestamp", i.getETimestamp()); + // ser.attribute(null, "uid", i.getUid()); + // ser.attribute(null, "user", i.getUser()); + ser.attribute(null, "visible", i.getVisible()); //$NON-NLS-1$ + ser.attribute(null, "version", i.getVersion()); //$NON-NLS-1$ + } + ser.attribute(null, "changeset", changeSetId+""); //$NON-NLS-1$ //$NON-NLS-2$ + + for(String k : n.getTagKeySet()){ + String val = n.getTag(k); + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", k); //$NON-NLS-1$ + ser.attribute(null, "v", val); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + } + ser.endTag(null, "node"); //$NON-NLS-1$ + } + + private boolean isNewChangesetRequired() { + // first commit + if (changeSetId == NO_CHANGESET_ID){ + return true; + } + + long now = System.currentTimeMillis(); + // changeset is idle for more than 30 minutes (1 hour according specification) + if (now - changeSetTimeStamp > 30 * 60 * 1000) { + return true; + } + + return false; + } + + public boolean commitNodeImpl(Action action, Node n, EntityInfo info, String comment){ + if (isNewChangesetRequired()){ + changeSetId = openChangeSet(comment); + changeSetTimeStamp = System.currentTimeMillis(); + } + if(changeSetId < 0){ + return false; + } + + final String a; + if (Action.MODIFY == action) { + a = "modify"; + } else if (Action.DELETE == action) { + a = "delete"; + } else { + a = "create"; + } + + + try { + StringWriter writer = new StringWriter(256); + XmlSerializer ser = Xml.newSerializer(); + try { + ser.setOutput(writer); + ser.startDocument("UTF-8", true); //$NON-NLS-1$ + ser.startTag(null, "osmChange"); //$NON-NLS-1$ + ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ + ser.startTag(null, a); + ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ + writeNode(n, info, ser, changeSetId, OsmandSettings.getOsmandSettings(ctx).USER_NAME.get()); + ser.endTag(null, a); + ser.endTag(null, "osmChange"); //$NON-NLS-1$ + ser.endDocument(); + } catch (IOException e) { + log.error("Unhandled exception", e); //$NON-NLS-1$ + } + String res = sendRequest(SITE_API+"api/0.6/changeset/"+changeSetId + "/upload", "POST", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + writer.getBuffer().toString(), ctx.getString(R.string.commiting_node), true); + log.debug(res+""); //$NON-NLS-1$ + if (res != null) { + if (Action.CREATE == action) { + long newId = n.getId(); + int i = res.indexOf("new_id=\""); //$NON-NLS-1$ + if (i > 0) { + i = i + "new_id=\"".length(); //$NON-NLS-1$ + int end = res.indexOf("\"", i); //$NON-NLS-1$ + if (end > 0) { + newId = Long.parseLong(res.substring(i, end)); + Node newN = new Node(n.getLatitude(), n.getLongitude(), newId); + for (String t : n.getTagKeySet()) { + newN.putTag(t, n.getTag(t)); + } + n = newN; + } + } + } + changeSetTimeStamp = System.currentTimeMillis(); + return true; + } + return false; + } finally { + // reuse changeset, do not close + //closeChangeSet(changeSetId); + } + } + + public Node loadNode(Amenity n) { + if(n.getId() % 2 == 1){ + // that's way id + return null; + } + long nodeId = n.getId() >> 1; + try { + String res = sendRequest(SITE_API+"api/0.6/node/"+nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ + if(res != null){ + OsmBaseStorage st = new OsmBaseStorage(); + st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ + EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); + Node entity = (Node) st.getRegisteredEntities().get(id); + entityInfo = st.getRegisteredEntityInfo().get(id); + // check whether this is node (because id of node could be the same as relation) + if(entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ + return entity; + } + return null; + } + + } catch (IOException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } catch (SAXException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } + return null; + } + + private void showWarning(final String msg){ + ctx.getMapView().post(new Runnable(){ + @Override + public void run() { + Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); + } + }); + } + +} diff --git a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java index 050333864d..5f955ba130 100644 --- a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java @@ -1,17 +1,5 @@ package net.osmand.plus.activities; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; import java.util.LinkedHashSet; @@ -19,48 +7,26 @@ import java.util.List; import java.util.Map; import java.util.Set; -import net.osmand.Base64; -import net.osmand.LogUtil; +import net.osmand.OpenstreetmapUtil; import net.osmand.OsmAndFormatter; -import net.osmand.Version; import net.osmand.data.Amenity; import net.osmand.data.AmenityType; -import net.osmand.osm.Entity; import net.osmand.osm.EntityInfo; import net.osmand.osm.MapRenderingTypes; -import net.osmand.osm.MapUtils; import net.osmand.osm.Node; -import net.osmand.osm.OpeningHoursParser; -import net.osmand.osm.Entity.EntityId; -import net.osmand.osm.Entity.EntityType; import net.osmand.osm.OSMSettings.OSMTagKey; +import net.osmand.osm.OpeningHoursParser; import net.osmand.osm.OpeningHoursParser.BasicDayOpeningHourRule; import net.osmand.osm.OpeningHoursParser.OpeningHoursRule; -import net.osmand.osm.io.OsmBaseStorage; import net.osmand.plus.AmenityIndexRepositoryOdb; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; -import org.apache.commons.logging.Log; -import org.apache.http.HttpResponse; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; -import org.xml.sax.SAXException; -import org.xmlpull.v1.XmlSerializer; import android.app.AlertDialog; +import android.app.AlertDialog.Builder; import android.app.Dialog; import android.app.ProgressDialog; -import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; @@ -69,7 +35,6 @@ import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.text.method.LinkMovementMethod; -import android.util.Xml; import android.view.View; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -80,29 +45,15 @@ import android.widget.Toast; public class EditingPOIActivity implements DialogProvider { -// private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; - private final static String SITE_API = "http://api.openstreetmap.org/"; //$NON-NLS-1$ - - private static final String DELETE_ACTION = "delete"; //$NON-NLS-1$ - private static final String MODIFY_ACTION = "modify"; //$NON-NLS-1$ - private static final String CREATE_ACTION = "create"; //$NON-NLS-1$ - - private static final long NO_CHANGESET_ID = -1; - private final MapActivity ctx; + private final OpenstreetmapUtil openstreetmapUtil; private AutoCompleteTextView typeText; private EditText nameText; private Button typeButton; private Button openHoursButton; private EditText openingHours; - private EntityInfo entityInfo; private EditText commentText; - // reuse changeset - private long changeSetId = NO_CHANGESET_ID; - private long changeSetTimeStamp = NO_CHANGESET_ID; - - private final static Log log = LogUtil.getLog(EditingPOIActivity.class); /* dialog stuff */ private static final int DIALOG_CREATE_POI = 200; @@ -119,10 +70,11 @@ public class EditingPOIActivity implements DialogProvider { public EditingPOIActivity(MapActivity uiContext){ this.ctx = uiContext; + this.openstreetmapUtil = new OpenstreetmapUtil(uiContext); } public void showEditDialog(Amenity editA){ - Node n = loadNode(editA); + Node n = openstreetmapUtil.loadNode(editA); if(n != null){ showPOIDialog(DIALOG_EDIT_POI, n, editA.getType(), editA.getSubType()); } else { @@ -144,7 +96,7 @@ public class EditingPOIActivity implements DialogProvider { } public void showDeleteDialog(Amenity a){ - final Node n = loadNode(a); + final Node n = openstreetmapUtil.loadNode(a); if(n == null){ Toast.makeText(ctx, ctx.getResources().getString(R.string.poi_error_poi_not_found), Toast.LENGTH_LONG).show(); return; @@ -172,7 +124,7 @@ public class EditingPOIActivity implements DialogProvider { public void onClick(DialogInterface dialog, int which) { Node n = (Node) args.getSerializable(KEY_AMENITY_NODE); String c = comment.getText().toString(); - commitNode(DELETE_ACTION, n, entityInfo, c, new Runnable(){ + commitNode(OpenstreetmapUtil.Action.DELETE, n, openstreetmapUtil.getEntityInfo(), c, new Runnable(){ @Override public void run() { Toast.makeText(ctx, ctx.getResources().getString(R.string.poi_remove_success), Toast.LENGTH_LONG).show(); @@ -285,7 +237,7 @@ public class EditingPOIActivity implements DialogProvider { Resources resources = v.getResources(); final String msg = n.getId() == -1 ? resources.getString(R.string.poi_action_add) : resources .getString(R.string.poi_action_change); - String action = n.getId() == -1 ? CREATE_ACTION : MODIFY_ACTION; + OpenstreetmapUtil.Action action = n.getId() == -1 ? OpenstreetmapUtil.Action.CREATE : OpenstreetmapUtil.Action.MODIFY; Map> typeNameToTagVal = MapRenderingTypes.getDefault().getAmenityTypeNameToTagVal(); AmenityType type = a.getType(); String tag = type.getDefaultTag(); @@ -313,7 +265,7 @@ public class EditingPOIActivity implements DialogProvider { } else { n.putTag(OSMTagKey.OPENING_HOURS.getValue(), openingHours.getText().toString()); } - commitNode(action, n, entityInfo, commentText.getText().toString(), new Runnable() { + commitNode(action, n, openstreetmapUtil.getEntityInfo(), commentText.getText().toString(), new Runnable() { @Override public void run() { Toast.makeText(ctx, MessageFormat.format(ctx.getResources().getString(R.string.poi_action_succeded_template), msg), @@ -355,15 +307,6 @@ public class EditingPOIActivity implements DialogProvider { updateSubTypesAdapter(a.getType()); } - private void showWarning(final String msg){ - ctx.getMapView().post(new Runnable(){ - @Override - public void run() { - Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); - } - }); - } - private Dialog createOpenHoursDlg(){ List time = OpeningHoursParser.parseOpenedHours(openingHours.getText().toString()); @@ -405,215 +348,7 @@ public class EditingPOIActivity implements DialogProvider { } - - protected String sendRequsetThroughHttpClient(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { - StringBuilder responseBody = new StringBuilder(); - try { - - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(params, 15000); - DefaultHttpClient httpclient = new DefaultHttpClient(params); - if (doAuthenticate) { - UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" //$NON-NLS-1$ - + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get()); - httpclient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), credentials); - } - HttpRequestBase method = null; - if (requestMethod.equals("GET")) { //$NON-NLS-1$ - method = new HttpGet(url); - } else if (requestMethod.equals("POST")) { //$NON-NLS-1$ - method = new HttpPost(url); - } else if (requestMethod.equals("PUT")) { //$NON-NLS-1$ - method = new HttpPut(url); - } else if (requestMethod.equals("DELETE")) { //$NON-NLS-1$ - method = new HttpDelete(url); - - } else { - throw new IllegalArgumentException(requestMethod + " is invalid method"); //$NON-NLS-1$ - } - if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - // TODO add when needed -// connection.setDoOutput(true); -// connection.setRequestProperty("Content-type", "text/xml"); -// OutputStream out = connection.getOutputStream(); -// if (requestBody != null) { -// BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); -// bwr.write(requestBody); -// bwr.flush(); -// } -// out.close(); - } - - HttpResponse response = httpclient.execute(method); - if(response.getStatusLine() == null || - response.getStatusLine().getStatusCode() != 200){ - - String msg; - if(response.getStatusLine() != null){ - msg = userOperation + " " +ctx.getString(R.string.failed_op); //$NON-NLS-1$ - } else { - msg = userOperation + " " + ctx.getString(R.string.failed_op) + response.getStatusLine().getStatusCode() + " : " + //$NON-NLS-1$//$NON-NLS-2$ - response.getStatusLine().getReasonPhrase(); - } - log.error(msg); - showWarning(msg); - } else { - InputStream is = response.getEntity().getContent(); - if (is != null) { - BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$ - String s; - while ((s = in.readLine()) != null) { - responseBody.append(s); - responseBody.append("\n"); //$NON-NLS-1$ - } - is.close(); - } - httpclient.getConnectionManager().shutdown(); - return responseBody.toString(); - } - } catch (MalformedURLException e) { - log.error(userOperation + " failed", e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } catch (IOException e) { - log.error(userOperation + " failed", e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } - return null; - - } - private String sendRequest(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { - log.info("Sending request " + url); //$NON-NLS-1$ -// if(true){ -// return sendRequsetThroughHttpClient(url, requestMethod, requestBody, userOperation, doAuthenticate); -// } - - try { - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - - connection.setConnectTimeout(15000); - connection.setRequestMethod(requestMethod); - StringBuilder responseBody = new StringBuilder(); - if (doAuthenticate) { - String token = OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get(); //$NON-NLS-1$ - connection.addRequestProperty("Authorization", "Basic " + Base64.encode(token.getBytes("UTF-8"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - connection.setDoInput(true); - if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - connection.setDoOutput(true); - connection.setRequestProperty("Content-type", "text/xml"); //$NON-NLS-1$ //$NON-NLS-2$ - OutputStream out = connection.getOutputStream(); - if (requestBody != null) { - BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); //$NON-NLS-1$ - bwr.write(requestBody); - bwr.flush(); - } - out.close(); - } - connection.connect(); - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - String msg = userOperation + " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); //$NON-NLS-1$//$NON-NLS-2$ - log.error(msg); - showWarning(msg); - } else { - log.info("Response : " + connection.getResponseMessage()); //$NON-NLS-1$ - // populate return fields. - responseBody.setLength(0); - InputStream i = connection.getInputStream(); - if (i != null) { - BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); //$NON-NLS-1$ - String s; - boolean f = true; - while ((s = in.readLine()) != null) { - if(!f){ - responseBody.append("\n"); //$NON-NLS-1$ - } else { - f = false; - } - responseBody.append(s); - } - } - return responseBody.toString(); - } - } catch (NullPointerException e) { - // that's tricky case why NPE is thrown to fix that problem httpClient could be used - String msg = ctx.getString(R.string.auth_failed); - log.error(msg , e); - showWarning(msg); - } catch (MalformedURLException e) { - log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } catch (IOException e) { - log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_io_error_template), userOperation)); - } - - return null; - } - - public long openChangeSet(String comment) { - long id = -1; - StringWriter writer = new StringWriter(256); - XmlSerializer ser = Xml.newSerializer(); - try { - ser.setOutput(writer); - ser.startDocument("UTF-8", true); //$NON-NLS-1$ - ser.startTag(null, "osm"); //$NON-NLS-1$ - ser.startTag(null, "changeset"); //$NON-NLS-1$ - - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", "comment"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "v", comment); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", "created_by"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "v", Version.getFullVersion(ctx)); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - ser.endTag(null, "changeset"); //$NON-NLS-1$ - ser.endTag(null, "osm"); //$NON-NLS-1$ - ser.endDocument(); - writer.close(); - } catch (IOException e) { - log.error("Unhandled exception", e); //$NON-NLS-1$ - } - String response = sendRequest(SITE_API + "api/0.6/changeset/create/", "PUT", writer.getBuffer().toString(), ctx.getString(R.string.opening_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ - if (response != null && response.length() > 0) { - id = Long.parseLong(response); - } - - return id; - } - - public void closeChangeSet(long id){ - String response = sendRequest(SITE_API+"api/0.6/changeset/"+id+"/close", "PUT", "", ctx.getString(R.string.closing_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - log.info("Response : " + response); //$NON-NLS-1$ - } - - private void writeNode(Node n, EntityInfo i, XmlSerializer ser, long changeSetId, String user) throws IllegalArgumentException, IllegalStateException, IOException{ - ser.startTag(null, "node"); //$NON-NLS-1$ - ser.attribute(null, "id", n.getId()+""); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "lat", n.getLatitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "lon", n.getLongitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ - if (i != null) { - // ser.attribute(null, "timestamp", i.getETimestamp()); - // ser.attribute(null, "uid", i.getUid()); - // ser.attribute(null, "user", i.getUser()); - ser.attribute(null, "visible", i.getVisible()); //$NON-NLS-1$ - ser.attribute(null, "version", i.getVersion()); //$NON-NLS-1$ - } - ser.attribute(null, "changeset", changeSetId+""); //$NON-NLS-1$ //$NON-NLS-2$ - - for(String k : n.getTagKeySet()){ - String val = n.getTag(k); - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", k); //$NON-NLS-1$ - ser.attribute(null, "v", val); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - } - ser.endTag(null, "node"); //$NON-NLS-1$ - } - - private void updateNodeInIndexes(String action, Node n) { + private void updateNodeInIndexes(OpenstreetmapUtil.Action action, Node n) { final OsmandApplication app = ctx.getMyApplication(); final AmenityIndexRepositoryOdb repo = app.getResourceManager().getUpdatablePoiDb(); ctx.getMapView().post(new Runnable() { @@ -630,12 +365,12 @@ public class EditingPOIActivity implements DialogProvider { }); // delete all amenities with same id - if (DELETE_ACTION.equals(action) || MODIFY_ACTION.equals(action)) { + if (OpenstreetmapUtil.Action.DELETE == action || OpenstreetmapUtil.Action.MODIFY == action) { repo.deleteAmenities(n.getId() << 1); repo.clearCache(); } // add amenities - if (!DELETE_ACTION.equals(action)) { + if (OpenstreetmapUtil.Action.DELETE != action) { List ams = Amenity.parseAmenities(n, new ArrayList()); for (Amenity a : ams) { repo.addAmenity(a); @@ -646,8 +381,8 @@ public class EditingPOIActivity implements DialogProvider { } - public void commitNode(final String action, final Node n, final EntityInfo info, final String comment, final Runnable successAction) { - if (info == null && !CREATE_ACTION.equals(action)) { + public void commitNode(final OpenstreetmapUtil.Action action, final Node n, final EntityInfo info, final String comment, final Runnable successAction) { + if (info == null && OpenstreetmapUtil.Action.CREATE != action) { Toast.makeText(ctx, ctx.getResources().getString(R.string.poi_error_info_not_loaded), Toast.LENGTH_LONG).show(); return; } @@ -657,7 +392,8 @@ public class EditingPOIActivity implements DialogProvider { @Override public void run() { try { - if (commitNodeImpl(action, n, info, comment)) { + if (openstreetmapUtil.commitNodeImpl(action, n, info, comment)) { + updateNodeInIndexes(action, n); ctx.getMapView().post(successAction); } } finally { @@ -668,110 +404,7 @@ public class EditingPOIActivity implements DialogProvider { }, "EditingPoi").start(); //$NON-NLS-1$ } - private boolean isNewChangesetRequired() { - // first commit - if (changeSetId == NO_CHANGESET_ID){ - return true; - } - long now = System.currentTimeMillis(); - // changeset is idle for more than 30 minutes (1 hour according specification) - if (now - changeSetTimeStamp > 30 * 60 * 1000) { - return true; - } - - return false; - } - - public boolean commitNodeImpl(String action, Node n, EntityInfo info, String comment){ - if (isNewChangesetRequired()){ - changeSetId = openChangeSet(comment); - changeSetTimeStamp = System.currentTimeMillis(); - } - if(changeSetId < 0){ - return false; - } - try { - StringWriter writer = new StringWriter(256); - XmlSerializer ser = Xml.newSerializer(); - try { - ser.setOutput(writer); - ser.startDocument("UTF-8", true); //$NON-NLS-1$ - ser.startTag(null, "osmChange"); //$NON-NLS-1$ - ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "generator", Version.getFullVersion(ctx)); //$NON-NLS-1$ - ser.startTag(null, action); - ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "generator", Version.getFullVersion(ctx)); //$NON-NLS-1$ - writeNode(n, info, ser, changeSetId, OsmandSettings.getOsmandSettings(ctx).USER_NAME.get()); - ser.endTag(null, action); - ser.endTag(null, "osmChange"); //$NON-NLS-1$ - ser.endDocument(); - } catch (IOException e) { - log.error("Unhandled exception", e); //$NON-NLS-1$ - } - String res = sendRequest(SITE_API+"api/0.6/changeset/"+changeSetId + "/upload", "POST", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - writer.getBuffer().toString(), ctx.getString(R.string.commiting_node), true); - log.debug(res+""); //$NON-NLS-1$ - if (res != null) { - if (CREATE_ACTION.equals(action)) { - long newId = n.getId(); - int i = res.indexOf("new_id=\""); //$NON-NLS-1$ - if (i > 0) { - i = i + "new_id=\"".length(); //$NON-NLS-1$ - int end = res.indexOf("\"", i); //$NON-NLS-1$ - if (end > 0) { - newId = Long.parseLong(res.substring(i, end)); - Node newN = new Node(n.getLatitude(), n.getLongitude(), newId); - for (String t : n.getTagKeySet()) { - newN.putTag(t, n.getTag(t)); - } - n = newN; - } - } - } - updateNodeInIndexes(action, n); - changeSetTimeStamp = System.currentTimeMillis(); - return true; - } - return false; - } finally { - // reuse changeset, do not close - //closeChangeSet(changeSetId); - } - } - - public Node loadNode(Amenity n) { - if(n.getId() % 2 == 1){ - // that's way id - return null; - } - long nodeId = n.getId() >> 1; - try { - String res = sendRequest(SITE_API+"api/0.6/node/"+nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ - if(res != null){ - OsmBaseStorage st = new OsmBaseStorage(); - st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ - EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); - Node entity = (Node) st.getRegisteredEntities().get(id); - entityInfo = st.getRegisteredEntityInfo().get(id); - // check whether this is node (because id of node could be the same as relation) - if(entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ - return entity; - } - return null; - } - - } catch (IOException e) { - log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ - Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); - } catch (SAXException e) { - log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ - Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); - } - return null; - } - @Override public Dialog onCreateDialog(int id) { Bundle args = dialogBundle; From 032130721e468706f022dd551c62f2897f5b8cec Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Thu, 24 Nov 2011 16:28:30 -0600 Subject: [PATCH 02/12] Create an interface for Openstreetmap class --- .../net/osmand/OpenstreetmapRemoteUtil.java | 406 ++++++++++++++++++ OsmAnd/src/net/osmand/OpenstreetmapUtil.java | 400 +---------------- .../plus/activities/EditingPOIActivity.java | 5 +- 3 files changed, 412 insertions(+), 399 deletions(-) create mode 100644 OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java diff --git a/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java new file mode 100644 index 0000000000..ad26b1c3aa --- /dev/null +++ b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java @@ -0,0 +1,406 @@ +package net.osmand; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import net.osmand.data.Amenity; +import net.osmand.osm.Entity; +import net.osmand.osm.Entity.EntityId; +import net.osmand.osm.Entity.EntityType; +import net.osmand.osm.EntityInfo; +import net.osmand.osm.MapUtils; +import net.osmand.osm.Node; +import net.osmand.osm.io.OsmBaseStorage; +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; +import net.osmand.plus.activities.MapActivity; + +import org.apache.commons.logging.Log; +import org.apache.http.HttpResponse; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.apache.http.params.HttpConnectionParams; +import org.apache.http.params.HttpParams; + +import org.xml.sax.SAXException; +import org.xmlpull.v1.XmlSerializer; + +import android.util.Xml; +import android.widget.Toast; + +public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { + +// private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; + private final static String SITE_API = "http://api.openstreetmap.org/"; //$NON-NLS-1$ + + private static final long NO_CHANGESET_ID = -1; + + private final MapActivity ctx; + private EntityInfo entityInfo; + + // reuse changeset + private long changeSetId = NO_CHANGESET_ID; + private long changeSetTimeStamp = NO_CHANGESET_ID; + + public final static Log log = LogUtil.getLog(OpenstreetmapRemoteUtil.class); + + public OpenstreetmapRemoteUtil(MapActivity uiContext){ + this.ctx = uiContext; + } + + public EntityInfo getEntityInfo() { + return entityInfo; + } + + protected String sendRequsetThroughHttpClient(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { + StringBuilder responseBody = new StringBuilder(); + try { + + HttpParams params = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(params, 15000); + DefaultHttpClient httpclient = new DefaultHttpClient(params); + if (doAuthenticate) { + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" //$NON-NLS-1$ + + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get()); + httpclient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), credentials); + } + HttpRequestBase method = null; + if (requestMethod.equals("GET")) { //$NON-NLS-1$ + method = new HttpGet(url); + } else if (requestMethod.equals("POST")) { //$NON-NLS-1$ + method = new HttpPost(url); + } else if (requestMethod.equals("PUT")) { //$NON-NLS-1$ + method = new HttpPut(url); + } else if (requestMethod.equals("DELETE")) { //$NON-NLS-1$ + method = new HttpDelete(url); + + } else { + throw new IllegalArgumentException(requestMethod + " is invalid method"); //$NON-NLS-1$ + } + if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + // TODO add when needed +// connection.setDoOutput(true); +// connection.setRequestProperty("Content-type", "text/xml"); +// OutputStream out = connection.getOutputStream(); +// if (requestBody != null) { +// BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); +// bwr.write(requestBody); +// bwr.flush(); +// } +// out.close(); + } + + HttpResponse response = httpclient.execute(method); + if(response.getStatusLine() == null || + response.getStatusLine().getStatusCode() != 200){ + + String msg; + if(response.getStatusLine() != null){ + msg = userOperation + " " +ctx.getString(R.string.failed_op); //$NON-NLS-1$ + } else { + msg = userOperation + " " + ctx.getString(R.string.failed_op) + response.getStatusLine().getStatusCode() + " : " + //$NON-NLS-1$//$NON-NLS-2$ + response.getStatusLine().getReasonPhrase(); + } + log.error(msg); + showWarning(msg); + } else { + InputStream is = response.getEntity().getContent(); + if (is != null) { + BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$ + String s; + while ((s = in.readLine()) != null) { + responseBody.append(s); + responseBody.append("\n"); //$NON-NLS-1$ + } + is.close(); + } + httpclient.getConnectionManager().shutdown(); + return responseBody.toString(); + } + } catch (MalformedURLException e) { + log.error(userOperation + " failed", e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } catch (IOException e) { + log.error(userOperation + " failed", e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } + return null; + + } + private String sendRequest(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { + log.info("Sending request " + url); //$NON-NLS-1$ +// if(true){ +// return sendRequsetThroughHttpClient(url, requestMethod, requestBody, userOperation, doAuthenticate); +// } + + try { + HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); + + connection.setConnectTimeout(15000); + connection.setRequestMethod(requestMethod); + StringBuilder responseBody = new StringBuilder(); + if (doAuthenticate) { + String token = OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get(); //$NON-NLS-1$ + connection.addRequestProperty("Authorization", "Basic " + Base64.encode(token.getBytes("UTF-8"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + connection.setDoInput(true); + if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + connection.setDoOutput(true); + connection.setRequestProperty("Content-type", "text/xml"); //$NON-NLS-1$ //$NON-NLS-2$ + OutputStream out = connection.getOutputStream(); + if (requestBody != null) { + BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); //$NON-NLS-1$ + bwr.write(requestBody); + bwr.flush(); + } + out.close(); + } + connection.connect(); + if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { + String msg = userOperation + " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); //$NON-NLS-1$//$NON-NLS-2$ + log.error(msg); + showWarning(msg); + } else { + log.info("Response : " + connection.getResponseMessage()); //$NON-NLS-1$ + // populate return fields. + responseBody.setLength(0); + InputStream i = connection.getInputStream(); + if (i != null) { + BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); //$NON-NLS-1$ + String s; + boolean f = true; + while ((s = in.readLine()) != null) { + if(!f){ + responseBody.append("\n"); //$NON-NLS-1$ + } else { + f = false; + } + responseBody.append(s); + } + } + return responseBody.toString(); + } + } catch (NullPointerException e) { + // that's tricky case why NPE is thrown to fix that problem httpClient could be used + String msg = ctx.getString(R.string.auth_failed); + log.error(msg , e); + showWarning(msg); + } catch (MalformedURLException e) { + log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); + } catch (IOException e) { + log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ + showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_io_error_template), userOperation)); + } + + return null; + } + + + public long openChangeSet(String comment) { + long id = -1; + StringWriter writer = new StringWriter(256); + XmlSerializer ser = Xml.newSerializer(); + try { + ser.setOutput(writer); + ser.startDocument("UTF-8", true); //$NON-NLS-1$ + ser.startTag(null, "osm"); //$NON-NLS-1$ + ser.startTag(null, "changeset"); //$NON-NLS-1$ + + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", "comment"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "v", comment); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", "created_by"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "v", Version.APP_NAME_VERSION); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + ser.endTag(null, "changeset"); //$NON-NLS-1$ + ser.endTag(null, "osm"); //$NON-NLS-1$ + ser.endDocument(); + writer.close(); + } catch (IOException e) { + log.error("Unhandled exception", e); //$NON-NLS-1$ + } + String response = sendRequest(SITE_API + "api/0.6/changeset/create/", "PUT", writer.getBuffer().toString(), ctx.getString(R.string.opening_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ + if (response != null && response.length() > 0) { + id = Long.parseLong(response); + } + + return id; + } + + public void closeChangeSet(long id){ + String response = sendRequest(SITE_API+"api/0.6/changeset/"+id+"/close", "PUT", "", ctx.getString(R.string.closing_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + log.info("Response : " + response); //$NON-NLS-1$ + } + + private void writeNode(Node n, EntityInfo i, XmlSerializer ser, long changeSetId, String user) throws IllegalArgumentException, IllegalStateException, IOException{ + ser.startTag(null, "node"); //$NON-NLS-1$ + ser.attribute(null, "id", n.getId()+""); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "lat", n.getLatitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "lon", n.getLongitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ + if (i != null) { + // ser.attribute(null, "timestamp", i.getETimestamp()); + // ser.attribute(null, "uid", i.getUid()); + // ser.attribute(null, "user", i.getUser()); + ser.attribute(null, "visible", i.getVisible()); //$NON-NLS-1$ + ser.attribute(null, "version", i.getVersion()); //$NON-NLS-1$ + } + ser.attribute(null, "changeset", changeSetId+""); //$NON-NLS-1$ //$NON-NLS-2$ + + for(String k : n.getTagKeySet()){ + String val = n.getTag(k); + ser.startTag(null, "tag"); //$NON-NLS-1$ + ser.attribute(null, "k", k); //$NON-NLS-1$ + ser.attribute(null, "v", val); //$NON-NLS-1$ + ser.endTag(null, "tag"); //$NON-NLS-1$ + } + ser.endTag(null, "node"); //$NON-NLS-1$ + } + + private boolean isNewChangesetRequired() { + // first commit + if (changeSetId == NO_CHANGESET_ID){ + return true; + } + + long now = System.currentTimeMillis(); + // changeset is idle for more than 30 minutes (1 hour according specification) + if (now - changeSetTimeStamp > 30 * 60 * 1000) { + return true; + } + + return false; + } + + public boolean commitNodeImpl(Action action, Node n, EntityInfo info, String comment){ + if (isNewChangesetRequired()){ + changeSetId = openChangeSet(comment); + changeSetTimeStamp = System.currentTimeMillis(); + } + if(changeSetId < 0){ + return false; + } + + final String a; + if (Action.MODIFY == action) { + a = "modify"; + } else if (Action.DELETE == action) { + a = "delete"; + } else { + a = "create"; + } + + + try { + StringWriter writer = new StringWriter(256); + XmlSerializer ser = Xml.newSerializer(); + try { + ser.setOutput(writer); + ser.startDocument("UTF-8", true); //$NON-NLS-1$ + ser.startTag(null, "osmChange"); //$NON-NLS-1$ + ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ + ser.startTag(null, a); + ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ + ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ + writeNode(n, info, ser, changeSetId, OsmandSettings.getOsmandSettings(ctx).USER_NAME.get()); + ser.endTag(null, a); + ser.endTag(null, "osmChange"); //$NON-NLS-1$ + ser.endDocument(); + } catch (IOException e) { + log.error("Unhandled exception", e); //$NON-NLS-1$ + } + String res = sendRequest(SITE_API+"api/0.6/changeset/"+changeSetId + "/upload", "POST", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + writer.getBuffer().toString(), ctx.getString(R.string.commiting_node), true); + log.debug(res+""); //$NON-NLS-1$ + if (res != null) { + if (Action.CREATE == action) { + long newId = n.getId(); + int i = res.indexOf("new_id=\""); //$NON-NLS-1$ + if (i > 0) { + i = i + "new_id=\"".length(); //$NON-NLS-1$ + int end = res.indexOf("\"", i); //$NON-NLS-1$ + if (end > 0) { + newId = Long.parseLong(res.substring(i, end)); + Node newN = new Node(n.getLatitude(), n.getLongitude(), newId); + for (String t : n.getTagKeySet()) { + newN.putTag(t, n.getTag(t)); + } + n = newN; + } + } + } + changeSetTimeStamp = System.currentTimeMillis(); + return true; + } + return false; + } finally { + // reuse changeset, do not close + //closeChangeSet(changeSetId); + } + } + + public Node loadNode(Amenity n) { + if(n.getId() % 2 == 1){ + // that's way id + return null; + } + long nodeId = n.getId() >> 1; + try { + String res = sendRequest(SITE_API+"api/0.6/node/"+nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ + if(res != null){ + OsmBaseStorage st = new OsmBaseStorage(); + st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ + EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); + Node entity = (Node) st.getRegisteredEntities().get(id); + entityInfo = st.getRegisteredEntityInfo().get(id); + // check whether this is node (because id of node could be the same as relation) + if(entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ + return entity; + } + return null; + } + + } catch (IOException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } catch (SAXException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } + return null; + } + + private void showWarning(final String msg){ + ctx.getMapView().post(new Runnable(){ + @Override + public void run() { + Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); + } + }); + } + +} diff --git a/OsmAnd/src/net/osmand/OpenstreetmapUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapUtil.java index d0ead27a13..ec231579e2 100644 --- a/OsmAnd/src/net/osmand/OpenstreetmapUtil.java +++ b/OsmAnd/src/net/osmand/OpenstreetmapUtil.java @@ -1,408 +1,16 @@ package net.osmand; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.List; - import net.osmand.data.Amenity; -import net.osmand.osm.Entity; -import net.osmand.osm.Entity.EntityId; -import net.osmand.osm.Entity.EntityType; import net.osmand.osm.EntityInfo; -import net.osmand.osm.MapUtils; import net.osmand.osm.Node; -import net.osmand.osm.io.OsmBaseStorage; -import net.osmand.plus.OsmandSettings; -import net.osmand.plus.R; -import net.osmand.plus.activities.MapActivity; -import org.apache.commons.logging.Log; -import org.apache.http.HttpResponse; -import org.apache.http.auth.AuthScope; -import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.methods.HttpDelete; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpPut; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.HttpConnectionParams; -import org.apache.http.params.HttpParams; - -import org.xml.sax.SAXException; -import org.xmlpull.v1.XmlSerializer; - -import android.util.Xml; -import android.widget.Toast; - -public class OpenstreetmapUtil { - -// private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; - private final static String SITE_API = "http://api.openstreetmap.org/"; //$NON-NLS-1$ +public interface OpenstreetmapUtil { public static enum Action {CREATE, MODIFY, DELETE}; - private static final long NO_CHANGESET_ID = -1; + public EntityInfo getEntityInfo(); - private final MapActivity ctx; - private EntityInfo entityInfo; + public boolean commitNodeImpl(Action action, Node n, EntityInfo info, String comment); - // reuse changeset - private long changeSetId = NO_CHANGESET_ID; - private long changeSetTimeStamp = NO_CHANGESET_ID; - - public final static Log log = LogUtil.getLog(OpenstreetmapUtil.class); - - public OpenstreetmapUtil(MapActivity uiContext){ - this.ctx = uiContext; - } - - public EntityInfo getEntityInfo() { - return entityInfo; - } - - protected String sendRequsetThroughHttpClient(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { - StringBuilder responseBody = new StringBuilder(); - try { - - HttpParams params = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(params, 15000); - DefaultHttpClient httpclient = new DefaultHttpClient(params); - if (doAuthenticate) { - UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" //$NON-NLS-1$ - + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get()); - httpclient.getCredentialsProvider().setCredentials(new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT), credentials); - } - HttpRequestBase method = null; - if (requestMethod.equals("GET")) { //$NON-NLS-1$ - method = new HttpGet(url); - } else if (requestMethod.equals("POST")) { //$NON-NLS-1$ - method = new HttpPost(url); - } else if (requestMethod.equals("PUT")) { //$NON-NLS-1$ - method = new HttpPut(url); - } else if (requestMethod.equals("DELETE")) { //$NON-NLS-1$ - method = new HttpDelete(url); - - } else { - throw new IllegalArgumentException(requestMethod + " is invalid method"); //$NON-NLS-1$ - } - if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - // TODO add when needed -// connection.setDoOutput(true); -// connection.setRequestProperty("Content-type", "text/xml"); -// OutputStream out = connection.getOutputStream(); -// if (requestBody != null) { -// BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8")); -// bwr.write(requestBody); -// bwr.flush(); -// } -// out.close(); - } - - HttpResponse response = httpclient.execute(method); - if(response.getStatusLine() == null || - response.getStatusLine().getStatusCode() != 200){ - - String msg; - if(response.getStatusLine() != null){ - msg = userOperation + " " +ctx.getString(R.string.failed_op); //$NON-NLS-1$ - } else { - msg = userOperation + " " + ctx.getString(R.string.failed_op) + response.getStatusLine().getStatusCode() + " : " + //$NON-NLS-1$//$NON-NLS-2$ - response.getStatusLine().getReasonPhrase(); - } - log.error(msg); - showWarning(msg); - } else { - InputStream is = response.getEntity().getContent(); - if (is != null) { - BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8")); //$NON-NLS-1$ - String s; - while ((s = in.readLine()) != null) { - responseBody.append(s); - responseBody.append("\n"); //$NON-NLS-1$ - } - is.close(); - } - httpclient.getConnectionManager().shutdown(); - return responseBody.toString(); - } - } catch (MalformedURLException e) { - log.error(userOperation + " failed", e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } catch (IOException e) { - log.error(userOperation + " failed", e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } - return null; - - } - private String sendRequest(String url, String requestMethod, String requestBody, String userOperation, boolean doAuthenticate) { - log.info("Sending request " + url); //$NON-NLS-1$ -// if(true){ -// return sendRequsetThroughHttpClient(url, requestMethod, requestBody, userOperation, doAuthenticate); -// } - - try { - HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection(); - - connection.setConnectTimeout(15000); - connection.setRequestMethod(requestMethod); - StringBuilder responseBody = new StringBuilder(); - if (doAuthenticate) { - String token = OsmandSettings.getOsmandSettings(ctx).USER_NAME.get() + ":" + OsmandSettings.getOsmandSettings(ctx).USER_PASSWORD.get(); //$NON-NLS-1$ - connection.addRequestProperty("Authorization", "Basic " + Base64.encode(token.getBytes("UTF-8"))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - connection.setDoInput(true); - if (requestMethod.equals("PUT") || requestMethod.equals("POST") || requestMethod.equals("DELETE")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - connection.setDoOutput(true); - connection.setRequestProperty("Content-type", "text/xml"); //$NON-NLS-1$ //$NON-NLS-2$ - OutputStream out = connection.getOutputStream(); - if (requestBody != null) { - BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"), 1024); //$NON-NLS-1$ - bwr.write(requestBody); - bwr.flush(); - } - out.close(); - } - connection.connect(); - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - String msg = userOperation + " " + ctx.getString(R.string.failed_op) + " : " + connection.getResponseMessage(); //$NON-NLS-1$//$NON-NLS-2$ - log.error(msg); - showWarning(msg); - } else { - log.info("Response : " + connection.getResponseMessage()); //$NON-NLS-1$ - // populate return fields. - responseBody.setLength(0); - InputStream i = connection.getInputStream(); - if (i != null) { - BufferedReader in = new BufferedReader(new InputStreamReader(i, "UTF-8"), 256); //$NON-NLS-1$ - String s; - boolean f = true; - while ((s = in.readLine()) != null) { - if(!f){ - responseBody.append("\n"); //$NON-NLS-1$ - } else { - f = false; - } - responseBody.append(s); - } - } - return responseBody.toString(); - } - } catch (NullPointerException e) { - // that's tricky case why NPE is thrown to fix that problem httpClient could be used - String msg = ctx.getString(R.string.auth_failed); - log.error(msg , e); - showWarning(msg); - } catch (MalformedURLException e) { - log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_unexpected_template), userOperation)); - } catch (IOException e) { - log.error(userOperation + " " + ctx.getString(R.string.failed_op) , e); //$NON-NLS-1$ - showWarning(MessageFormat.format(ctx.getResources().getString(R.string.poi_error_io_error_template), userOperation)); - } - - return null; - } - - - public long openChangeSet(String comment) { - long id = -1; - StringWriter writer = new StringWriter(256); - XmlSerializer ser = Xml.newSerializer(); - try { - ser.setOutput(writer); - ser.startDocument("UTF-8", true); //$NON-NLS-1$ - ser.startTag(null, "osm"); //$NON-NLS-1$ - ser.startTag(null, "changeset"); //$NON-NLS-1$ - - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", "comment"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "v", comment); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", "created_by"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "v", Version.APP_NAME_VERSION); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - ser.endTag(null, "changeset"); //$NON-NLS-1$ - ser.endTag(null, "osm"); //$NON-NLS-1$ - ser.endDocument(); - writer.close(); - } catch (IOException e) { - log.error("Unhandled exception", e); //$NON-NLS-1$ - } - String response = sendRequest(SITE_API + "api/0.6/changeset/create/", "PUT", writer.getBuffer().toString(), ctx.getString(R.string.opening_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ - if (response != null && response.length() > 0) { - id = Long.parseLong(response); - } - - return id; - } - - public void closeChangeSet(long id){ - String response = sendRequest(SITE_API+"api/0.6/changeset/"+id+"/close", "PUT", "", ctx.getString(R.string.closing_changeset), true); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - log.info("Response : " + response); //$NON-NLS-1$ - } - - private void writeNode(Node n, EntityInfo i, XmlSerializer ser, long changeSetId, String user) throws IllegalArgumentException, IllegalStateException, IOException{ - ser.startTag(null, "node"); //$NON-NLS-1$ - ser.attribute(null, "id", n.getId()+""); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "lat", n.getLatitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "lon", n.getLongitude()+""); //$NON-NLS-1$ //$NON-NLS-2$ - if (i != null) { - // ser.attribute(null, "timestamp", i.getETimestamp()); - // ser.attribute(null, "uid", i.getUid()); - // ser.attribute(null, "user", i.getUser()); - ser.attribute(null, "visible", i.getVisible()); //$NON-NLS-1$ - ser.attribute(null, "version", i.getVersion()); //$NON-NLS-1$ - } - ser.attribute(null, "changeset", changeSetId+""); //$NON-NLS-1$ //$NON-NLS-2$ - - for(String k : n.getTagKeySet()){ - String val = n.getTag(k); - ser.startTag(null, "tag"); //$NON-NLS-1$ - ser.attribute(null, "k", k); //$NON-NLS-1$ - ser.attribute(null, "v", val); //$NON-NLS-1$ - ser.endTag(null, "tag"); //$NON-NLS-1$ - } - ser.endTag(null, "node"); //$NON-NLS-1$ - } - - private boolean isNewChangesetRequired() { - // first commit - if (changeSetId == NO_CHANGESET_ID){ - return true; - } - - long now = System.currentTimeMillis(); - // changeset is idle for more than 30 minutes (1 hour according specification) - if (now - changeSetTimeStamp > 30 * 60 * 1000) { - return true; - } - - return false; - } - - public boolean commitNodeImpl(Action action, Node n, EntityInfo info, String comment){ - if (isNewChangesetRequired()){ - changeSetId = openChangeSet(comment); - changeSetTimeStamp = System.currentTimeMillis(); - } - if(changeSetId < 0){ - return false; - } - - final String a; - if (Action.MODIFY == action) { - a = "modify"; - } else if (Action.DELETE == action) { - a = "delete"; - } else { - a = "create"; - } - - - try { - StringWriter writer = new StringWriter(256); - XmlSerializer ser = Xml.newSerializer(); - try { - ser.setOutput(writer); - ser.startDocument("UTF-8", true); //$NON-NLS-1$ - ser.startTag(null, "osmChange"); //$NON-NLS-1$ - ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ - ser.startTag(null, a); - ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ - ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ - writeNode(n, info, ser, changeSetId, OsmandSettings.getOsmandSettings(ctx).USER_NAME.get()); - ser.endTag(null, a); - ser.endTag(null, "osmChange"); //$NON-NLS-1$ - ser.endDocument(); - } catch (IOException e) { - log.error("Unhandled exception", e); //$NON-NLS-1$ - } - String res = sendRequest(SITE_API+"api/0.6/changeset/"+changeSetId + "/upload", "POST", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - writer.getBuffer().toString(), ctx.getString(R.string.commiting_node), true); - log.debug(res+""); //$NON-NLS-1$ - if (res != null) { - if (Action.CREATE == action) { - long newId = n.getId(); - int i = res.indexOf("new_id=\""); //$NON-NLS-1$ - if (i > 0) { - i = i + "new_id=\"".length(); //$NON-NLS-1$ - int end = res.indexOf("\"", i); //$NON-NLS-1$ - if (end > 0) { - newId = Long.parseLong(res.substring(i, end)); - Node newN = new Node(n.getLatitude(), n.getLongitude(), newId); - for (String t : n.getTagKeySet()) { - newN.putTag(t, n.getTag(t)); - } - n = newN; - } - } - } - changeSetTimeStamp = System.currentTimeMillis(); - return true; - } - return false; - } finally { - // reuse changeset, do not close - //closeChangeSet(changeSetId); - } - } - - public Node loadNode(Amenity n) { - if(n.getId() % 2 == 1){ - // that's way id - return null; - } - long nodeId = n.getId() >> 1; - try { - String res = sendRequest(SITE_API+"api/0.6/node/"+nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ - if(res != null){ - OsmBaseStorage st = new OsmBaseStorage(); - st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ - EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); - Node entity = (Node) st.getRegisteredEntities().get(id); - entityInfo = st.getRegisteredEntityInfo().get(id); - // check whether this is node (because id of node could be the same as relation) - if(entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ - return entity; - } - return null; - } - - } catch (IOException e) { - log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ - Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); - } catch (SAXException e) { - log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ - Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); - } - return null; - } - - private void showWarning(final String msg){ - ctx.getMapView().post(new Runnable(){ - @Override - public void run() { - Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); - } - }); - } - + public Node loadNode(Amenity n); } diff --git a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java index 5f955ba130..969dbd1753 100644 --- a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import net.osmand.OpenstreetmapRemoteUtil; import net.osmand.OpenstreetmapUtil; import net.osmand.OsmAndFormatter; import net.osmand.data.Amenity; @@ -21,8 +22,6 @@ import net.osmand.osm.OpeningHoursParser.OpeningHoursRule; import net.osmand.plus.AmenityIndexRepositoryOdb; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; - - import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.app.Dialog; @@ -70,7 +69,7 @@ public class EditingPOIActivity implements DialogProvider { public EditingPOIActivity(MapActivity uiContext){ this.ctx = uiContext; - this.openstreetmapUtil = new OpenstreetmapUtil(uiContext); + this.openstreetmapUtil = new OpenstreetmapRemoteUtil(uiContext); } public void showEditDialog(Amenity editA){ From 83fd560681f4247319880d7da9a210520c50a06c Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Thu, 24 Nov 2011 16:32:24 -0600 Subject: [PATCH 03/12] Save asynchrone local edition in a local db --- .../net/osmand/OpenstreetmapLocalUtil.java | 92 ++++++++++++++++ OsmAnd/src/net/osmand/OpenstreetmapPoint.java | 91 ++++++++++++++++ .../net/osmand/OpenstreetmapRemoteUtil.java | 30 +++--- .../osmand/plus/OpenstreetmapsDbHelper.java | 100 ++++++++++++++++++ .../plus/activities/EditingPOIActivity.java | 9 +- 5 files changed, 308 insertions(+), 14 deletions(-) create mode 100644 OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java create mode 100644 OsmAnd/src/net/osmand/OpenstreetmapPoint.java create mode 100644 OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java diff --git a/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java new file mode 100644 index 0000000000..8fd20688ae --- /dev/null +++ b/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java @@ -0,0 +1,92 @@ +package net.osmand; + +import java.util.Map; + +import net.osmand.OpenstreetmapPoint; +import net.osmand.data.Amenity; +import net.osmand.data.AmenityType; +import net.osmand.osm.Entity; +import net.osmand.osm.Entity.EntityId; +import net.osmand.osm.Entity.EntityType; +import net.osmand.osm.EntityInfo; +import net.osmand.osm.MapRenderingTypes; +import net.osmand.osm.MapUtils; +import net.osmand.osm.Node; +import net.osmand.osm.OSMSettings.OSMTagKey; +import net.osmand.plus.R; +import net.osmand.plus.OpenstreetmapsDbHelper; +import net.osmand.plus.activities.MapActivity; + +import org.apache.commons.logging.Log; + +import android.util.Xml; +import android.widget.Toast; + +public class OpenstreetmapLocalUtil implements OpenstreetmapUtil { + + private final MapActivity ctx; + private final OpenstreetmapsDbHelper db; + + public final static Log log = LogUtil.getLog(OpenstreetmapLocalUtil.class); + + public OpenstreetmapLocalUtil(MapActivity uiContext){ + this.ctx = uiContext; + this.db = new OpenstreetmapsDbHelper(ctx); + } + + public EntityInfo getEntityInfo() { + return new EntityInfo(); + } + + public boolean commitNodeImpl(Action action, Node n, EntityInfo info, String comment){ + OpenstreetmapPoint p = new OpenstreetmapPoint(); + p.setEntity(n); + p.setAction(action); + p.setComment(comment); + return db.addOpenstreetmap(p); + } + + public Node loadNode(Amenity n) { + if(n.getId() % 2 == 1){ + // that's way id + return null; + } + long nodeId = n.getId() >> 1; + + EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); + Node entity = new Node(n.getLocation().getLatitude(), + n.getLocation().getLongitude(), + nodeId); + + Map> typeNameToTagVal = MapRenderingTypes.getDefault().getAmenityTypeNameToTagVal(); + AmenityType type = n.getType(); + String tag = type.getDefaultTag(); + String subType = n.getSubType(); + String val = subType; + if (typeNameToTagVal.containsKey(type)) { + Map map = typeNameToTagVal.get(type); + if (map.containsKey(subType)) { + String res = map.get(subType); + if (res != null) { + int i = res.indexOf(' '); + if (i != -1) { + tag = res.substring(0, i); + val = res.substring(i + 1); + } else { + tag = res; + } + } + } + } + entity.putTag(tag, val); + entity.putTag(OSMTagKey.NAME.getValue(), n.getName()); + entity.putTag(OSMTagKey.OPENING_HOURS.getValue(), n.getOpeningHours()); + + // check whether this is node (because id of node could be the same as relation) + if(entity != null && MapUtils.getDistance(entity.getLatLon(), n.getLocation()) < 50){ + return entity; + } + return null; + } + +} diff --git a/OsmAnd/src/net/osmand/OpenstreetmapPoint.java b/OsmAnd/src/net/osmand/OpenstreetmapPoint.java new file mode 100644 index 0000000000..c06ae9a2f2 --- /dev/null +++ b/OsmAnd/src/net/osmand/OpenstreetmapPoint.java @@ -0,0 +1,91 @@ +package net.osmand; + +import java.io.Serializable; + +import net.osmand.OpenstreetmapRemoteUtil; +import net.osmand.data.AmenityType; +import net.osmand.osm.Node; +import net.osmand.osm.OSMSettings.OSMTagKey; + +public class OpenstreetmapPoint implements Serializable { + private static final long serialVersionUID = 729654300829771467L; + private Node entity; + private OpenstreetmapUtil.Action action; + private String comment; + private boolean stored = false; + + public OpenstreetmapPoint(){ + } + + public long getId() { + return entity.getId(); + } + + public String getName() { + return entity.getTag(OSMTagKey.NAME.getValue()); + } + + public String getType() { + String type = AmenityType.valueToString(AmenityType.OTHER); + for(String k : entity.getTagKeySet()){ + if (!OSMTagKey.NAME.getValue().equals(k) && + !OSMTagKey.OPENING_HOURS.getValue().equals(k)) { + type = k; + break; + } + } + return type; + } + + public String getSubtype() { + return entity.getTag(this.getType()); + } + + public double getLatitude() { + return entity.getLatitude(); + } + + public double getLongitude() { + return entity.getLongitude(); + } + + public String getOpeninghours() { + return entity.getTag(OSMTagKey.OPENING_HOURS.getValue()); + } + + public Node getEntity() { + return entity; + } + + public OpenstreetmapUtil.Action getAction() { + return action; + } + + public String getComment() { + return comment; + } + + public boolean isStored() { + return stored; + } + + public void setEntity(Node entity) { + this.entity = entity; + } + + public void setAction(String action) { + this.action = OpenstreetmapRemoteUtil.actionString.get(action); + } + + public void setAction(OpenstreetmapUtil.Action action) { + this.action = action; + } + + public void setComment(String comment) { + this.comment = comment; + } + + public void setStored(boolean stored) { + this.stored = stored; + } +} diff --git a/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java index ad26b1c3aa..1fef744d5e 100644 --- a/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java +++ b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java @@ -14,7 +14,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.text.MessageFormat; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import net.osmand.data.Amenity; import net.osmand.osm.Entity; @@ -52,7 +54,19 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { // private final static String SITE_API = "http://api06.dev.openstreetmap.org/"; private final static String SITE_API = "http://api.openstreetmap.org/"; //$NON-NLS-1$ - + + public static final Map stringAction = new HashMap(); + public static final Map actionString = new HashMap(); + static { + stringAction.put(Action.CREATE, "create"); + stringAction.put(Action.MODIFY, "modify"); + stringAction.put(Action.DELETE, "delete"); + + actionString.put("create", Action.CREATE); + actionString.put("modify", Action.MODIFY); + actionString.put("delete", Action.DELETE); + }; + private static final long NO_CHANGESET_ID = -1; private final MapActivity ctx; @@ -304,16 +318,6 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { return false; } - final String a; - if (Action.MODIFY == action) { - a = "modify"; - } else if (Action.DELETE == action) { - a = "delete"; - } else { - a = "create"; - } - - try { StringWriter writer = new StringWriter(256); XmlSerializer ser = Xml.newSerializer(); @@ -323,11 +327,11 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { ser.startTag(null, "osmChange"); //$NON-NLS-1$ ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ - ser.startTag(null, a); + ser.startTag(null, stringAction.get(action)); ser.attribute(null, "version", "0.6"); //$NON-NLS-1$ //$NON-NLS-2$ ser.attribute(null, "generator", Version.APP_NAME); //$NON-NLS-1$ writeNode(n, info, ser, changeSetId, OsmandSettings.getOsmandSettings(ctx).USER_NAME.get()); - ser.endTag(null, a); + ser.endTag(null, stringAction.get(action)); ser.endTag(null, "osmChange"); //$NON-NLS-1$ ser.endDocument(); } catch (IOException e) { diff --git a/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java b/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java new file mode 100644 index 0000000000..1085e0cb76 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java @@ -0,0 +1,100 @@ +package net.osmand.plus; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.osmand.OpenstreetmapPoint; +import net.osmand.OpenstreetmapRemoteUtil; +import net.osmand.osm.Node; +import net.osmand.osm.OSMSettings.OSMTagKey; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class OpenstreetmapsDbHelper extends SQLiteOpenHelper { + + private static final int DATABASE_VERSION = 1; + public static final String OPENSTREETMAP_DB_NAME = "openstreetmap"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_TABLE_NAME = "openstreetmap"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_ID = "id"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_NAME = "name"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_TYPE = "type"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_SUBTYPE = "subtype"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_LAT = "latitude"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_LON = "longitude"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_ACTION = "action"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_COMMENT = "comment"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_COL_OPENINGHOURS = "openinghours"; //$NON-NLS-1$ + private static final String OPENSTREETMAP_TABLE_CREATE = "CREATE TABLE " + OPENSTREETMAP_TABLE_NAME + " (" + //$NON-NLS-1$ //$NON-NLS-2$ + OPENSTREETMAP_COL_ID + " INTEGER, " + OPENSTREETMAP_COL_NAME + " TEXT, " + OPENSTREETMAP_COL_TYPE + " TEXT, " + OPENSTREETMAP_COL_SUBTYPE + " TEXT, " + //$NON-NLS-1$ //$NON-NLS-2$ + OPENSTREETMAP_COL_LAT + " double, " + OPENSTREETMAP_COL_LON + " double, " + //$NON-NLS-1$ //$NON-NLS-2$ + OPENSTREETMAP_COL_ACTION + " TEXT, " + OPENSTREETMAP_COL_COMMENT + " TEXT, " + OPENSTREETMAP_COL_OPENINGHOURS + " TEXT);"; //$NON-NLS-1$ //$NON-NLS-2$ + + private List cachedOpenstreetmapPoints = new ArrayList(); + private final Context context; + + public OpenstreetmapsDbHelper(Context context) { + super(context, OPENSTREETMAP_DB_NAME, null, DATABASE_VERSION); + this.context = context; + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(OPENSTREETMAP_TABLE_CREATE); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + + public List getOpenstreetmapPoints() { + checkOpenstreetmapPoints(); + return cachedOpenstreetmapPoints; + } + + public boolean addOpenstreetmap(OpenstreetmapPoint p) { + checkOpenstreetmapPoints(); + SQLiteDatabase db = getWritableDatabase(); + if (db != null) { + db.execSQL("INSERT INTO " + OPENSTREETMAP_TABLE_NAME + + " (" + OPENSTREETMAP_COL_ID + ", " + OPENSTREETMAP_COL_NAME + ", " + OPENSTREETMAP_COL_TYPE + ", " + OPENSTREETMAP_COL_SUBTYPE + ", " + OPENSTREETMAP_COL_LAT + "," + OPENSTREETMAP_COL_LON + "," + OPENSTREETMAP_COL_ACTION + "," + OPENSTREETMAP_COL_COMMENT + "," + OPENSTREETMAP_COL_OPENINGHOURS + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", new Object[] { p.getId(), p.getName(), p.getType(), p.getSubtype(), p.getLatitude(), p.getLongitude(), OpenstreetmapRemoteUtil.stringAction.get(p.getAction()), p.getComment(), p.getOpeninghours() }); //$NON-NLS-1$ //$NON-NLS-2$ + cachedOpenstreetmapPoints.add(p); + p.setStored(true); + return true; + } + return false; + } + + private void checkOpenstreetmapPoints(){ + SQLiteDatabase db = getWritableDatabase(); + if (db != null) { + Cursor query = db.rawQuery("SELECT " + OPENSTREETMAP_COL_ID + ", " + OPENSTREETMAP_COL_NAME + ", " + OPENSTREETMAP_COL_TYPE + ", " + OPENSTREETMAP_COL_SUBTYPE + ", " + OPENSTREETMAP_COL_LAT + "," + OPENSTREETMAP_COL_LON + "," + OPENSTREETMAP_COL_ACTION + "," + OPENSTREETMAP_COL_COMMENT + "," + OPENSTREETMAP_COL_OPENINGHOURS + " FROM " + //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + OPENSTREETMAP_TABLE_NAME, null); + cachedOpenstreetmapPoints.clear(); + if (query.moveToFirst()) { + do { + String name = query.getString(1); + OpenstreetmapPoint p = new OpenstreetmapPoint(); + Node entity = new Node(query.getDouble(4), + query.getDouble(5), + query.getLong(0)); + + entity.putTag(query.getString(2), query.getString(3)); + entity.putTag(OSMTagKey.NAME.getValue(), name); + entity.putTag(OSMTagKey.OPENING_HOURS.getValue(), query.getString(8)); + p.setEntity(entity); + p.setStored(true); + p.setAction(query.getString(6)); + p.setComment(query.getString(7)); + } while (query.moveToNext()); + } + query.close(); + } + } + +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java index 969dbd1753..d51ea6127a 100644 --- a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java @@ -7,6 +7,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import net.osmand.OpenstreetmapLocalUtil; import net.osmand.OpenstreetmapRemoteUtil; import net.osmand.OpenstreetmapUtil; import net.osmand.OsmAndFormatter; @@ -69,7 +70,13 @@ public class EditingPOIActivity implements DialogProvider { public EditingPOIActivity(MapActivity uiContext){ this.ctx = uiContext; - this.openstreetmapUtil = new OpenstreetmapRemoteUtil(uiContext); + + OsmandSettings settings = OsmandSettings.getOsmandSettings(ctx); + if(!settings.isInternetConnectionAvailable(true)){ + this.openstreetmapUtil = new OpenstreetmapLocalUtil(ctx); + } else { + this.openstreetmapUtil = new OpenstreetmapRemoteUtil(ctx); + } } public void showEditDialog(Amenity editA){ From 84ad4b24a41be3a149e43f543c0a94cfdbbcd630 Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Thu, 24 Nov 2011 16:35:07 -0600 Subject: [PATCH 04/12] Add new activity to display all the POI changes to upload to openstreetmap server --- OsmAnd/AndroidManifest.xml | 7 +++--- OsmAnd/res/layout/local_openstreetmap.xml | 10 +++++++++ OsmAnd/res/values/strings.xml | 4 ++++ OsmAnd/res/xml/settings_pref.xml | 1 + .../src/net/osmand/plus/OsmandSettings.java | 2 ++ .../LocalOpenstreetmapActivity.java | 22 +++++++++++++++++++ .../plus/activities/SettingsActivity.java | 6 +++++ 7 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 OsmAnd/res/layout/local_openstreetmap.xml create mode 100644 OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index 809c72e1aa..463c48018a 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -55,9 +55,10 @@ - - - + + + + diff --git a/OsmAnd/res/layout/local_openstreetmap.xml b/OsmAnd/res/layout/local_openstreetmap.xml new file mode 100644 index 0000000000..d23da1e89b --- /dev/null +++ b/OsmAnd/res/layout/local_openstreetmap.xml @@ -0,0 +1,10 @@ + + + + + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 1aca7ae82a..d03e673757 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,5 +1,9 @@ + Asynchrone Openstreetmap POI Edition: + Local Openstreetmap Points + Local Points Saved in DB + Enable sending HTTP requests to a specified web service Enable Live Tracking Specify Live Tracking interval diff --git a/OsmAnd/res/xml/settings_pref.xml b/OsmAnd/res/xml/settings_pref.xml index 3fafac34a6..7af1ae0e3e 100644 --- a/OsmAnd/res/xml/settings_pref.xml +++ b/OsmAnd/res/xml/settings_pref.xml @@ -107,6 +107,7 @@ + diff --git a/OsmAnd/src/net/osmand/plus/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/OsmandSettings.java index 35d6ae1d28..44a12d46ec 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/OsmandSettings.java @@ -439,6 +439,8 @@ public class OsmandSettings { public final OsmandPreference USER_PASSWORD = new StringPreference("user_password", "", true); + public static final String LOCAL_OPENSTREETMAP_POINTS = "local_openstreetmap_points"; + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference DAYNIGHT_MODE = diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java b/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java new file mode 100644 index 0000000000..903576d60e --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java @@ -0,0 +1,22 @@ +package net.osmand.plus.activities; + +import net.osmand.plus.OsmandSettings; +import net.osmand.plus.R; + +import android.app.ExpandableListActivity; +import android.os.Bundle; + +public class LocalOpenstreetmapActivity extends ExpandableListActivity { + + private OsmandSettings settings; + + @SuppressWarnings("unchecked") + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.local_openstreetmap); + settings = OsmandSettings.getOsmandSettings(this); + } + +} diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java index 162f1b6be2..0ad031bc8d 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java @@ -67,6 +67,7 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference private Preference saveCurrentTrack; private Preference testVoiceCommands; + private Preference localOpenstreetmapPoints; private EditTextPreference applicationDir; private ListPreference applicationModePreference; @@ -331,6 +332,8 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference routeServiceEnabled.setOnPreferenceChangeListener(this); applicationDir = (EditTextPreference) screen.findPreference(OsmandSettings.EXTERNAL_STORAGE_DIR); applicationDir.setOnPreferenceChangeListener(this); + localOpenstreetmapPoints = (Preference) screen.findPreference(OsmandSettings.LOCAL_OPENSTREETMAP_POINTS); + localOpenstreetmapPoints.setOnPreferenceClickListener(this); @@ -778,6 +781,9 @@ public class SettingsActivity extends PreferenceActivity implements OnPreference helper.close(); } return true; + } else if(preference == localOpenstreetmapPoints){ + startActivity(new Intent(this, LocalOpenstreetmapActivity.class)); + return true; } return false; } From db007efb75be810a5083e446d972926c2c3d4072 Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Thu, 24 Nov 2011 16:43:26 -0600 Subject: [PATCH 05/12] Add list, delete and upload POI to Openstreetmap Offline POI edition activity --- .../layout/local_openstreetmap_list_item.xml | 7 + ...local_openstreetmap_list_item_category.xml | 8 + OsmAnd/res/values/strings.xml | 6 +- .../net/osmand/OpenstreetmapLocalUtil.java | 6 +- OsmAnd/src/net/osmand/OpenstreetmapPoint.java | 18 +- .../net/osmand/OpenstreetmapRemoteUtil.java | 33 ++- .../osmand/plus/OpenstreetmapsDbHelper.java | 27 +- .../plus/activities/EditingPOIActivity.java | 2 +- .../LocalOpenstreetmapActivity.java | 258 +++++++++++++++++- 9 files changed, 351 insertions(+), 14 deletions(-) create mode 100644 OsmAnd/res/layout/local_openstreetmap_list_item.xml create mode 100644 OsmAnd/res/layout/local_openstreetmap_list_item_category.xml diff --git a/OsmAnd/res/layout/local_openstreetmap_list_item.xml b/OsmAnd/res/layout/local_openstreetmap_list_item.xml new file mode 100644 index 0000000000..e9f5fe5143 --- /dev/null +++ b/OsmAnd/res/layout/local_openstreetmap_list_item.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/OsmAnd/res/layout/local_openstreetmap_list_item_category.xml b/OsmAnd/res/layout/local_openstreetmap_list_item_category.xml new file mode 100644 index 0000000000..a96308db9b --- /dev/null +++ b/OsmAnd/res/layout/local_openstreetmap_list_item_category.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index d03e673757..291090a91a 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -1,6 +1,10 @@ - Asynchrone Openstreetmap POI Edition: + + Show POI on Map + Upload modification to OSM + Delete POI modification + Asynchrone Openstreetmap POI Edition: Local Openstreetmap Points Local Points Saved in DB diff --git a/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java index 8fd20688ae..5d1b3c21b2 100644 --- a/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java +++ b/OsmAnd/src/net/osmand/OpenstreetmapLocalUtil.java @@ -15,21 +15,21 @@ import net.osmand.osm.Node; import net.osmand.osm.OSMSettings.OSMTagKey; import net.osmand.plus.R; import net.osmand.plus.OpenstreetmapsDbHelper; -import net.osmand.plus.activities.MapActivity; import org.apache.commons.logging.Log; +import android.content.Context; import android.util.Xml; import android.widget.Toast; public class OpenstreetmapLocalUtil implements OpenstreetmapUtil { - private final MapActivity ctx; + private final Context ctx; private final OpenstreetmapsDbHelper db; public final static Log log = LogUtil.getLog(OpenstreetmapLocalUtil.class); - public OpenstreetmapLocalUtil(MapActivity uiContext){ + public OpenstreetmapLocalUtil(Context uiContext){ this.ctx = uiContext; this.db = new OpenstreetmapsDbHelper(ctx); } diff --git a/OsmAnd/src/net/osmand/OpenstreetmapPoint.java b/OsmAnd/src/net/osmand/OpenstreetmapPoint.java index c06ae9a2f2..961cc90c43 100644 --- a/OsmAnd/src/net/osmand/OpenstreetmapPoint.java +++ b/OsmAnd/src/net/osmand/OpenstreetmapPoint.java @@ -22,7 +22,10 @@ public class OpenstreetmapPoint implements Serializable { } public String getName() { - return entity.getTag(OSMTagKey.NAME.getValue()); + String ret = entity.getTag(OSMTagKey.NAME.getValue()); + if (ret == null) + return ""; + return ret; } public String getType() { @@ -50,7 +53,10 @@ public class OpenstreetmapPoint implements Serializable { } public String getOpeninghours() { - return entity.getTag(OSMTagKey.OPENING_HOURS.getValue()); + String ret = entity.getTag(OSMTagKey.OPENING_HOURS.getValue()); + if (ret == null) + return ""; + return entity.getTag(ret); } public Node getEntity() { @@ -88,4 +94,12 @@ public class OpenstreetmapPoint implements Serializable { public void setStored(boolean stored) { this.stored = stored; } + + public String toString() { + return new StringBuffer("Openstreetmap Point ").append(this.getAction()).append(" ").append(this.getName()) + .append(" (").append(this.getId()).append("): [") + .append(this.getType()).append("/").append(this.getSubtype()) + .append(" (").append(this.getLatitude()).append(", ").append(this.getLongitude()) + .append(")]").toString(); + } } diff --git a/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java index 1fef744d5e..106f2ca962 100644 --- a/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java +++ b/OsmAnd/src/net/osmand/OpenstreetmapRemoteUtil.java @@ -47,7 +47,9 @@ import org.apache.http.params.HttpParams; import org.xml.sax.SAXException; import org.xmlpull.v1.XmlSerializer; +import android.content.Context; import android.util.Xml; +import android.view.View; import android.widget.Toast; public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { @@ -69,7 +71,8 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { private static final long NO_CHANGESET_ID = -1; - private final MapActivity ctx; + private final Context ctx; + private final View view; private EntityInfo entityInfo; // reuse changeset @@ -78,8 +81,9 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { public final static Log log = LogUtil.getLog(OpenstreetmapRemoteUtil.class); - public OpenstreetmapRemoteUtil(MapActivity uiContext){ + public OpenstreetmapRemoteUtil(Context uiContext, View view){ this.ctx = uiContext; + this.view = view; } public EntityInfo getEntityInfo() { @@ -367,6 +371,29 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { } } + public EntityInfo loadNode(Node n) { + long nodeId = n.getId() >> 1; + try { + String res = sendRequest(SITE_API + "api/0.6/node/"+nodeId, "GET", null, ctx.getString(R.string.loading_poi_obj) + nodeId, false); //$NON-NLS-1$ //$NON-NLS-2$ + if(res != null){ + OsmBaseStorage st = new OsmBaseStorage(); + st.parseOSM(new ByteArrayInputStream(res.getBytes("UTF-8")), null, null, true); //$NON-NLS-1$ + EntityId id = new Entity.EntityId(EntityType.NODE, nodeId); + Node entity = (Node) st.getRegisteredEntities().get(id); + entityInfo = st.getRegisteredEntityInfo().get(id); + return entityInfo; + } + + } catch (IOException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } catch (SAXException e) { + log.error("Loading node failed " + nodeId, e); //$NON-NLS-1$ + Toast.makeText(ctx, ctx.getResources().getString(R.string.error_io_error), Toast.LENGTH_LONG).show(); + } + return null; + } + public Node loadNode(Amenity n) { if(n.getId() % 2 == 1){ // that's way id @@ -399,7 +426,7 @@ public class OpenstreetmapRemoteUtil implements OpenstreetmapUtil { } private void showWarning(final String msg){ - ctx.getMapView().post(new Runnable(){ + view.post(new Runnable(){ @Override public void run() { Toast.makeText(ctx, msg, Toast.LENGTH_LONG).show(); diff --git a/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java b/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java index 1085e0cb76..1f7f0645cb 100644 --- a/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/OpenstreetmapsDbHelper.java @@ -70,6 +70,28 @@ public class OpenstreetmapsDbHelper extends SQLiteOpenHelper { return false; } + public boolean deleteOpenstreetmap(OpenstreetmapPoint p) { + checkOpenstreetmapPoints(); + SQLiteDatabase db = getWritableDatabase(); + if (db != null) { + db.execSQL("DELETE FROM " + OPENSTREETMAP_TABLE_NAME + + " WHERE " + OPENSTREETMAP_COL_ID + " = ? AND " + + OPENSTREETMAP_COL_NAME + " = ? AND " + + OPENSTREETMAP_COL_TYPE + " = ? AND " + + OPENSTREETMAP_COL_SUBTYPE + " = ? AND " + + OPENSTREETMAP_COL_LAT + " = ? AND " + + OPENSTREETMAP_COL_LON + " = ? AND " + + OPENSTREETMAP_COL_ACTION + " = ? AND " + + OPENSTREETMAP_COL_COMMENT + " = ? AND " + + OPENSTREETMAP_COL_OPENINGHOURS + " = ?", + new Object[] { p.getId(), p.getName(), p.getType(), p.getSubtype(), p.getLatitude(), p.getLongitude(), OpenstreetmapRemoteUtil.stringAction.get(p.getAction()), p.getComment(), p.getOpeninghours() }); //$NON-NLS-1$ //$NON-NLS-2$ + cachedOpenstreetmapPoints.remove(p); + p.setStored(false); + return true; + } + return false; + } + private void checkOpenstreetmapPoints(){ SQLiteDatabase db = getWritableDatabase(); if (db != null) { @@ -86,11 +108,14 @@ public class OpenstreetmapsDbHelper extends SQLiteOpenHelper { entity.putTag(query.getString(2), query.getString(3)); entity.putTag(OSMTagKey.NAME.getValue(), name); - entity.putTag(OSMTagKey.OPENING_HOURS.getValue(), query.getString(8)); + String openingHours = query.getString(8); + if (openingHours != null && openingHours.length() > 0) + entity.putTag(OSMTagKey.OPENING_HOURS.getValue(), openingHours); p.setEntity(entity); p.setStored(true); p.setAction(query.getString(6)); p.setComment(query.getString(7)); + cachedOpenstreetmapPoints.add(p); } while (query.moveToNext()); } query.close(); diff --git a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java index d51ea6127a..ef9db21d99 100644 --- a/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/EditingPOIActivity.java @@ -75,7 +75,7 @@ public class EditingPOIActivity implements DialogProvider { if(!settings.isInternetConnectionAvailable(true)){ this.openstreetmapUtil = new OpenstreetmapLocalUtil(ctx); } else { - this.openstreetmapUtil = new OpenstreetmapRemoteUtil(ctx); + this.openstreetmapUtil = new OpenstreetmapRemoteUtil(ctx, ctx.getMapView()); } } diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java b/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java index 903576d60e..d19f6b4cac 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalOpenstreetmapActivity.java @@ -1,15 +1,44 @@ package net.osmand.plus.activities; +import android.app.ExpandableListActivity; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.content.DialogInterface; +import android.graphics.Color; +import android.os.Bundle; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ContextMenu.ContextMenuInfo; +import android.widget.BaseExpandableListAdapter; +import android.widget.ExpandableListView; +import android.widget.TextView; +import android.widget.ExpandableListView.ExpandableListContextMenuInfo; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.osmand.OpenstreetmapPoint; +import net.osmand.OpenstreetmapUtil; +import net.osmand.OpenstreetmapRemoteUtil; +import net.osmand.LogUtil; +import net.osmand.osm.EntityInfo; +import net.osmand.plus.AmenityIndexRepositoryOdb; +import net.osmand.plus.OpenstreetmapsDbHelper; import net.osmand.plus.OsmandSettings; import net.osmand.plus.R; -import android.app.ExpandableListActivity; -import android.os.Bundle; - public class LocalOpenstreetmapActivity extends ExpandableListActivity { + private LocalOpenstreetmapAdapter listAdapter; + private OsmandSettings settings; + private OpenstreetmapsDbHelper db; + @SuppressWarnings("unchecked") @Override protected void onCreate(Bundle savedInstanceState) { @@ -17,6 +46,229 @@ public class LocalOpenstreetmapActivity extends ExpandableListActivity { setContentView(R.layout.local_openstreetmap); settings = OsmandSettings.getOsmandSettings(this); + listAdapter = new LocalOpenstreetmapAdapter(); + + getExpandableListView().setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + long packedPos = ((ExpandableListContextMenuInfo)menuInfo).packedPosition; + int group = ExpandableListView.getPackedPositionGroup(packedPos); + int child = ExpandableListView.getPackedPositionChild(packedPos); + if (child >= 0 && group >= 0) { + final OpenstreetmapPoint point = (OpenstreetmapPoint) listAdapter.getChild(group, child); + showContextMenu(point); + } + } + }); + + setListAdapter(listAdapter); + + db = new OpenstreetmapsDbHelper(this); + List l = db.getOpenstreetmapPoints(); + android.util.Log.d(LogUtil.TAG, "List of POI " + l.size() + " length"); + for (OpenstreetmapPoint p : l) { + listAdapter.addOpenstreetmapPoint(p); + } + listAdapter.notifyDataSetChanged(); + } + + private void showContextMenu(final OpenstreetmapPoint info) { + Builder builder = new AlertDialog.Builder(this); + final List menu = new ArrayList(); + + menu.add(R.string.local_openstreetmap_show_poi); + menu.add(R.string.local_openstreetmap_upload); + menu.add(R.string.local_openstreetmap_delete); + + String[] values = new String[menu.size()]; + for (int i = 0; i < values.length; i++) { + values[i] = getString(menu.get(i)); + } + builder.setItems(values, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + int resId = menu.get(which); + if (info != null) { + if (resId == R.string.local_openstreetmap_show_poi) { + OsmandSettings settings = OsmandSettings.getOsmandSettings(LocalOpenstreetmapActivity.this); + settings.setMapLocationToShow(info.getLatitude(), info.getLongitude(), settings.getLastKnownMapZoom()); + MapActivity.launchMapActivityMoveToTop(LocalOpenstreetmapActivity.this); + } else if (resId == R.string.local_openstreetmap_delete) { + listAdapter.delete(info); + } else if (resId == R.string.local_openstreetmap_upload) { + OpenstreetmapRemoteUtil remote = new OpenstreetmapRemoteUtil(LocalOpenstreetmapActivity.this, + LocalOpenstreetmapActivity.this.getWindow().getDecorView()); + EntityInfo entityInfo = null; + if (OpenstreetmapUtil.Action.CREATE != info.getAction()) { + entityInfo = remote.loadNode(info.getEntity()); + } + if (remote.commitNodeImpl(info.getAction(), info.getEntity(), entityInfo, info.getComment())) { + listAdapter.delete(info); + } + } + } + } + }); + + builder.show(); } + + protected class LocalOpenstreetmapAdapter extends BaseExpandableListAdapter { + Map> data = new LinkedHashMap>(); + List category = new ArrayList(); + List filterCategory = null; + + + public LocalOpenstreetmapAdapter() { + } + + public void clear() { + data.clear(); + category.clear(); + filterCategory = null; + notifyDataSetChanged(); + } + + public void delete(OpenstreetmapPoint i) { + final AmenityIndexRepositoryOdb repo = ((OsmandApplication) getApplication()).getResourceManager().getUpdatablePoiDb(); + android.util.Log.d(LogUtil.TAG, "Delete " + i); + db.deleteOpenstreetmap(i); + String c = i.getType(); + if(c != null){ + data.get(c).remove(i); + // We need to re-insert the POI if it is a delete or modify + repo.deleteAmenities(i.getId() << 1); + repo.clearCache(); + } + listAdapter.notifyDataSetChanged(); + } + + public void cancelFilter(){ + filterCategory = null; + notifyDataSetChanged(); + } + + public void filterCategories(String... types) { + List filter = new ArrayList(); + List source = filterCategory == null ? category : filterCategory; + for (String info : source) { + for (String ts : types) { + if (info.compareTo(ts) == 0) { + filter.add(info); + } + } + } + filterCategory = filter; + notifyDataSetChanged(); + } + + public void addOpenstreetmapPoint(OpenstreetmapPoint info) { + int found = -1; + // search from end + for (int i = category.size() - 1; i >= 0; i--) { + String cat = category.get(i); + if (cat.compareTo(info.getType()) == 0) { + found = i; + break; + } + } + if (found == -1) { + found = category.size(); + category.add(info.getType()); + } + if (!data.containsKey(category.get(found))) { + data.put(category.get(found), new ArrayList()); + } + data.get(category.get(found)).add(info); + } + + @Override + public OpenstreetmapPoint getChild(int groupPosition, int childPosition) { + String cat = filterCategory != null ? filterCategory.get(groupPosition) : category.get(groupPosition); + return data.get(cat).get(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + // it would be unusable to have 10000 local indexes + return groupPosition * 10000 + childPosition; + } + + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { + View v = convertView; + final OpenstreetmapPoint child = (OpenstreetmapPoint) getChild(groupPosition, childPosition); + if (v == null ) { + LayoutInflater inflater = getLayoutInflater(); + v = inflater.inflate(net.osmand.plus.R.layout.local_openstreetmap_list_item, parent, false); + } + TextView viewName = ((TextView) v.findViewById(R.id.local_openstreetmap_name)); + viewName.setText("(" + child.getSubtype() + ") " + child.getName()); + if (child.getAction() == OpenstreetmapUtil.Action.CREATE) { + viewName.setTextColor(Color.GREEN); + } else if (child.getAction() == OpenstreetmapUtil.Action.MODIFY) { + viewName.setTextColor(Color.MAGENTA); + } else if (child.getAction() == OpenstreetmapUtil.Action.DELETE) { + viewName.setTextColor(Color.RED); + } + + return v; + } + + @Override + public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + View v = convertView; + String group = getGroup(groupPosition); + if (v == null) { + LayoutInflater inflater = getLayoutInflater(); + v = inflater.inflate(net.osmand.plus.R.layout.local_openstreetmap_list_item_category, parent, false); + } + StringBuilder t = new StringBuilder(group); + TextView nameView = ((TextView) v.findViewById(R.id.local_openstreetmap_category_name)); + t.append(" [").append(getChildrenCount(groupPosition)); + if(getString(R.string.local_openstreetmap_items).length() > 0){ + t.append(" ").append(getString(R.string.local_openstreetmap_items)); + } + if(getString(R.string.local_openstreetmap_items).length() > 0){ + t.append(" ").append(getString(R.string.local_openstreetmap_items)); + } + List list = data.get(group); + t.append("]"); + nameView.setText(t.toString()); + + return v; + } + + @Override + public int getChildrenCount(int groupPosition) { + String cat = filterCategory != null ? filterCategory.get(groupPosition) : category.get(groupPosition); + return data.get(cat).size(); + } + + @Override + public String getGroup(int groupPosition) { + return filterCategory == null ? category.get(groupPosition) : filterCategory.get(groupPosition); + } + + @Override + public int getGroupCount() { + return filterCategory == null ? category.size() : filterCategory.size(); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + } } From efbacdb0988f3749ec954f2da885bcf956cca8a7 Mon Sep 17 00:00:00 2001 From: Fabien Carrion Date: Tue, 3 Jan 2012 20:05:32 -0600 Subject: [PATCH 06/12] Add an upload all POI button --- OsmAnd/res/layout/local_openstreetmap.xml | 5 ++++ OsmAnd/res/values/strings.xml | 1 + .../net/osmand/OpenstreetmapRemoteUtil.java | 18 +++++++++++++- .../LocalOpenstreetmapActivity.java | 24 +++++++++++++++++-- 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/OsmAnd/res/layout/local_openstreetmap.xml b/OsmAnd/res/layout/local_openstreetmap.xml index d23da1e89b..9759093947 100644 --- a/OsmAnd/res/layout/local_openstreetmap.xml +++ b/OsmAnd/res/layout/local_openstreetmap.xml @@ -4,6 +4,11 @@ android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> + +