SlideShare a Scribd company logo
1 of 22
Download to read offline
Creating an Uber Clone - Part XVII
We finally have a client server application that works but we don't have any "real" functionality. To get to that point we’ll need some webservices…
WebServices from the Device?
✦I took a shortcut and chose to invoke the Google
webservices from the device
✦The reason I did this is that I want to teach mobile
programming not server programming
✦Google API keys would be more secure in the server
✦We can cache common queries in the server and
reduce API costs
✦We can monitor the calls sent out and identify trends
✦We can instantly switch providers without shipping a
new version of the app
© Codename One 2017 all rights reserved
I chose to invoke the Google webservices from the device code instead of using our server to proxy the calls. 

I did this because this is a mobile programing course not a server development course. This makes sense for a simple project and is arguably faster as I connect directly
to Google. However, if I was building a real world application I would have directed all requests to our server and invoked Google from there. This might have a
performance penalty but it's worth it. Here are some advantages:

API keys would be stored on the server and would thus be more secure from theft.

We can cache queries and reduce API costs between users. Currently I can only cache calls per user but if lots of people are searching for the same things this can add
up.

I can use analytics and statistics in the server code and notice patterns about user behavior.

And finally I can switch to a different company for the webservice implementation to get a better deal. If Google discontinues its service I don’t have to ship an app
update either.
Googles GIS Services
✦Geocoding
✦Reverse Geocoding
✦Directions
✦Places Autocomplete
© Codename One 2017 all rights reserved
We’ll use 4 of Googles location based WebServices to implement the functionality of this app. With geocoding we provide an address or location and get back a location
on the map. This is useful when a driver receives a set of locations and needs to know the actual map coordinates.

Reverse Geocoding is the exact opposite. It provides the name of a given location. This is useful for pointing a pin on the map and naming the location.

Directions provides directions, trip time etc. We can get the points of the path and plot them out on the map.

The places API allows searching for a place similar to the geocoding API. The autocomplete version lets us type into a text field and see "suggestions" appear. We’ll use
it for the search functionality.
public static final String GOOGLE_DIRECTIONS_KEY = "----";
public static final String GOOGLE_GEOCODING_KEY = "----";
public static final String GOOGLE_PLACES_KEY = "----";
New Fields in Globals
All of these API's require developer keys which you can obtain from their respective websites. I've edited the Globals class to include these new keys required by the 3
API’s. Make sure to replace the dashes with the relevant keys. You can get the keys by going to the directions, geocoding and places websites and follow the process
there.
https://maps.googleapis.com/maps/api/geocode/json?
latlng=40.714224,-73.961452&key=YOUR_API_KEY
Reverse Geocoding
We use the /maps/api/geocode/json URL for Reverse Geocoding. Google provides this example for usage of the API. It's just a latitude/longitude pair and your API key.
{
"results" : [
{
"address_components" : [
{
"long_name" : "277",
"short_name" : "277",
"types" : [ "street_number" ]
},
{
"long_name" : "Bedford Avenue",
"short_name" : "Bedford Ave",
"types" : [ "route" ]
},
... trimmed ...
],
"formatted_address" : "277 Bedford Avenue, Brooklyn, NY 11211, USA",
"geometry" : {
"location" : {
"lat" : 40.714232,
"lng" : -73.9612889
Reverse Geocoding - Result
The result of that URL look like this response JSON. Lets go over two important pieces…

We need to get this result array from the response, we only care about the first element and will discard the rest.
{
"results" : [
{
"address_components" : [
{
"long_name" : "277",
"short_name" : "277",
"types" : [ "street_number" ]
},
{
"long_name" : "Bedford Avenue",
"short_name" : "Bedford Ave",
"types" : [ "route" ]
},
... trimmed ...
],
"formatted_address" : "277 Bedford Avenue, Brooklyn, NY 11211, USA",
"geometry" : {
"location" : {
"lat" : 40.714232,
"lng" : -73.9612889
Reverse Geocoding - Result
This is the only attribute we need at this time from this API. Now that we know what we are looking for lets look at the code that accomplishes this.
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
I'll use the SearchService class to encapsulate this functionality for each of these services
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
There is an edge case where location isn't ready yet when this method is invoked, in this case I found it best to just do nothing. Usually it's best to fail by throwing an
exception but that is a valid situation to which I have a decent fallback option so I prefer doing nothing
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
If we send two such calls in rapid succession I only need the last one so I'm canceling the previous request
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
The reverse geocode API latlng argument determines the location for which we are looking
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
We get the parsed result as a Map containing a hierarchy of objects the callback is invoked asynchronously when the response arrives
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
This gets the results list from the JSON and extracts the first element from there
public class SearchService {
private static ConnectionRequest lastLocationRequest;
public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) {
if(l == null) {
return;
}
if(lastLocationRequest != null) {
lastLocationRequest.kill();
}
lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("latlng", l.getLatitude() + "," + l.getLongitude()).
queryParam("key", GOOGLE_GEOCODING_KEY).
queryParam("language", "en").
queryParam("result_type", "street_address|point_of_interest").
getAsJsonMap(callbackMap -> {
Map data = callbackMap.getResponseData();
if(data != null) {
List results = (List)data.get("results");
if(results != null && results.size() > 0) {
Map firstResult = (Map)results.get(0);
name.onSucess((String)firstResult.get("formatted_address"));
}
}
});
}
SearchService
We extract the one attribute we care about the formatted_address entry and invoke the callback method with this result
https://maps.googleapis.com/maps/api/place/autocomplete/json?
input=lev&location=32.072449,34.778613&radius=50000&key=API_KEY
Places Autocomplete
The places autocomplete API is a bit more challenging since this API is invoked as a user types. We'll need the ability to cancel a request just as we would with the
geocoding calls. Caching is also crucial in this case, so we must cache as much as possible to avoid overuse of the API and performance issues.

Lets start by reviewing the API URL and responses. The default sample from google wasn't very helpful so I had to read the docs a bit and came up with this URL.

The search is relevant to a specific location and radius. Otherwise it would suggest places from all over the world which probably doesn't make sense for an Uber style
application. Notice the radius is specified in meters.

The input value is the string for which we would like auto-complete suggestions.
{
"predictions" : [
{
"description" : "Levinsky, Tel Aviv-Yafo, Israel",
"id" : "13fd8422602e10c4a7be775c88280b383a15f368",
"matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"structured_formatting" : {
"main_text" : "Levinsky",
"main_text_matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"secondary_text" : "Tel Aviv-Yafo, Israel"
},
"place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs",
"reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv",
Places Autocomplete - Result
This request produces this JSON result.

All predications are again within an array but this time we'll need all of them.
{
"predictions" : [
{
"description" : "Levinsky, Tel Aviv-Yafo, Israel",
"id" : "13fd8422602e10c4a7be775c88280b383a15f368",
"matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"structured_formatting" : {
"main_text" : "Levinsky",
"main_text_matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"secondary_text" : "Tel Aviv-Yafo, Israel"
},
"place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs",
"reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv",
Places Autocomplete - Result
The UI would require the text broken down so we need the main text
{
"predictions" : [
{
"description" : "Levinsky, Tel Aviv-Yafo, Israel",
"id" : "13fd8422602e10c4a7be775c88280b383a15f368",
"matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"structured_formatting" : {
"main_text" : "Levinsky",
"main_text_matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"secondary_text" : "Tel Aviv-Yafo, Israel"
},
"place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs",
"reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv",
Places Autocomplete - Result
And we'll need the secondary text too
{
"predictions" : [
{
"description" : "Levinsky, Tel Aviv-Yafo, Israel",
"id" : "13fd8422602e10c4a7be775c88280b383a15f368",
"matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"structured_formatting" : {
"main_text" : "Levinsky",
"main_text_matched_substrings" : [
{
"length" : 3,
"offset" : 0
}
],
"secondary_text" : "Tel Aviv-Yafo, Israel"
},
"place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs",
"reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv",
Places Autocomplete - Result
We'll also need the place_id and the reason for this is a HUGE omission in this API. Notice it has no location information... We will need the place_id value to query again
for the location
public static class SuggestionResult {
private final String mainText;
private final String secondaryText;
private final String fullText;
private final String placeId;
public SuggestionResult(String mainText, String secondaryText,
String fullText, String placeId) {
this.mainText = mainText;
this.secondaryText = secondaryText;
this.fullText = fullText;
this.placeId = placeId;
}
public String getPlaceId() {
return placeId;
}
public String getMainText() {
return mainText;
}
public String getSecondaryText() {
return secondaryText;
}
public String getFullText() {
return fullText;
}
SuggestionResult
Before we move on to the code we'll need a way to send the results back. We can do that with a list of SuggestionResult entries.

This is a pretty trivial class and doesn't require any explaining.
public String getMainText() {
return mainText;
}
public String getSecondaryText() {
return secondaryText;
}
public String getFullText() {
return fullText;
}
public void getLocation(SuccessCallback<Location> result) {
Rest.get("https://maps.googleapis.com/maps/api/place/details/json").
queryParam("placeid", placeId).
queryParam("key", GOOGLE_PLACES_KEY).
getAsJsonMap(callbackMap -> {
Map r = (Map)callbackMap.getResponseData().get("result");
Map geomMap = (Map)r.get("geometry");
Map locationMap = (Map)geomMap.get("location");
double lat = Util.toDoubleValue(locationMap.get("lat"));
double lon = Util.toDoubleValue(locationMap.get("lng"));
result.onSucess(new Location(lat, lon));
});
}
}
SuggestionResult
This class solves the issue of getting the location for a specific entry with the method getLocation. I won't go too much into the details of that code above since it's very
similar to the code we saw before we just get additional details about a place and parse the results. Notice that this is a part of the SuggestionResult class so we don’t
invoke this unless we actually need the location of a place
private static ConnectionRequest lastSuggestionRequest;
private static String lastSuggestionValue;
private static final Map<String, List<SuggestionResult>>
locationCache = new HashMap<>();
SearchService
There is one last thing we need before we go into the suggestion method itself. We need variables to cache the data and current request. Otherwise multiple incoming
requests might collide and block the network. 

We need the lastSuggestionRequest so we can cancel it.

The lastSuggestionValue lets us distinguish duplicate values this can sometimes happen as an edit event might repeat a request that was already sent for example if a
user types and deletes a character. This can happen since we will wait 500ms before sending characters.

The locationCache reduces duplicate requests. Notice that this can grow to a level of a huge memory leak but realistically that would require a huge number of searches.
If this still bothers you we have the CacheMap class that serializes extra data to storage

More Related Content

Similar to Creating an Uber Clone - Part XVII - Transcript.pdf

Developing Apps for Emerging Markets
Developing Apps for Emerging MarketsDeveloping Apps for Emerging Markets
Developing Apps for Emerging MarketsAnnyce Davis
 
API Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015
API  Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015 API  Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015
API Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015 Hamdi Hmidi
 
How to build an AngularJS backend-ready app WITHOUT BACKEND
How to build an AngularJS backend-ready app WITHOUT BACKEND How to build an AngularJS backend-ready app WITHOUT BACKEND
How to build an AngularJS backend-ready app WITHOUT BACKEND Enrique Oriol Bermúdez
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Eliran Eliassy
 
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineGoogle Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineRoman Kirillov
 
Creating an Uber Clone - Part XXIX.pdf
Creating an Uber Clone - Part XXIX.pdfCreating an Uber Clone - Part XXIX.pdf
Creating an Uber Clone - Part XXIX.pdfShaiAlmog1
 
A gently introduction to AngularJS
A gently introduction to AngularJSA gently introduction to AngularJS
A gently introduction to AngularJSGregor Woiwode
 
Angular.js Primer in Aalto University
Angular.js Primer in Aalto UniversityAngular.js Primer in Aalto University
Angular.js Primer in Aalto UniversitySC5.io
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverSpike Brehm
 
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...Rif Kiamil
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineAndy McKay
 
Server side data sync for mobile apps with silex
Server side data sync for mobile apps with silexServer side data sync for mobile apps with silex
Server side data sync for mobile apps with silexMichele Orselli
 

Similar to Creating an Uber Clone - Part XVII - Transcript.pdf (20)

Developing Apps for Emerging Markets
Developing Apps for Emerging MarketsDeveloping Apps for Emerging Markets
Developing Apps for Emerging Markets
 
API Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015
API  Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015 API  Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015
API Driven Application - AngulatJS, NodeJS and MongoDB | JCertif Tunisia 2015
 
How to build an AngularJS backend-ready app WITHOUT BACKEND
How to build an AngularJS backend-ready app WITHOUT BACKEND How to build an AngularJS backend-ready app WITHOUT BACKEND
How to build an AngularJS backend-ready app WITHOUT BACKEND
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngineGoogle Cloud Endpoints: Building Third-Party APIs on Google AppEngine
Google Cloud Endpoints: Building Third-Party APIs on Google AppEngine
 
Creating an Uber Clone - Part XXIX.pdf
Creating an Uber Clone - Part XXIX.pdfCreating an Uber Clone - Part XXIX.pdf
Creating an Uber Clone - Part XXIX.pdf
 
CARTO ENGINE
CARTO ENGINECARTO ENGINE
CARTO ENGINE
 
Google Maps Api
Google Maps ApiGoogle Maps Api
Google Maps Api
 
A gently introduction to AngularJS
A gently introduction to AngularJSA gently introduction to AngularJS
A gently introduction to AngularJS
 
MAD Unit 6.pptx
MAD Unit 6.pptxMAD Unit 6.pptx
MAD Unit 6.pptx
 
Angular.js Primer in Aalto University
Angular.js Primer in Aalto UniversityAngular.js Primer in Aalto University
Angular.js Primer in Aalto University
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...
Interview with Developer Jose Luis Arenas regarding Google App Engine & Geosp...
 
Cross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App EngineCross Domain Web
Mashups with JQuery and Google App Engine
Cross Domain Web
Mashups with JQuery and Google App Engine
 
DIY Uber
DIY UberDIY Uber
DIY Uber
 
mobl
moblmobl
mobl
 
Intro To Google Maps
Intro To Google MapsIntro To Google Maps
Intro To Google Maps
 
huhu
huhuhuhu
huhu
 
Server side data sync for mobile apps with silex
Server side data sync for mobile apps with silexServer side data sync for mobile apps with silex
Server side data sync for mobile apps with silex
 
Html5 geolocation api
Html5 geolocation apiHtml5 geolocation api
Html5 geolocation api
 

More from ShaiAlmog1

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...ShaiAlmog1
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfShaiAlmog1
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfShaiAlmog1
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfShaiAlmog1
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfShaiAlmog1
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfShaiAlmog1
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfShaiAlmog1
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfShaiAlmog1
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfShaiAlmog1
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfShaiAlmog1
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfShaiAlmog1
 

More from ShaiAlmog1 (20)

The Duck Teaches Learn to debug from the masters. Local to production- kill ...
The Duck Teaches  Learn to debug from the masters. Local to production- kill ...The Duck Teaches  Learn to debug from the masters. Local to production- kill ...
The Duck Teaches Learn to debug from the masters. Local to production- kill ...
 
create-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdfcreate-netflix-clone-06-client-ui.pdf
create-netflix-clone-06-client-ui.pdf
 
create-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdfcreate-netflix-clone-01-introduction_transcript.pdf
create-netflix-clone-01-introduction_transcript.pdf
 
create-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdfcreate-netflix-clone-02-server_transcript.pdf
create-netflix-clone-02-server_transcript.pdf
 
create-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdfcreate-netflix-clone-04-server-continued_transcript.pdf
create-netflix-clone-04-server-continued_transcript.pdf
 
create-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdfcreate-netflix-clone-01-introduction.pdf
create-netflix-clone-01-introduction.pdf
 
create-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdfcreate-netflix-clone-06-client-ui_transcript.pdf
create-netflix-clone-06-client-ui_transcript.pdf
 
create-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdfcreate-netflix-clone-03-server.pdf
create-netflix-clone-03-server.pdf
 
create-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdfcreate-netflix-clone-04-server-continued.pdf
create-netflix-clone-04-server-continued.pdf
 
create-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdfcreate-netflix-clone-05-client-model_transcript.pdf
create-netflix-clone-05-client-model_transcript.pdf
 
create-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdfcreate-netflix-clone-03-server_transcript.pdf
create-netflix-clone-03-server_transcript.pdf
 
create-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdfcreate-netflix-clone-02-server.pdf
create-netflix-clone-02-server.pdf
 
create-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdfcreate-netflix-clone-05-client-model.pdf
create-netflix-clone-05-client-model.pdf
 
Creating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdfCreating a Whatsapp Clone - Part II.pdf
Creating a Whatsapp Clone - Part II.pdf
 
Creating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdfCreating a Whatsapp Clone - Part IX - Transcript.pdf
Creating a Whatsapp Clone - Part IX - Transcript.pdf
 
Creating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdfCreating a Whatsapp Clone - Part II - Transcript.pdf
Creating a Whatsapp Clone - Part II - Transcript.pdf
 
Creating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdfCreating a Whatsapp Clone - Part V - Transcript.pdf
Creating a Whatsapp Clone - Part V - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdfCreating a Whatsapp Clone - Part IV - Transcript.pdf
Creating a Whatsapp Clone - Part IV - Transcript.pdf
 
Creating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdfCreating a Whatsapp Clone - Part IV.pdf
Creating a Whatsapp Clone - Part IV.pdf
 
Creating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdfCreating a Whatsapp Clone - Part I - Transcript.pdf
Creating a Whatsapp Clone - Part I - Transcript.pdf
 

Recently uploaded

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountPuma Security, LLC
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024Scott Keck-Warren
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Hyundai Motor Group
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking MenDelhi Call girls
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 

Recently uploaded (20)

Breaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path MountBreaking the Kubernetes Kill Chain: Host Path Mount
Breaking the Kubernetes Kill Chain: Host Path Mount
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
The transition to renewables in India.pdf
The transition to renewables in India.pdfThe transition to renewables in India.pdf
The transition to renewables in India.pdf
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024SQL Database Design For Developers at php[tek] 2024
SQL Database Design For Developers at php[tek] 2024
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2Next-generation AAM aircraft unveiled by Supernal, S-A2
Next-generation AAM aircraft unveiled by Supernal, S-A2
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men08448380779 Call Girls In Greater Kailash - I Women Seeking Men
08448380779 Call Girls In Greater Kailash - I Women Seeking Men
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 

Creating an Uber Clone - Part XVII - Transcript.pdf

  • 1. Creating an Uber Clone - Part XVII We finally have a client server application that works but we don't have any "real" functionality. To get to that point we’ll need some webservices…
  • 2. WebServices from the Device? ✦I took a shortcut and chose to invoke the Google webservices from the device ✦The reason I did this is that I want to teach mobile programming not server programming ✦Google API keys would be more secure in the server ✦We can cache common queries in the server and reduce API costs ✦We can monitor the calls sent out and identify trends ✦We can instantly switch providers without shipping a new version of the app © Codename One 2017 all rights reserved I chose to invoke the Google webservices from the device code instead of using our server to proxy the calls. I did this because this is a mobile programing course not a server development course. This makes sense for a simple project and is arguably faster as I connect directly to Google. However, if I was building a real world application I would have directed all requests to our server and invoked Google from there. This might have a performance penalty but it's worth it. Here are some advantages: API keys would be stored on the server and would thus be more secure from theft. We can cache queries and reduce API costs between users. Currently I can only cache calls per user but if lots of people are searching for the same things this can add up. I can use analytics and statistics in the server code and notice patterns about user behavior. And finally I can switch to a different company for the webservice implementation to get a better deal. If Google discontinues its service I don’t have to ship an app update either.
  • 3. Googles GIS Services ✦Geocoding ✦Reverse Geocoding ✦Directions ✦Places Autocomplete © Codename One 2017 all rights reserved We’ll use 4 of Googles location based WebServices to implement the functionality of this app. With geocoding we provide an address or location and get back a location on the map. This is useful when a driver receives a set of locations and needs to know the actual map coordinates. Reverse Geocoding is the exact opposite. It provides the name of a given location. This is useful for pointing a pin on the map and naming the location. Directions provides directions, trip time etc. We can get the points of the path and plot them out on the map. The places API allows searching for a place similar to the geocoding API. The autocomplete version lets us type into a text field and see "suggestions" appear. We’ll use it for the search functionality.
  • 4. public static final String GOOGLE_DIRECTIONS_KEY = "----"; public static final String GOOGLE_GEOCODING_KEY = "----"; public static final String GOOGLE_PLACES_KEY = "----"; New Fields in Globals All of these API's require developer keys which you can obtain from their respective websites. I've edited the Globals class to include these new keys required by the 3 API’s. Make sure to replace the dashes with the relevant keys. You can get the keys by going to the directions, geocoding and places websites and follow the process there.
  • 5. https://maps.googleapis.com/maps/api/geocode/json? latlng=40.714224,-73.961452&key=YOUR_API_KEY Reverse Geocoding We use the /maps/api/geocode/json URL for Reverse Geocoding. Google provides this example for usage of the API. It's just a latitude/longitude pair and your API key.
  • 6. { "results" : [ { "address_components" : [ { "long_name" : "277", "short_name" : "277", "types" : [ "street_number" ] }, { "long_name" : "Bedford Avenue", "short_name" : "Bedford Ave", "types" : [ "route" ] }, ... trimmed ... ], "formatted_address" : "277 Bedford Avenue, Brooklyn, NY 11211, USA", "geometry" : { "location" : { "lat" : 40.714232, "lng" : -73.9612889 Reverse Geocoding - Result The result of that URL look like this response JSON. Lets go over two important pieces… We need to get this result array from the response, we only care about the first element and will discard the rest.
  • 7. { "results" : [ { "address_components" : [ { "long_name" : "277", "short_name" : "277", "types" : [ "street_number" ] }, { "long_name" : "Bedford Avenue", "short_name" : "Bedford Ave", "types" : [ "route" ] }, ... trimmed ... ], "formatted_address" : "277 Bedford Avenue, Brooklyn, NY 11211, USA", "geometry" : { "location" : { "lat" : 40.714232, "lng" : -73.9612889 Reverse Geocoding - Result This is the only attribute we need at this time from this API. Now that we know what we are looking for lets look at the code that accomplishes this.
  • 8. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService I'll use the SearchService class to encapsulate this functionality for each of these services
  • 9. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService There is an edge case where location isn't ready yet when this method is invoked, in this case I found it best to just do nothing. Usually it's best to fail by throwing an exception but that is a valid situation to which I have a decent fallback option so I prefer doing nothing
  • 10. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService If we send two such calls in rapid succession I only need the last one so I'm canceling the previous request
  • 11. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService The reverse geocode API latlng argument determines the location for which we are looking
  • 12. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService We get the parsed result as a Map containing a hierarchy of objects the callback is invoked asynchronously when the response arrives
  • 13. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService This gets the results list from the JSON and extracts the first element from there
  • 14. public class SearchService { private static ConnectionRequest lastLocationRequest; public static void nameMyCurrentLocation(Location l, SuccessCallback<String> name) { if(l == null) { return; } if(lastLocationRequest != null) { lastLocationRequest.kill(); } lastLocationRequest = Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("latlng", l.getLatitude() + "," + l.getLongitude()). queryParam("key", GOOGLE_GEOCODING_KEY). queryParam("language", "en"). queryParam("result_type", "street_address|point_of_interest"). getAsJsonMap(callbackMap -> { Map data = callbackMap.getResponseData(); if(data != null) { List results = (List)data.get("results"); if(results != null && results.size() > 0) { Map firstResult = (Map)results.get(0); name.onSucess((String)firstResult.get("formatted_address")); } } }); } SearchService We extract the one attribute we care about the formatted_address entry and invoke the callback method with this result
  • 15. https://maps.googleapis.com/maps/api/place/autocomplete/json? input=lev&location=32.072449,34.778613&radius=50000&key=API_KEY Places Autocomplete The places autocomplete API is a bit more challenging since this API is invoked as a user types. We'll need the ability to cancel a request just as we would with the geocoding calls. Caching is also crucial in this case, so we must cache as much as possible to avoid overuse of the API and performance issues. Lets start by reviewing the API URL and responses. The default sample from google wasn't very helpful so I had to read the docs a bit and came up with this URL. The search is relevant to a specific location and radius. Otherwise it would suggest places from all over the world which probably doesn't make sense for an Uber style application. Notice the radius is specified in meters. The input value is the string for which we would like auto-complete suggestions.
  • 16. { "predictions" : [ { "description" : "Levinsky, Tel Aviv-Yafo, Israel", "id" : "13fd8422602e10c4a7be775c88280b383a15f368", "matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "structured_formatting" : { "main_text" : "Levinsky", "main_text_matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "secondary_text" : "Tel Aviv-Yafo, Israel" }, "place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs", "reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv", Places Autocomplete - Result This request produces this JSON result. All predications are again within an array but this time we'll need all of them.
  • 17. { "predictions" : [ { "description" : "Levinsky, Tel Aviv-Yafo, Israel", "id" : "13fd8422602e10c4a7be775c88280b383a15f368", "matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "structured_formatting" : { "main_text" : "Levinsky", "main_text_matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "secondary_text" : "Tel Aviv-Yafo, Israel" }, "place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs", "reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv", Places Autocomplete - Result The UI would require the text broken down so we need the main text
  • 18. { "predictions" : [ { "description" : "Levinsky, Tel Aviv-Yafo, Israel", "id" : "13fd8422602e10c4a7be775c88280b383a15f368", "matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "structured_formatting" : { "main_text" : "Levinsky", "main_text_matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "secondary_text" : "Tel Aviv-Yafo, Israel" }, "place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs", "reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv", Places Autocomplete - Result And we'll need the secondary text too
  • 19. { "predictions" : [ { "description" : "Levinsky, Tel Aviv-Yafo, Israel", "id" : "13fd8422602e10c4a7be775c88280b383a15f368", "matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "structured_formatting" : { "main_text" : "Levinsky", "main_text_matched_substrings" : [ { "length" : 3, "offset" : 0 } ], "secondary_text" : "Tel Aviv-Yafo, Israel" }, "place_id" : "Eh9MZXZpbnNreSwgVGVsIEF2aXYtWWFmbywgSXNyYWVs", "reference" : "-pQdrYHivUaFGV9GwZkfwp4xkjUL2Z2mpGNXJBv", Places Autocomplete - Result We'll also need the place_id and the reason for this is a HUGE omission in this API. Notice it has no location information... We will need the place_id value to query again for the location
  • 20. public static class SuggestionResult { private final String mainText; private final String secondaryText; private final String fullText; private final String placeId; public SuggestionResult(String mainText, String secondaryText, String fullText, String placeId) { this.mainText = mainText; this.secondaryText = secondaryText; this.fullText = fullText; this.placeId = placeId; } public String getPlaceId() { return placeId; } public String getMainText() { return mainText; } public String getSecondaryText() { return secondaryText; } public String getFullText() { return fullText; } SuggestionResult Before we move on to the code we'll need a way to send the results back. We can do that with a list of SuggestionResult entries. This is a pretty trivial class and doesn't require any explaining.
  • 21. public String getMainText() { return mainText; } public String getSecondaryText() { return secondaryText; } public String getFullText() { return fullText; } public void getLocation(SuccessCallback<Location> result) { Rest.get("https://maps.googleapis.com/maps/api/place/details/json"). queryParam("placeid", placeId). queryParam("key", GOOGLE_PLACES_KEY). getAsJsonMap(callbackMap -> { Map r = (Map)callbackMap.getResponseData().get("result"); Map geomMap = (Map)r.get("geometry"); Map locationMap = (Map)geomMap.get("location"); double lat = Util.toDoubleValue(locationMap.get("lat")); double lon = Util.toDoubleValue(locationMap.get("lng")); result.onSucess(new Location(lat, lon)); }); } } SuggestionResult This class solves the issue of getting the location for a specific entry with the method getLocation. I won't go too much into the details of that code above since it's very similar to the code we saw before we just get additional details about a place and parse the results. Notice that this is a part of the SuggestionResult class so we don’t invoke this unless we actually need the location of a place
  • 22. private static ConnectionRequest lastSuggestionRequest; private static String lastSuggestionValue; private static final Map<String, List<SuggestionResult>> locationCache = new HashMap<>(); SearchService There is one last thing we need before we go into the suggestion method itself. We need variables to cache the data and current request. Otherwise multiple incoming requests might collide and block the network. We need the lastSuggestionRequest so we can cancel it. The lastSuggestionValue lets us distinguish duplicate values this can sometimes happen as an edit event might repeat a request that was already sent for example if a user types and deletes a character. This can happen since we will wait 500ms before sending characters. The locationCache reduces duplicate requests. Notice that this can grow to a level of a huge memory leak but realistically that would require a huge number of searches. If this still bothers you we have the CacheMap class that serializes extra data to storage