for study we have to develop an Android game which is location based. Currently we use OSMDroid to show the map. There are resources (like wood, stone, ...) which the player has to collect. These resources are currently stored in our backend with hardcoded long/lat and will be added with setMarker onto the current map.
To provide this game globally, we want to set the resources dynamically based on the "real" world. So we need different layers from OSM (like forest, sea, ..) to set our resources automatically without asking our backend.
After some hours searching with google I found out that the Overpass API seems to help me implementing this functionality. But I can't find any tutorial for using Overpass API in Android. I tried some things but I don't get it... So I need your help, please give me an example or explanation how to implement this :/
This is my current code, but I don't think that this is correct..
URL url = new URL("http://overpass-api.de/api/interpreter");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.setDoOutput(true);
urlConnection.connect();
InputStream inputStream = urlConnection.getInputStream();
inputStream.close();
Following exception will be thrown at InputStream inputStream = urlConnection.getInputStream();:
W/System.err(3958): java.io.FileNotFoundException: http://overpass-api.de/api/interpreter
W/System.err(3958): at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177)
W/System.err(3958): at de.htw.berlin.games.based.location.gui.MapActivity$test.doInBackground(MapActivity.java:536)
W/System.err(3958): at de.htw.berlin.games.based.location.gui.MapActivity$test.doInBackground(MapActivity.java:1)
W/System.err(3958): at android.os.AsyncTask$2.call(AsyncTask.java:287)
W/System.err(3958): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
W/System.err(3958): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
W/System.err(3958): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
W/System.err(3958): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
W/System.err(3958): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
W/System.err(3958): at java.lang.Thread.run(Thread.java:856)
Thanks for all helpful replies :)
This exception you're getting is thrown because an HTTP GET call to http://overpass-api.de/api/interpreter returns a 400 Bad Request response.
What you want to do is a POST request to http://overpass-api.de/api/interpreter. An example of form-data to pass to this API is :
data='<?xml version="1.0" encoding="UTF-8"?><osm-script><!--
This is an example Overpass query.
Try it out by pressing the Run button above!
You can find more examples with the Load tool.
-->
<query type="node">
<has-kv k="amenity" v="drinking_water"/>
<bbox-query s="41.88659196260802" w="12.488558292388916" n="41.89248629819397" e="12.51119613647461"/><!--this is auto-completed with the
current map view coordinates.-->
</query>
<print/></osm-script>'
To find out how the API is working you should check, using your browser, what HTTP query is made to the API when clicking on Run in the example I pointed out.
EDIT
You can find plenty of examples like this one that shows how to post data using HTTP in Android. You'll have to add data as a key and the XML query string as a value in the used value pair container, such as :
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
nameValuePairs.add(new BasicNameValuePair("data", _The_XML_Query_String_));
Stick to the linked example for the rest and you hould be fine.
Check out https://github.com/zsoltk/overpasser. It's a Java library to ease working with the Overpass API.
You don't have to write the QL string yourself
It comes with a Retrofit adapter (so you can skip the networking part)
It has an Android sample showing how to put them all together over a Google Maps to get you started in no time
For anyone in future who needs to find a solution with overpass api, here is what I did very often: Overpass API can be addressed with a GET-Request. A GET-Request comes with a HTTP-protocoll and can be used in (I think) every programming language. You have to make a GET-request to the overpass-interpreter with all the queries in the url. In Android it would look like this:
String urlOverpass = "https://overpass-api.de/api/interpreter?data=[out:json][timeout:100];(node[shop=supermarket](52.402957,13.337429,52.420730,13.379530);way[shop=supermarket](52.402957,13.337429,52.420730,13.379530););out%20body;%3E;out%20skel%20qt;";
/* here you speak to the interpreter and you can insert whatever query you need. As an example look at overpass-turbo.eu*/
StringRequest request = new StringRequest(urlOverpass, new Response.Listener<String>() {
#Override
public void onResponse(String string) {
/* Here you can do whatever you like with the data which comes from the interpreter. The "string" is the response.*/
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {
/* Here you can explain what happens when an error is coming from the interpreter*/
}
});
RequestQueue rQueue = Volley.newRequestQueue(MainActivity.this);
rQueue.add(request);
Dont forget to implement the library: implementation 'com.android.volley:volley:1.1.1'
But there are multiple possibilities to fetch data from an api with a GET-request.
Related
My goal for this program was to poll the Google Directions API and plot the course by Polyline on a MapView in an Android app.
However, when I get the DirectionsResult back from the API call, trying to access directionsResult.routes[0] or directionsResult.geocodedWaypoints[0] results in a NullPointerException.
I am currently importing the following libraries using maven repositories (excerpt from my build.gradle):
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.google.http-client:google-http-client-android:1.21.0'
compile 'com.google.http-client:google-http-client-jackson:1.21.0'
compile 'com.google.http-client:google-http-client-jackson2:1.21.0'
compile 'com.google.http-client:google-http-client-gson:1.21.0'
compile 'com.google.http-client:google-http-client-protobuf:1.21.0'
compile 'com.google.http-client:google-http-client:1.21.0'
compile 'com.google.maps:google-maps-services:0.1.10'
compile 'com.google.android.gms:play-services:8.4.0'
My current code inside an AsyncTask implementation with debugging calls commented out:
#Override
protected synchronized String doInBackground(URL... params)
{
try {
HttpRequestFactory requestFactory = HTTP_TRANSPORT.createRequestFactory(new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
request.setParser(new JsonObjectParser(JSON_FACTORY));
}
});
GenericUrl url = new GenericUrl("http://maps.googleapis.com/maps/api/directions/json");
// to and from are both LatLng's
url.put("origin", from.toUrlValue());
url.put("destination", to.toUrlValue());
url.put("sensor", true);
HttpRequest request = requestFactory.buildGetRequest(url);
//wait(1000);
HttpResponse httpResponse = request.execute();
//Log.i(TAG, httpResponse.parseAsString());
//wait(1000);
DirectionsResult directionsResult = httpResponse.parseAs(DirectionsResult.class);
//wait(1000);
//Log.i(TAG, Integer.toString(httpResponse.getStatusCode()));
//Log.i(TAG, httpResponse.getStatusMessage());
latlngs = directionsResult.routes[0].overviewPolyline.decodePath();
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
My Debugging Process:
This is my printed request url:
http://maps.googleapis.com/maps/api/directions/json?origin=40.426870,-86.925083&destination=40.430092,-86.921679&sensor=true
Obviously it is a short but complete call. With elements of legs containing polyline points (which is what I assume I'm after). This JSON is the same as the response I got from reading httpResponse.parseAsString().
Looking at some StackExchange questions, I saw some people suggested waiting for data to be received. I put 1 second delays between all integral parts of request cycle with no results.
None of the other parts of the call were null.
httpResponse.getStatusCode() returned 200.
httpResponse.getStatusMessage() returned OK.
Everything appears normal until I attempt to use DirectionsResult to parse the JSON HttpResponse with:
DirectionsResult directionsResult = HttpResponse.parseAs(DirectionsResult.class);
After which I get a NullPointerException when accessing the two fields of DirectionsResult: routes and geocodedWaypoints.
Has anyone had similar issues with this class? I've been thinking I might submit an issue on the proper google-http-client library GitHub page if this isn't resolved here.
So it turns out that there were two things I was not doing right.
First, I included no API key in the URL, which doesn't explain why I was able to print a correctly formatted JSON response with httpRequest.parseAsString(), but regardless obviously an API key would need to be included for proper billing of my Google Developer account. Another change that needed to be made as a result of this, is changing the URL from 'http' to 'https'.
Second, there seems to be an issue with requesting an Android API key rather than a Server API key. I received this response when using the Android API key:
{
"error_message" : "This IP, site or mobile application is not authorized to use this API key. Request received from IP address 128.210.106.49, with empty referer",
"routes" : [],
"status" : "REQUEST_DENIED"
}
As a solution, when creating an API key, choose the Server option instead of Android.
I'm using an android app to submit to a Rails app with a Mongo backend hosted on Heroku. I am able to use the form online to submit a new item into the mongo database, but when I try to submit JSON from an android app I get:
{"error":"Unprocessable Entity","status":"422"}
I think this may have to do with the order of JSON when I submit as it seems to have gotten mixed up in order which I realized is normal according to a past SO question. But, the solution on that question did not work for me.
Here is the construction of the JSONObject in my Android app:
JSONObject jsonObject = new JSONObject();
jsonObject.put("ph", 2);
jsonObject.put("chlorine", 2.0);
jsonObject.put("magnified_Link", url);//URLEncoder.encode(encodedImage, "UTF-8"));
jsonObject.put("taste", "yucky");
jsonObject.put("odor", "smelly");
jsonObject.put("temperature", "77.0");
jsonObject.put("mercury", 234);
jsonObject.put("hardness", 9.0);
jsonObject.put("lat", latitude);
jsonObject.put("long", longitude);
String json = jsonObject.toString(); // Output to string
Log.d(TAG, json);
StringEntity se = new StringEntity(json);
// put json string into server
httpPost.setEntity(se);
//httpPost.setHeader("Authorization", "Client-ID " +API_KEY);
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
This is how the JSON appears in my android app when I log it before submission:
{"chlorine":2,"odor":"smelly","magnified_Link":"http:\/\/i.imgur.com\/JnbuUwy.jpg","long":0,"hardness":9,"ph":2,"taste":"yucky","lat":0,"mercury":234,"temperature":"77.0"}
This is how my JSON page looks like on my rails app with one successful submission into it through the online rails app:
[{"id":{"$oid":"558766f26633380003000000"},"ph":1.0,"chlorine":2.0,"magnified_Link":"3","taste":"4","odor":"5","temperature":6.0,"mercury":7.0,"hardness":8.0,"lat":40.7127,"long":-74.0059,"url":"https://distributed-health.herokuapp.com/distributed_healths/558766f26633380003000000.json"}]
Finally here is the model for a "DistributedHealth" (my object) in my rails app:
class DistributedHealth
include Mongoid::Document
field :ph, type: Float
field :chlorine, type: Float
field :magnified_Link, type: String
field :taste, type: String
field :odor, type: String
field :temperature, type: Float
field :mercury, type: Float
field :hardness, type: Float
field :lat, type: Float
field :long, type: Float
end
I can include any other relevant code and will make edits to this post as I continue trying to solve this issue.
Thank you,
Clayton.
EDIT: I have the error message from my heroku server
2015-06-23T21:29:34.382730+00:00 heroku[router]: at=info method=POST path="/distributed_healths.json" host=distributed-health.herokuapp.com request_id=2dcde4a4-3dcc-4381-98ba-6ca399400a6d fwd="128.54.58.245" dyno=web.1 connect=3ms service=14ms status=422 bytes=328
The issue ended up being a Rails side one and didn't have to do with the order of the JSON being submitted. I turned out the need for a CSRF token using the line right after class DistributedHealthsController < ApplicationController in my Controller:
skip_before_action :verify_authenticity_token
This isn't recommended as it is a work around security, but if you want to exclude certain methods this post will show you how.
More discussion on the topic can be found here and some documentation on the topic can be found here.
Even though I am now able to do a POST to my rails app I am still getting a Error 500 in my Android Studio log that looks like this:
{"error":"Internal Server Error","status":"500"}
It doesn't seem to be hindering functionality right now so I am not going to lose any sleep over it.
Spoiled by Python (e.g. requests.post(url, data={'p1':'v1','p2':'v2'}, headers={'H1': 'V1'})), I am looking for an equivalent to use on Android.
My code is already run on separate Threads, so I don't need AsyncWhatever.
HttpURLConnection is recommended for Android because it is all kinds of lightweight, but the equivalent of the Python request is... large.
I see multiple partial (e.g. pre-encoded params string) solutions, and several problems to work around. Rather than risk mistakes or overlooking something writing it myself, I ask:
Is such a wrapper available already, a request(Method, String, Map<String,String>, Map<String,String>), or similar?
I expect such a solution to need little code per call, and manage all weirdness (e.g. pre-Froyo keepAlive hack) itself.
I suppose I have the right solution for you. Had the same problem (spoiled by Node.js request) and didn't like the "interface" of HttpURLConnection.
You can find a tiny library without dependencies which wrappes HttpURLConnection in a way that not so common use cases can be implemented by using the wrapped HUC. It's called DavidWebb. There is also a link to alternative libraries in case you miss something.
A typical POST request with JSON payload and JSON response with some headers would look like this:
JSONObject postObj = new JSONObject();
postObj.put("p1", "v1");
postObj.put("p2", "v2");
Webb webb = Webb.create();
Response<JSONObject> response = webb
.post("http://www.example.com/app/myresource")
.header("x-date-header", new Date())
.header("x-other-header", "some-text")
.body(postObj)
.connectTimeout(3000)
.asJsonObject();
if (response.isSuccess()) {
JSONObject outcome = response.getBody();
// ...
} else {
System.out.println(response.getStatusCode());
System.out.println(response.getResponseMessage());
System.out.println(response.getErrorBody());
}
There are many ways to set default values for the Webb instance and use or overwrite them in the Request object.
The library is well tested, especially on Android, but it has no specific code or dependencies on Android API, so you have to manage keepAlive for Froyo by yourself.
Using Robolectric 2.3-SNAPSHOT, I want to test an object that'll execute a request in the background. In order to isolate it, I'm trying to mock the HttpResponse returned, without success after some hours invested.
I've created a project that anyone can clone. Simly run ./gradlew check https://github.com/Maragues/RobolectricDummyProject (git clone https://github.com/Maragues/RobolectricDummyProject.git)
I've tried
Robolectric.setDefaultHttpResponse(200, "my_mocked_word");
MockWebServer (https://code.google.com/p/mockwebserver/)
But the tests fail because they query the real URL
private static final String MOCKED_WORD = "MOCKED";
#Test
public void mockedRequestUsingMockServer() throws Exception {
mMockWebServer.enqueue(new MockResponse().setBody(MOCKED_WORD));
mMockWebServer.play();
Robolectric.getFakeHttpLayer().interceptHttpRequests(false);
Robolectric.getFakeHttpLayer().interceptResponseContent(false);
String result = request.loadDataFromNetwork();
assertEquals(MOCKED_WORD, result);
}
#Test
public void mockedRequestUsingRobolectric() throws Exception {
Robolectric.setDefaultHttpResponse(200, MOCKED_WORD);
String result = request.loadDataFromNetwork();
assertEquals(MOCKED_WORD, result);
}
The code executing the request
public String loadDataFromNetwork() throws Exception {
// With Uri.Builder class we can build our url is a safe manner
Uri.Builder uriBuilder = Uri.parse("http://robospice-sample.appspot.com/reverse").buildUpon();
uriBuilder.appendQueryParameter("word", word);
String url = uriBuilder.build().toString();
HttpURLConnection urlConnection = (HttpURLConnection) new URL(url)
.openConnection();
String result = IOUtils.toString(urlConnection.getInputStream());
urlConnection.disconnect();
return result;
}
Possibly related questions
Can't capture HTTP request with robolectric (I've tried that without success. Perhaps I'm missing something)
Anyone had success mocking HttpRequests with Robolectric? (I'm not using eclipse)
You're disabling Roboelectric's HTTP layer, so you're using the real HTTP layer. This means that there's no clever magic happening under the hood of your test: when you send an HTTP request, it's really going out onto the internet (as you are seeing).
MockWebServer doesn't stop this. It just sets up a web server locally, that your test can connect to.
So to resolve this problem, you need to stop attempting to connect to a real server, and instead, connect to the mock server. To do this, yo need to inject/set the URL in the request.
#Test
public void mockedRequestUsingMockServer() throws Exception {
mMockWebServer = new MockWebServer();
mMockWebServer.play();
mMockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(MOCKED_WORD));
request.myUrl = mMockWebServer.getUrl("/");
String result = request.loadDataFromNetwork();
assertEquals(MOCKED_WORD, result);
mMockWebServer.shutdown();
}
It turns out that Robolectric's FakeHttpLayer only works with Apache's HttpClient, which is highly discouraged on versions greater than Froyo. Extracted from Robolectric's Google Group
That being said, the usage of HttpUrlConnection will cause you trouble. I'd try to use Android's implementation of HttpClient where possible, since Robolectric intercepts all calls to that library and lets you set up canned responses to your HTTP calls. We're looking at doing the same for HttpUrlConnection, though it's not clear when that'll happen.
Apart from that, a unit test should not need to mock the HTTP layer. My approach was wrong from the beginning.
You can try this(ref:https://github.com/square/okhttp/tree/master/mockwebserver).
// Create a MockWebServer. These are lean enough that you can create a new
// instance for every unit test.
MockWebServer server = new MockWebServer();
// Schedule some responses.
server.enqueue(new MockResponse().setBody("it's all cool"));
// Start the server.
server.play();
// Ask the server for its URL. You'll need this to make HTTP requests.
//Http is my own http executor.
Http.Response response = http.get(server.getUrl("/"));
then, you can compare the response to server.enqueue(new MockResponse().setBody("it's all cool"));
MockWebServer is a part of okhttp https://github.com/square/okhttp/tree/master/mockwebserver. the URLConnectionImpl in android 4.4 have been changed from defaultHttpClient to Okhttp.
I am currently playing with Spring Android Resttemplate to interact with a java-backed REST API. Actually, I am using android annotations to send http calls to this back-end service and I must say it rocks. Basically, Android annotations allows you to define an interface for the service calls and the http methods to be used for each api call available : it will generate all the boiler-plate code related to low-level stuff like marshalling/unmarshalling, calling the right http method according to the interface definition.
Now, I would like to set some headers to http requests : How can I achieve this knowing that I only have a reference to the Service interface defining all the calls ?
I can also have reference to the RestTemplate object but it seems there is now way of setting the headers.
any help would really be appreciated
thanks
The way I approached it is by creating an instance of ApiClient in the application class and set a custom REST template.
In my case I was using Jackson for JSON message conversion:
RestTemplate restTemplate = new RestTemplate(fac);
MappingJacksonHttpMessageConverter converter =
new MappingJacksonHttpMessageConverter();
converter.getObjectMapper().configure(Feature.UNWRAP_ROOT_VALUE, true);
restTemplate
.getMessageConverters()
.add(converter);
mClient.setRestTemplate(restTemplate);
My request factory fac then looks like this:
ClientHttpRequestFactory fac = new HttpComponentsClientHttpRequestFactory() {
#Override
protected HttpUriRequest createHttpRequest(HttpMethod httpMethod, URI uri) {
HttpUriRequest uriRequest = super.createHttpRequest(httpMethod, uri);
// Add request headers
uriRequest.addHeader(
"Content-Type",
MediaType.APPLICATION_JSON_VALUE);
return uriRequest;
}
#Override
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod)
throws IOException {
if (Config.DEBUG_REQUESTS) {
Log.d(TAG, uri);
}
return super.createRequest(uri, httpMethod);
}
};
WARNING
Although this works on all Android devices in our office, I've recently discovered that headers don't appear to be added with all devices! I'm not sure why this is (or which devices specifically), but I'm looking in to it and will try to update this answer when I've found a resolution.