SlideShare a Scribd company logo
1 of 12
Download to read offline
Creating an Uber Clone - Part XXIX
Before we go into the big UI changes lets go over some of the networking level changes in the code
public class Ride implements PropertyBusinessObject {
public final LongProperty<Ride> userId = new LongProperty<>("userId");
public final Property<String, Ride> name = new Property<>("name");
public final Property<String, Ride> from = new Property<>("from");
public final Property<String, Ride> destination = new Property<>("destination");
private final PropertyIndex idx = new PropertyIndex(this, "Ride",
userId, name, from, destination);
@Override
public PropertyIndex getPropertyIndex() {
return idx;
}
}
Ride (Client Side)
In order to encapsulate the new Ride JSON object from the server I added an equivalent properties object locally. The class is practically identical to the server side
RideDAO class but uses the properties syntax instead of the standard POJO syntax.
public class DriverService {
private static String currentRide;
public static void fetchRideDetails(long id,
SuccessCallback<Ride> rideDetails) {
Rest.get(SERVER_URL + "ride/get").
acceptJson().queryParam("id", "" + id).
getAsJsonMap(response -> {
Map data = response.getResponseData();
if(data != null) {
Ride r = new Ride();
r.getPropertyIndex().populateFromMap(data);
rideDetails.onSucess(r);
}
});
}
public static boolean acceptRide(long id) {
Response<String> response =
Rest.get(SERVER_URL + "ride/accept").
acceptJson().
queryParam("token", UserService.getToken()).
queryParam("userId", "" + id).
getAsString();
if(response.getResponseCode() == 200) {
currentRide = response.getResponseData();
return true;
}
return false;
DriverService
The driver service class is a static representation of the driver specific server API’s.

This field represents the id of the current ride from this driver
public class DriverService {
private static String currentRide;
public static void fetchRideDetails(long id,
SuccessCallback<Ride> rideDetails) {
Rest.get(SERVER_URL + "ride/get").
acceptJson().queryParam("id", "" + id).
getAsJsonMap(response -> {
Map data = response.getResponseData();
if(data != null) {
Ride r = new Ride();
r.getPropertyIndex().populateFromMap(data);
rideDetails.onSucess(r);
}
});
}
public static boolean acceptRide(long id) {
Response<String> response =
Rest.get(SERVER_URL + "ride/accept").
acceptJson().
queryParam("token", UserService.getToken()).
queryParam("userId", "" + id).
getAsString();
if(response.getResponseCode() == 200) {
currentRide = response.getResponseData();
return true;
}
return false;
DriverService
Here we have the standard get method I mentioned earlier to retrieve the ride details and return them via a callback
public class DriverService {
private static String currentRide;
public static void fetchRideDetails(long id,
SuccessCallback<Ride> rideDetails) {
Rest.get(SERVER_URL + "ride/get").
acceptJson().queryParam("id", "" + id).
getAsJsonMap(response -> {
Map data = response.getResponseData();
if(data != null) {
Ride r = new Ride();
r.getPropertyIndex().populateFromMap(data);
rideDetails.onSucess(r);
}
});
}
public static boolean acceptRide(long id) {
Response<String> response =
Rest.get(SERVER_URL + "ride/accept").
acceptJson().
queryParam("token", UserService.getToken()).
queryParam("userId", "" + id).
getAsString();
if(response.getResponseCode() == 200) {
currentRide = response.getResponseData();
return true;
}
return false;
DriverService
We use accept to indicate a driver is accepting a users hailing, if he doesn't we don't care... Once accepted he gets a reference to the id of the newly created Ride object
in the server. Notice that failure is indeed a possibility. For example if the user canceled the ride or a different driver accepted first
}
public static boolean acceptRide(long id) {
Response<String> response =
Rest.get(SERVER_URL + "ride/accept").
acceptJson().
queryParam("token", UserService.getToken()).
queryParam("userId", "" + id).
getAsString();
if(response.getResponseCode() == 200) {
currentRide = response.getResponseData();
return true;
}
return false;
}
public static void startRide() {
Rest.post(SERVER_URL + "ride/start").
acceptJson().
queryParam("id", currentRide).
getAsString();
}
public static void finishRide() {
Rest.post(SERVER_URL + "ride/finish").
acceptJson().
queryParam("id", currentRide).
getAsString();
}
}
DriverService
When we invoke startRide and finishRide we use the currentRide id unlike the userId which we used to create the ride
public static void findLocation(String name,
SuccessCallback<Coord> location) {
Rest.get("https://maps.googleapis.com/maps/api/geocode/json").
queryParam("address", name).
queryParam("key", Globals.GOOGLE_GEOCODING_KEY).
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);
Map geometryMap = (Map)firstResult.get("geometry");
Map locationMap = (Map)geometryMap.get("location");
double lat = Util.toDoubleValue(locationMap.get("lat"));
double lon = Util.toDoubleValue(locationMap.get("lng"));
location.onSucess(new Coord(lat, lon));
}
}
});
}
SearchService
In SearchService we had to add support for geocoding. Before this we only had the reverse geocoding which we use to locate the from/to points on the map.

We need this API since the driver only gets the to/from location names and we want to plot them on the map.

There isn't all that much to say about this method. It just searches the Google geocode API for a location with the given name and returns the coordinate of that location.
public class UserService {
private static User me;
public static User getUser() {
return me;
}
public static String getToken() {
if(UberClone.isDriverMode()) {
return Preferences.get("driver-token", null);
} else {
return Preferences.get("token", null);
}
}
public static void loadUser() {
me = new User();
if(UberClone.isDriverMode()) {
PreferencesObject.create(me).setPrefix("driver").bind();
} else {
PreferencesObject.create(me).bind();
}
if(Display.getInstance().isSimulator()) {
Log.p("User details: " + me.getPropertyIndex().toString());
}
}
UserService
There were many small changes in the UserService class most of them relate to the way identity is managed in the app.

One of the big problems in having two applications with one project is that both projects share the same data in the simulator. So if I want to launch the project twice,
once to run the user version and once for the driver version. I will have a problem. Both will inspect the same storage information and use the same user identity. They
might collide...

Notice that this is purely a simulator problem. The simulator doesn't currently isolate separate applications. Ideally this is something to improve in the simulator and might
not be an issue in the future. The solution is simple though. We can just save the data to different "locations" or "keys" if we are in the driver app. Lets review the
changes…

This is illustrated perfectly in the first change in this class. We use a different token to determine if the user is logged in for the case of a driver. Notice we replaced the
invocations of Preferences.get("token", null) that were all over the code with this method call
public class UserService {
private static User me;
public static User getUser() {
return me;
}
public static String getToken() {
if(UberClone.isDriverMode()) {
return Preferences.get("driver-token", null);
} else {
return Preferences.get("token", null);
}
}
public static void loadUser() {
me = new User();
if(UberClone.isDriverMode()) {
PreferencesObject.create(me).setPrefix("driver").bind();
} else {
PreferencesObject.create(me).bind();
}
if(Display.getInstance().isSimulator()) {
Log.p("User details: " + me.getPropertyIndex().toString());
}
}
UserService
The Preferences bind API lets us set a different prefix for the driver object that will be prepended to the properties in the Preferences
public class UserService {
private static User me;
public static User getUser() {
return me;
}
public static String getToken() {
if(UberClone.isDriverMode()) {
return Preferences.get("driver-token", null);
} else {
return Preferences.get("token", null);
}
}
public static void loadUser() {
me = new User();
if(UberClone.isDriverMode()) {
PreferencesObject.create(me).setPrefix("driver").bind();
} else {
PreferencesObject.create(me).bind();
}
if(Display.getInstance().isSimulator()) {
Log.p("User details: " + me.getPropertyIndex().toString());
}
}
UserService
This is a cool little trick that allows me to debug with a fake number, I used the isSimulator method to log the verification code on the simulator and can just type it in
even if the twilio code failed to send a message
public static void registerPushToken(String pushToken) {
Rest.get(SERVER_URL + "user/setPushToken").
queryParam("token", getToken()).
queryParam("pushToken", pushToken).getAsStringAsync(
new Callback<Response<String>>() {
@Override
public void onSucess(Response<String> value) {
}
@Override
public void onError(Object sender, Throwable err, int errorCode,
String errorMessage) {
}
});
}
public static boolean addNewUser(User u) {
Response<String> token = Rest.post(SERVER_URL + "user/add").
jsonContent().
body(u.getPropertyIndex().toJSON()).getAsString();
if(token.getResponseCode() != 200) {
return false;
}
UserService
Sends the push token to the server. Right now we don't need to do anything in the event callback. This is invoked on registration success and allows the server to send
driver push keys to the client
public static boolean addNewUser(User u) {
Response<String> token = Rest.post(SERVER_URL + "user/add").
jsonContent().
body(u.getPropertyIndex().toJSON()).getAsString();
if(token.getResponseCode() != 200) {
return false;
}
if(UberClone.isDriverMode()) {
Preferences.set("driver-token", token.getResponseData());
registerPush();
} else {
Preferences.set("token", token.getResponseData());
}
return true;
}
public static void loginWithPhone(String phoneNumber,
String password, final SuccessCallback<User> onSuccess,
final FailureCallback<Object> onError) {
Rest.get(SERVER_URL + "user/login").
acceptJson().
queryParam("password", password).
queryParam("phone", phoneNumber).
UserService
After the first activation of the driver app we need to register for push. Notice I'm using the version of this method from the CN class with static import but the callback
will go as expected into the DriverApp class

More Related Content

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

assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docxassignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
ssuser562afc1
 
Spca2014 hillier build your_own_rest_service
Spca2014 hillier build your_own_rest_serviceSpca2014 hillier build your_own_rest_service
Spca2014 hillier build your_own_rest_service
NCCOMMS
 
Build Lightweight Web Module
Build Lightweight Web ModuleBuild Lightweight Web Module
Build Lightweight Web Module
Morgan Cheng
 

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

Creating an Uber Clone - Part XV - Transcript.pdf
Creating an Uber Clone - Part XV - Transcript.pdfCreating an Uber Clone - Part XV - Transcript.pdf
Creating an Uber Clone - Part XV - Transcript.pdf
 
Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2Angular Workshop_Sarajevo2
Angular Workshop_Sarajevo2
 
Creating a Facebook Clone - Part XXVII - Transcript.pdf
Creating a Facebook Clone - Part XXVII - Transcript.pdfCreating a Facebook Clone - Part XXVII - Transcript.pdf
Creating a Facebook Clone - Part XXVII - Transcript.pdf
 
assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docxassignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
assignmentTwoCar.javaassignmentTwoCar.javapackage assignmentTw.docx
 
Codemotion appengine
Codemotion appengineCodemotion appengine
Codemotion appengine
 
Creating an Uber Clone - Part XXX - Transcript.pdf
Creating an Uber Clone - Part XXX - Transcript.pdfCreating an Uber Clone - Part XXX - Transcript.pdf
Creating an Uber Clone - Part XXX - Transcript.pdf
 
Spca2014 hillier build your_own_rest_service
Spca2014 hillier build your_own_rest_serviceSpca2014 hillier build your_own_rest_service
Spca2014 hillier build your_own_rest_service
 
Creating an Uber Clone - Part XXXI.pdf
Creating an Uber Clone - Part XXXI.pdfCreating an Uber Clone - Part XXXI.pdf
Creating an Uber Clone - Part XXXI.pdf
 
Creating a Facebook Clone - Part XXVII.pdf
Creating a Facebook Clone - Part XXVII.pdfCreating a Facebook Clone - Part XXVII.pdf
Creating a Facebook Clone - Part XXVII.pdf
 
Server Side Swift with Swag
Server Side Swift with SwagServer Side Swift with Swag
Server Side Swift with Swag
 
Android workshop
Android workshopAndroid workshop
Android workshop
 
Build Lightweight Web Module
Build Lightweight Web ModuleBuild Lightweight Web Module
Build Lightweight Web Module
 
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
“iOS 11 в App in the Air”, Пронин Сергей, App in the Air
 
ES2015 Modules
ES2015 ModulesES2015 Modules
ES2015 Modules
 
Clean Javascript
Clean JavascriptClean Javascript
Clean Javascript
 
Developing Apps for Emerging Markets
Developing Apps for Emerging MarketsDeveloping Apps for Emerging Markets
Developing Apps for Emerging Markets
 
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
 
Creating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdfCreating a Facebook Clone - Part XXV - Transcript.pdf
Creating a Facebook Clone - Part XXV - Transcript.pdf
 
React Router: React Meetup XXL
React Router: React Meetup XXLReact Router: React Meetup XXL
React Router: React Meetup XXL
 
Creating an Uber Clone - Part XXXIV.pdf
Creating an Uber Clone - Part XXXIV.pdfCreating an Uber Clone - Part XXXIV.pdf
Creating an Uber Clone - Part XXXIV.pdf
 

More from ShaiAlmog1

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

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 

Recently uploaded (20)

Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
A Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source MilvusA Beginners Guide to Building a RAG App Using Open Source Milvus
A Beginners Guide to Building a RAG App Using Open Source Milvus
 
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost SavingRepurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
Repurposing LNG terminals for Hydrogen Ammonia: Feasibility and Cost Saving
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 

Creating an Uber Clone - Part XXIX - Transcript.pdf

  • 1. Creating an Uber Clone - Part XXIX Before we go into the big UI changes lets go over some of the networking level changes in the code
  • 2. public class Ride implements PropertyBusinessObject { public final LongProperty<Ride> userId = new LongProperty<>("userId"); public final Property<String, Ride> name = new Property<>("name"); public final Property<String, Ride> from = new Property<>("from"); public final Property<String, Ride> destination = new Property<>("destination"); private final PropertyIndex idx = new PropertyIndex(this, "Ride", userId, name, from, destination); @Override public PropertyIndex getPropertyIndex() { return idx; } } Ride (Client Side) In order to encapsulate the new Ride JSON object from the server I added an equivalent properties object locally. The class is practically identical to the server side RideDAO class but uses the properties syntax instead of the standard POJO syntax.
  • 3. public class DriverService { private static String currentRide; public static void fetchRideDetails(long id, SuccessCallback<Ride> rideDetails) { Rest.get(SERVER_URL + "ride/get"). acceptJson().queryParam("id", "" + id). getAsJsonMap(response -> { Map data = response.getResponseData(); if(data != null) { Ride r = new Ride(); r.getPropertyIndex().populateFromMap(data); rideDetails.onSucess(r); } }); } public static boolean acceptRide(long id) { Response<String> response = Rest.get(SERVER_URL + "ride/accept"). acceptJson(). queryParam("token", UserService.getToken()). queryParam("userId", "" + id). getAsString(); if(response.getResponseCode() == 200) { currentRide = response.getResponseData(); return true; } return false; DriverService The driver service class is a static representation of the driver specific server API’s. This field represents the id of the current ride from this driver
  • 4. public class DriverService { private static String currentRide; public static void fetchRideDetails(long id, SuccessCallback<Ride> rideDetails) { Rest.get(SERVER_URL + "ride/get"). acceptJson().queryParam("id", "" + id). getAsJsonMap(response -> { Map data = response.getResponseData(); if(data != null) { Ride r = new Ride(); r.getPropertyIndex().populateFromMap(data); rideDetails.onSucess(r); } }); } public static boolean acceptRide(long id) { Response<String> response = Rest.get(SERVER_URL + "ride/accept"). acceptJson(). queryParam("token", UserService.getToken()). queryParam("userId", "" + id). getAsString(); if(response.getResponseCode() == 200) { currentRide = response.getResponseData(); return true; } return false; DriverService Here we have the standard get method I mentioned earlier to retrieve the ride details and return them via a callback
  • 5. public class DriverService { private static String currentRide; public static void fetchRideDetails(long id, SuccessCallback<Ride> rideDetails) { Rest.get(SERVER_URL + "ride/get"). acceptJson().queryParam("id", "" + id). getAsJsonMap(response -> { Map data = response.getResponseData(); if(data != null) { Ride r = new Ride(); r.getPropertyIndex().populateFromMap(data); rideDetails.onSucess(r); } }); } public static boolean acceptRide(long id) { Response<String> response = Rest.get(SERVER_URL + "ride/accept"). acceptJson(). queryParam("token", UserService.getToken()). queryParam("userId", "" + id). getAsString(); if(response.getResponseCode() == 200) { currentRide = response.getResponseData(); return true; } return false; DriverService We use accept to indicate a driver is accepting a users hailing, if he doesn't we don't care... Once accepted he gets a reference to the id of the newly created Ride object in the server. Notice that failure is indeed a possibility. For example if the user canceled the ride or a different driver accepted first
  • 6. } public static boolean acceptRide(long id) { Response<String> response = Rest.get(SERVER_URL + "ride/accept"). acceptJson(). queryParam("token", UserService.getToken()). queryParam("userId", "" + id). getAsString(); if(response.getResponseCode() == 200) { currentRide = response.getResponseData(); return true; } return false; } public static void startRide() { Rest.post(SERVER_URL + "ride/start"). acceptJson(). queryParam("id", currentRide). getAsString(); } public static void finishRide() { Rest.post(SERVER_URL + "ride/finish"). acceptJson(). queryParam("id", currentRide). getAsString(); } } DriverService When we invoke startRide and finishRide we use the currentRide id unlike the userId which we used to create the ride
  • 7. public static void findLocation(String name, SuccessCallback<Coord> location) { Rest.get("https://maps.googleapis.com/maps/api/geocode/json"). queryParam("address", name). queryParam("key", Globals.GOOGLE_GEOCODING_KEY). 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); Map geometryMap = (Map)firstResult.get("geometry"); Map locationMap = (Map)geometryMap.get("location"); double lat = Util.toDoubleValue(locationMap.get("lat")); double lon = Util.toDoubleValue(locationMap.get("lng")); location.onSucess(new Coord(lat, lon)); } } }); } SearchService In SearchService we had to add support for geocoding. Before this we only had the reverse geocoding which we use to locate the from/to points on the map. We need this API since the driver only gets the to/from location names and we want to plot them on the map. There isn't all that much to say about this method. It just searches the Google geocode API for a location with the given name and returns the coordinate of that location.
  • 8. public class UserService { private static User me; public static User getUser() { return me; } public static String getToken() { if(UberClone.isDriverMode()) { return Preferences.get("driver-token", null); } else { return Preferences.get("token", null); } } public static void loadUser() { me = new User(); if(UberClone.isDriverMode()) { PreferencesObject.create(me).setPrefix("driver").bind(); } else { PreferencesObject.create(me).bind(); } if(Display.getInstance().isSimulator()) { Log.p("User details: " + me.getPropertyIndex().toString()); } } UserService There were many small changes in the UserService class most of them relate to the way identity is managed in the app. One of the big problems in having two applications with one project is that both projects share the same data in the simulator. So if I want to launch the project twice, once to run the user version and once for the driver version. I will have a problem. Both will inspect the same storage information and use the same user identity. They might collide... Notice that this is purely a simulator problem. The simulator doesn't currently isolate separate applications. Ideally this is something to improve in the simulator and might not be an issue in the future. The solution is simple though. We can just save the data to different "locations" or "keys" if we are in the driver app. Lets review the changes… This is illustrated perfectly in the first change in this class. We use a different token to determine if the user is logged in for the case of a driver. Notice we replaced the invocations of Preferences.get("token", null) that were all over the code with this method call
  • 9. public class UserService { private static User me; public static User getUser() { return me; } public static String getToken() { if(UberClone.isDriverMode()) { return Preferences.get("driver-token", null); } else { return Preferences.get("token", null); } } public static void loadUser() { me = new User(); if(UberClone.isDriverMode()) { PreferencesObject.create(me).setPrefix("driver").bind(); } else { PreferencesObject.create(me).bind(); } if(Display.getInstance().isSimulator()) { Log.p("User details: " + me.getPropertyIndex().toString()); } } UserService The Preferences bind API lets us set a different prefix for the driver object that will be prepended to the properties in the Preferences
  • 10. public class UserService { private static User me; public static User getUser() { return me; } public static String getToken() { if(UberClone.isDriverMode()) { return Preferences.get("driver-token", null); } else { return Preferences.get("token", null); } } public static void loadUser() { me = new User(); if(UberClone.isDriverMode()) { PreferencesObject.create(me).setPrefix("driver").bind(); } else { PreferencesObject.create(me).bind(); } if(Display.getInstance().isSimulator()) { Log.p("User details: " + me.getPropertyIndex().toString()); } } UserService This is a cool little trick that allows me to debug with a fake number, I used the isSimulator method to log the verification code on the simulator and can just type it in even if the twilio code failed to send a message
  • 11. public static void registerPushToken(String pushToken) { Rest.get(SERVER_URL + "user/setPushToken"). queryParam("token", getToken()). queryParam("pushToken", pushToken).getAsStringAsync( new Callback<Response<String>>() { @Override public void onSucess(Response<String> value) { } @Override public void onError(Object sender, Throwable err, int errorCode, String errorMessage) { } }); } public static boolean addNewUser(User u) { Response<String> token = Rest.post(SERVER_URL + "user/add"). jsonContent(). body(u.getPropertyIndex().toJSON()).getAsString(); if(token.getResponseCode() != 200) { return false; } UserService Sends the push token to the server. Right now we don't need to do anything in the event callback. This is invoked on registration success and allows the server to send driver push keys to the client
  • 12. public static boolean addNewUser(User u) { Response<String> token = Rest.post(SERVER_URL + "user/add"). jsonContent(). body(u.getPropertyIndex().toJSON()).getAsString(); if(token.getResponseCode() != 200) { return false; } if(UberClone.isDriverMode()) { Preferences.set("driver-token", token.getResponseData()); registerPush(); } else { Preferences.set("token", token.getResponseData()); } return true; } public static void loginWithPhone(String phoneNumber, String password, final SuccessCallback<User> onSuccess, final FailureCallback<Object> onError) { Rest.get(SERVER_URL + "user/login"). acceptJson(). queryParam("password", password). queryParam("phone", phoneNumber). UserService After the first activation of the driver app we need to register for push. Notice I'm using the version of this method from the CN class with static import but the callback will go as expected into the DriverApp class