I want to do reverse geocoding in Android, and sometimes find the Geocoder is not quite reliable. So I researched to use the google Geocoding API.
I found there are two kinds such API: the client-side geocoding (JavaScript calls to google.maps.Geocoder) and server-side geocoding (HTTP requests to /maps/api/geocode).
Seems client-side geocoding is usually the most appropriate approach as it executes in the browser, so I think it's not suitable for Android. Am I right?
If using the server-side geocoding approach, Android would send the http requests to maps/api/geocode. Then I need to store the api key to the remote server and request it each time when app starts. Is is the best way to do so? Has any one did it this way?
===================================
Another question: should I use the geocoding result combined with google map? Can I just display the result to the end user without showing the map? What rules should I follow?
Google has provided the Geocoder class for handling geocoding and reverse geocoding for android device. You can take a look in this Geocoder class in this article https://developer.android.com/reference/android/location/Geocoder.html
The JavaScript geocoding libraries are not entirely "client-side", they still need to make an RPC to Google to get the geocode results.
You can make HTTP calls to https://maps.googleapis.com/maps/api/geocode from within your Android app easily enough using Volley and then parse the JSON response.
Something like (incomplete example):
Listener<JSONObject> listener = new Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
JSONArray jsonArray = response.getJSONArray("results");
if (jsonArray.length() > 0) {
JSONObject jsonObject = jsonArray.getJSONObject(0);
// Do something...
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
String url = "http://maps.googleapis.com/maps/api/geocode/json?sensor=true&latlng="
+ lat + "," + lng + "&key=" + API_KEY;
JsonObjectRequest request = new JsonObjectRequest(url, null, listener, errorListener);
mRequestQueue.add(request);
Related
I have written this code but I'm getting an error. How can I get to work?
But the same token works with postman.
Error:
{"message":"The security token included in the request is invalid."}
Code :
public class test extends AppCompatActivity {
private final AWS4Signer signer = new AWS4Signer();
Request<?> aws;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
aws = generateBasicRequest();
signer.setServiceName("execute-api");
signer.sign(aws, credentials);
new get_aws().execute();
}
private Request<?> generateBasicRequest() {
Request<?> request = new DefaultRequest<Void>("execute-api");
request.addHeader("Content-type", "application/json");
String securityToken = "Session Token";
request.addHeader("X-Amz-Security-Token", securityToken);
request.addHeader("Host", "********.amazonaws.com");
request.addHeader("x-amz-archive-description", "test test");
request.setResourcePath("/");
request.setEndpoint(URI.create("https://******.execute-api.****.amazonaws.com/data/all"));
return request;
}
private class get_aws extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
BufferedReader in = null;
String data = null;
try {
HttpClient httpclient = new DefaultHttpClient();
HttpGet request = new HttpGet();
request.addHeader("Authorization", aws.getHeaders().get("Authorization"));
request.addHeader("X-Amz-Date",request_aws.getHeaders().get("X-Amz-Date"));
request.addHeader("Content-Type","application/x-www-form-urlencoded");
URI website = new URI("https://********.execute-api.*******.amazonaws.com/data/all");
request.setURI(website);
HttpResponse response = httpclient.execute(request);
in = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));
String line = in.readLine();
Log.d("line", line);
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
return null;
}
#Override
protected void onPostExecute(Void result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
}
To answer your immediate question, AWS can generate a Java SDK from your API Gateway for you.
Using the generated SDK, you can then pass an AWSCredentialsProvider object into your SDK.
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
ApiClientFactory factory = new ApiClientFactory()
.credentialsProvider(credentials);
But...
You should never ship IAM access keys in a shipped application. These credentials can be retrieved by anyone who has installed your application by opening the .apk file.
Those credentials can then be used to access any other AWS actions the associated IAM User has access to in your account. This means anyone with access to the application apk (ie: anyone who can download the app from the app store) has access to your AWS account.
Depending what problem you're trying to solve will dictate the correct solution to the problem.
My Lambda needs an IAM Role to run
This is a fairly common mistake to make with API gateway when people see the "Invoke with caller credentials" option from API Gateway.
Uncheck this box and the Lambda will run with the IAM Role you defined in Lambda.
If requests fail after doing this, you need to make sure API Gateway has permission to invoke your lambda.
Restrict API to the application itself without users
Your application can't keep a secret and you have no user credentials.
You should disable Authorization completely, this is effectively a public API.
Requiring an API Key (and usage plan) to rate limit your API can be useful, but keep in mind this is not a security measure as, again - your application can't keep that key secret.
You want users to log in first (no existing source of users)
This makes sense if your API call is only designed to be called by registered users.
You'll need to configure Cognito User Pools for this. This shouldn't be confused with Cognito Federated Identities - which focuses on a different part of the problem. You can use it to solve this, but trust me - you'll be happier if you don't go down that path.
To get cracking you'll need to take a few steps:
Create a User Pool (detailed settings explained here).
Configure a Cognito Authorizer on your API Gateway.
Create an App Client for your pool. Don't generate a client secret key when you do this.
Integrate with your Android application. There's a prebuilt Android example available from AWS for getting the client side going: AmazonCognitoYourUserPoolsDemo
You want users to log in first (existing source of users)
If you can use SAML or OAuth2.0 / OpenID Connect to authenticate your users, follow the instructions and then configure federation.
If not, this is possibly the time to consider Cognito Federated Identities, specifically using the Developer Authenticated Identities process. But again, I'd really recommend against it.
API Gateway & Cognito is a massive topic. Hopefully the instructions provided are a great entry point to the relevant parts of the documentation.
Have you tried to look at the examples from AWS https://github.com/awslabs/aws-sdk-android-samples with cognito credentials, I found them easier to use, in case you want to use your AccessKey and SecretKey, you can also use something like this
AWSCredentials credentials = new BasicAWSCredentials("AccessKey", "SecretKey");
AmazonS3Client sS3Client = new AmazonS3Client(credentials,Region.getRegion("Region"));
I am currently working on creating a custom authentication for a Xamarin.Android app using Azure. I have successfully created my API and it is properly returning values when submitting a raw payload using Advanced REST Client.
I am now trying to implement this on Xamarin.Android using Azure's MobileServiceClient SDK and when using the invokeApi method as demonstrated below in my code, I am getting an exception indicating that it is calling GET instead of POST. Is anyone aware of what I might be doing wrong?
ex.Message returns
"Cannot GET /api/register?username=azureAccountTest&password=testingpassword"
public async Task RegisterAsync()
{
Dictionary<string, string> user = new Dictionary<string, string>
{
{ "username", username },
{ "password", password }
};
try
{
CancellationToken ct;
var result = await client.InvokeApiAsync("register", HttpMethod.Post, user, ct);
}
catch(Exception ex)
{
var message = ex.Message;
}
}
According to your description, I tested this issue on my local side and I could retrieve the authenticationToken as follows:
You used the following method for InvokeApiAsync:
public Task<JToken> InvokeApiAsync(string apiName, HttpMethod method, IDictionary<string, string> parameters, CancellationToken cancellationToken = default(CancellationToken));
Note: It summarizes that the Additional data will sent to through the query string.
Per my understanding, you could refer to the following method for sending additional data though the HTTP content as follows:
JObject user = new JObject();
user.Add("username", "bruce");
user.Add("password", "123456");
var result = await App.MobileService.InvokeApiAsync("/.auth/login/custom", HttpMethod.Post, user, ct);
Additionally, you need to specific the mobileAppUri with https endpoint when deploy to azure side. Here is a similar issue, you could refer to here. Moreover, I would recommend you refer to adrian hall's book about Custom Authentication.
UPDATE:
Based on your comment, I checked the custom authentication and found the following note from adrian hall's book about Custom Authentication:
You must turn on Authentication / Authorization in your App Service. Set the Action to take when request is not authenticated to Allow Request (no action) and do not configure any of the supported authentication providers.
I'm trying to use the AWS generated Android SDK for my API Gateway project. Based on the information on this Site I created a client interface with a API method like this:
#com.amazonaws.mobileconnectors.apigateway.annotation.Operation(path = "/events/bookingUpdate", method = "POST")
void bookingUpdatePost(BookingUpdate body);
So when I want to call my API I used the following code:
try {
clientInterface.bookingUpdatePost(generateBookingUpdateDeviceInformation(bookingUpdate));
} catch (ApiClientException e) {
Log.e(BuildConfig.APPLICATION_ID, e.getLocalizedMessage());
if (listener != null) {
listener.onBookingUpdatePostRequestFinished(new Error(e.getLocalizedMessage()));
}
}
is there any way to retrieve the APIResponse Headers from the amazon mobile connectors library?
There isn't a way to do so because it's not exposed externally. But you can use a generic invoker instead to achieve this.
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 want to create a route map navigation in Android google map v2 along with all those details which google provides( like at this crossing you have to turn left). I know this can be done using a parsing techniques i have done that but what im looking for is, is there any new method predefined within android v2 google map which will give all these details and will show the route map within two points.
Again Im mentioning it not using the JSON parsing technique. anything newly implemented for v2
Any sample projects or examples is very much helpful.
thanks
Without using json parsing technique to draw routes can be done by one method linking to google navigation by doing stuff like this.
String address = marker.getTitle();
Intent intent;
if (address == null) {
LatLng pt = marker.getPosition();
String lat = Double.toString(pt.latitude); //this i mentioned with current location
String lng = Double.toString(pt.longitude);
intent = new Intent(android.content.Intent.ACTION_VIEW,
Uri.parse("google.navigation:q=" + lat + "," + lng));
However for Google map direction, json parsing is best.
No, there is only the Google Directions API available for a route response, which includes JSON parsing.