SlideShare a Scribd company logo
1 of 25
Download to read offline
Creating an Uber Clone - Part XXV
Next we’ll go into the underlying business logic portion of the hailing process on the client
LocationService
✦Mark hailing in websocket code
✦Server checks available area
✦Send push notifications to available drivers
✦Expand the circle of search
© Codename One 2017 all rights reserved
Up until now I kept hailing as a vague process. I think Uber probably has a far more elaborate system than the one I developed in an hour but this should work fine for
most cases. Hailing includes the following phases:

We mark that we are interested in hailing in the location service WebSocket code.

The server checks which drivers are available in the area and returns to us their push keys

We send push notifications to available drivers

As time moves on we expand the circle of search for drivers
public static final String CODENAME_ONE_PUSH_KEY = "----";
public static final String GOOGLE_PUSH_AUTH_KEY = "----";
public static final String APNS_DEV_PUSH_CERT = "----";
public static final String APNS_PROD_PUSH_CERT = "----";
public static final String APNS_DEV_PUSH_PASS = "----";
public static final String APNS_PROD_PUSH_PASS = "----";
public static final boolean APNS_PRODUCTION = false;
Globals
In order to do this I had to make some changes to the Websocket protocol in the LocationService class. But first we need some additional variables in the Globals class.
I'll go into more details on those values in the next chapter but for now we need the variables only. 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

Right now we can leave these all blank and get to them in the next chapter.
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
Let's move to the LocationService class and the variables I had to add

When a driver accepts our hail the server sends a special message indicating that the hail was accepted
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
Previously we had 2 modes for polling the server for searching and not searching. I added a 3rd mode that allows us to disable hailing
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
I've made the LocationService into a singleton
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
These represent whether we are hailing and if so to what radius? We use a Motion object to get a growing radius that will stretch over time to encapsulate a wider region
for hailing
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
Our source and destination values which we need to broadcast a hail
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
When we send a push notification to a car we need to make sure we didn't already notify it
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
The unique id of the driver we've found
private static final short MESSAGE_TYPE_DRIVER_FOUND = 4;
private static final short HAILING_OFF = 0;
private static final short HAILING_ON = 1;
private static final short HAILING_TURN_OFF = 2;
private static LocationService instance;
private int hailing;
private Motion halingRadius;
private String from;
private String to;
private List<String> notificationList;
private long driverId;
private CarAdded driverFound;
LocationService
This is the callback we invoke when a driver is found
if(ll == lon && lt == lat && dir == direction && hailing == HAILING_OFF) {
// no need to do an update
return;
}
sendLocationUpdate
Now that these are out of the way lets look at the other things that need doing. We changed the way we handle the communication protocol by and we added some
additional details.

First we need to ignore location changes when doing hailing which we can do by adding that condition.
dos.writeDouble(lat);
dos.writeDouble(lon);
dos.writeFloat(dir);
if(hailing == HAILING_ON) {
dos.writeDouble(((double)halingRadius.getValue()) / 1000.0);
dos.writeByte(HAILING_ON);
byte[] fromBytes = from.getBytes("UTF-8");
byte[] toBytes = to.getBytes("UTF-8");
dos.writeShort(fromBytes.length);
dos.write(fromBytes);
dos.writeShort(toBytes.length);
dos.write(toBytes);
} else {
dos.writeDouble(1);
dos.writeByte(hailing);
if(hailing == HAILING_TURN_OFF) {
hailing = HAILING_OFF;
}
}
dos.flush();
send(bos.toByteArray());
} catch(IOException err) {
Log.e(err);
}
sendLocationUpdate
Next we need to change the protocol a little bit

This was previously limited to 0 only and now we check if we are in hailing mode
dos.writeDouble(lat);
dos.writeDouble(lon);
dos.writeFloat(dir);
if(hailing == HAILING_ON) {
dos.writeDouble(((double)halingRadius.getValue()) / 1000.0);
dos.writeByte(HAILING_ON);
byte[] fromBytes = from.getBytes("UTF-8");
byte[] toBytes = to.getBytes("UTF-8");
dos.writeShort(fromBytes.length);
dos.write(fromBytes);
dos.writeShort(toBytes.length);
dos.write(toBytes);
} else {
dos.writeDouble(1);
dos.writeByte(hailing);
if(hailing == HAILING_TURN_OFF) {
hailing = HAILING_OFF;
}
}
dos.flush();
send(bos.toByteArray());
} catch(IOException err) {
Log.e(err);
}
sendLocationUpdate
During hailing mode the radius of search grows over time
dos.writeDouble(lat);
dos.writeDouble(lon);
dos.writeFloat(dir);
if(hailing == HAILING_ON) {
dos.writeDouble(((double)halingRadius.getValue()) / 1000.0);
dos.writeByte(HAILING_ON);
byte[] fromBytes = from.getBytes("UTF-8");
byte[] toBytes = to.getBytes("UTF-8");
dos.writeShort(fromBytes.length);
dos.write(fromBytes);
dos.writeShort(toBytes.length);
dos.write(toBytes);
} else {
dos.writeDouble(1);
dos.writeByte(hailing);
if(hailing == HAILING_TURN_OFF) {
hailing = HAILING_OFF;
}
}
dos.flush();
send(bos.toByteArray());
} catch(IOException err) {
Log.e(err);
}
sendLocationUpdate
We send the from/to values as UTF-8 encoded strings which allows us to communicate locale specific locations
dos.writeDouble(lat);
dos.writeDouble(lon);
dos.writeFloat(dir);
if(hailing == HAILING_ON) {
dos.writeDouble(((double)halingRadius.getValue()) / 1000.0);
dos.writeByte(HAILING_ON);
byte[] fromBytes = from.getBytes("UTF-8");
byte[] toBytes = to.getBytes("UTF-8");
dos.writeShort(fromBytes.length);
dos.write(fromBytes);
dos.writeShort(toBytes.length);
dos.write(toBytes);
} else {
dos.writeDouble(1);
dos.writeByte(hailing);
if(hailing == HAILING_TURN_OFF) {
hailing = HAILING_OFF;
}
}
dos.flush();
send(bos.toByteArray());
} catch(IOException err) {
Log.e(err);
}
sendLocationUpdate
When we turn off hailing in the server it's a "one time thing". After it's off we can go back to the regular mode
dos.writeDouble(lat);
dos.writeDouble(lon);
dos.writeFloat(dir);
if(hailing == HAILING_ON) {
dos.writeDouble(((double)halingRadius.getValue()) / 1000.0);
dos.writeByte(HAILING_ON);
byte[] fromBytes = from.getBytes("UTF-8");
byte[] toBytes = to.getBytes("UTF-8");
dos.writeShort(fromBytes.length);
dos.write(fromBytes);
dos.writeShort(toBytes.length);
dos.write(toBytes);
} else {
dos.writeDouble(1);
dos.writeByte(hailing);
if(hailing == HAILING_TURN_OFF) {
hailing = HAILING_OFF;
}
}
dos.flush();
send(bos.toByteArray());
} catch(IOException err) {
Log.e(err);
}
sendLocationUpdate
This isn't likely as this is a RAM based stream
@Override
protected void onMessage(byte[] bytes) {
try {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
short response = dis.readShort();
if(response == MESSAGE_TYPE_DRIVER_FOUND) {
driverId = dis.readLong();
User u = cars.get(driverId);
if(u == null) {
u = new User().id.set(driverId);
cars.put(driverId, u);
}
u.car.set(dis.readUTF()).
givenName.set(dis.readUTF()).
surname.set(dis.readUTF()).
currentRating.set(dis.readFloat());
final User finalUser = u;
callSerially(() -> driverFound.carAdded(finalUser));
return;
}
int size = dis.readInt();
List<String> sendPush = null;
for(int iter = 0 ; iter < size ; iter++) {
onMessage
We also need to handle message reception code.

This is a new special case that provides us with details on the driver that picked up the ride. We are provided with the driver id, car & name. Notice we need the finalUser
variable since car might change and a value that can change can't be passed to an inner class or lambda in Java.
@Override
protected void onMessage(byte[] bytes) {
try {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
short response = dis.readShort();
if(response == MESSAGE_TYPE_DRIVER_FOUND) {
driverId = dis.readLong();
User u = cars.get(driverId);
if(u == null) {
u = new User().id.set(driverId);
cars.put(driverId, u);
}
u.car.set(dis.readUTF()).
givenName.set(dis.readUTF()).
surname.set(dis.readUTF()).
currentRating.set(dis.readFloat());
final User finalUser = u;
callSerially(() -> driverFound.carAdded(finalUser));
return;
}
int size = dis.readInt();
List<String> sendPush = null;
for(int iter = 0 ; iter < size ; iter++) {
onMessage
This is a list of push keys who we should notify
List<String> sendPush = null;
for(int iter = 0 ; iter < size ; iter++) {
long id = dis.readLong();
User car = cars.get(id);
if(car == null) {
car = new User().
id.set(id).
latitude.set(dis.readDouble()).
longitude.set(dis.readDouble()).
direction.set(dis.readFloat()).
pushToken.set(dis.readUTF());
cars.put(id, car);
User finalCar = car;
callSerially(() -> carCallback.carAdded(finalCar));
} else {
car.latitude.set(dis.readDouble()).
longitude.set(dis.readDouble()).
direction.set(dis.readFloat()).
pushToken.set(dis.readUTF());
}
if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){
if(!notificationList.contains(car.pushToken.get())) {
notificationList.add(car.pushToken.get());
onMessage
I added a push token to the driver details so we can send a push message to a specific driver
if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){
if(!notificationList.contains(car.pushToken.get())) {
notificationList.add(car.pushToken.get());
if(sendPush == null) {
sendPush = new ArrayList<>();
}
sendPush.add(car.pushToken.get());
}
}
}
if(sendPush != null) {
String[] devices = new String[sendPush.size()];
sendPush.toArray(devices);
String apnsCert = APNS_DEV_PUSH_CERT;
String apnsPass = APNS_DEV_PUSH_PASS;
if(APNS_PRODUCTION) {
apnsCert = APNS_PROD_PUSH_CERT;
apnsPass = APNS_PROD_PUSH_PASS;
}
new Push(CODENAME_ONE_PUSH_KEY,
"#" + UserService.getUser().id.getLong() +
";Ride pending from: " + from + " to: " + to,
devices).
onMessage
If the car wasn't notified yet add it to the list of cars that we should notify
if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){
if(!notificationList.contains(car.pushToken.get())) {
notificationList.add(car.pushToken.get());
if(sendPush == null) {
sendPush = new ArrayList<>();
}
sendPush.add(car.pushToken.get());
}
}
}
if(sendPush != null) {
String[] devices = new String[sendPush.size()];
sendPush.toArray(devices);
String apnsCert = APNS_DEV_PUSH_CERT;
String apnsPass = APNS_DEV_PUSH_PASS;
if(APNS_PRODUCTION) {
apnsCert = APNS_PROD_PUSH_CERT;
apnsPass = APNS_PROD_PUSH_PASS;
}
new Push(CODENAME_ONE_PUSH_KEY,
"#" + UserService.getUser().id.getLong() +
";Ride pending from: " + from + " to: " + to,
devices).
onMessage
We send the push message in a batch to speed this up
}
}
if(sendPush != null) {
String[] devices = new String[sendPush.size()];
sendPush.toArray(devices);
String apnsCert = APNS_DEV_PUSH_CERT;
String apnsPass = APNS_DEV_PUSH_PASS;
if(APNS_PRODUCTION) {
apnsCert = APNS_PROD_PUSH_CERT;
apnsPass = APNS_PROD_PUSH_PASS;
}
new Push(CODENAME_ONE_PUSH_KEY,
"#" + UserService.getUser().id.getLong() +
";Ride pending from: " + from + " to: " + to,
devices).
pushType(3).
apnsAuth(apnsCert, apnsPass, APNS_PRODUCTION).
gcmAuth(GOOGLE_PUSH_AUTH_KEY).sendAsync();
}
} catch(IOException err) {
Log.e(err);
}
}
onMessage
We send push type 3 which includes a data payload (the first section) and a visual payload which you see after the semicolon
public final Property<String, User> pushToken = new Property<>("pushToken");
private final PropertyIndex idx = new PropertyIndex(this, "User", id, givenName,
surname, phone, email, facebookId, googleId, driver, car, currentRating,
latitude, longitude, direction, authToken, password, pushToken);
User
Before we can compile that code we need to add a pushToken attribute to the User class
public static void hailRide(String from, String to, CarAdded callback) {
instance.driverFound = callback;
instance.from = from;
instance.to = to;
instance.notificationList = new ArrayList<>();
instance.halingRadius = Motion.createLinearMotion(500, 2000, 30000);
instance.halingRadius.start();
instance.hailing = HAILING_ON;
instance.server.sendLocationUpdate();
}
hailRide
Finally we have the hailRide method which is relatively simple. There isn't much we need to cover here. It just initializes the variables and starts the Motion object for the
expanding radius.

This should conclude the client side of the hailing process and now we need to address the server side…

More Related Content

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

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

Creating an Uber Clone - Part XII.pdf
Creating an Uber Clone - Part XII.pdfCreating an Uber Clone - Part XII.pdf
Creating an Uber Clone - Part XII.pdf
 
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
 
Creating an Uber Clone - Part XXVI.pdf
Creating an Uber Clone - Part XXVI.pdfCreating an Uber Clone - Part XXVI.pdf
Creating an Uber Clone - Part XXVI.pdf
 
Securing Your AWS Infrastructure with Edge Services
Securing Your AWS Infrastructure with Edge ServicesSecuring Your AWS Infrastructure with Edge Services
Securing Your AWS Infrastructure with Edge Services
 
introduction to Windows Comunication Foundation
introduction to Windows Comunication Foundationintroduction to Windows Comunication Foundation
introduction to Windows Comunication Foundation
 
Creating an Uber Clone - Part XVII - Transcript.pdf
Creating an Uber Clone - Part XVII - Transcript.pdfCreating an Uber Clone - Part XVII - Transcript.pdf
Creating an Uber Clone - Part XVII - Transcript.pdf
 
Running Serverless at The Edge (CTD302) - AWS re:Invent 2018
Running Serverless at The Edge (CTD302) - AWS re:Invent 2018Running Serverless at The Edge (CTD302) - AWS re:Invent 2018
Running Serverless at The Edge (CTD302) - AWS re:Invent 2018
 
TripThru_API_Doc_v1
TripThru_API_Doc_v1TripThru_API_Doc_v1
TripThru_API_Doc_v1
 
Using the GSMA OneAPI Gateway
Using the GSMA OneAPI GatewayUsing the GSMA OneAPI Gateway
Using the GSMA OneAPI Gateway
 
Testing Services Effectively
Testing Services Effectively Testing Services Effectively
Testing Services Effectively
 
Aruba OS 6.4 Command Line Interface Reference Guide
Aruba OS 6.4 Command Line Interface Reference GuideAruba OS 6.4 Command Line Interface Reference Guide
Aruba OS 6.4 Command Line Interface Reference Guide
 
Nashik MuleSoft Virtual Meetup#1 - Shared and Dedicated Load Balancer
Nashik MuleSoft Virtual Meetup#1 - Shared and Dedicated Load BalancerNashik MuleSoft Virtual Meetup#1 - Shared and Dedicated Load Balancer
Nashik MuleSoft Virtual Meetup#1 - Shared and Dedicated Load Balancer
 
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
 
Techdays Helsinki - Creating the distributed apps of the future using dapr - ...
Techdays Helsinki - Creating the distributed apps of the future using dapr - ...Techdays Helsinki - Creating the distributed apps of the future using dapr - ...
Techdays Helsinki - Creating the distributed apps of the future using dapr - ...
 
Creating an Uber Clone - Part XII - Transcript.pdf
Creating an Uber Clone - Part XII - Transcript.pdfCreating an Uber Clone - Part XII - Transcript.pdf
Creating an Uber Clone - Part XII - Transcript.pdf
 
Executing a Large Scale Migration to AWS (ENT337-R2) - AWS re:Invent 2018
Executing a Large Scale Migration to AWS (ENT337-R2) - AWS re:Invent 2018Executing a Large Scale Migration to AWS (ENT337-R2) - AWS re:Invent 2018
Executing a Large Scale Migration to AWS (ENT337-R2) - AWS re:Invent 2018
 
Creating an Uber Clone - Part XXXI - Transcript.pdf
Creating an Uber Clone - Part XXXI - Transcript.pdfCreating an Uber Clone - Part XXXI - Transcript.pdf
Creating an Uber Clone - Part XXXI - Transcript.pdf
 
Optimizing Lambda@Edge for Performance and Cost Efficiency (CTD405-R2) - AWS ...
Optimizing Lambda@Edge for Performance and Cost Efficiency (CTD405-R2) - AWS ...Optimizing Lambda@Edge for Performance and Cost Efficiency (CTD405-R2) - AWS ...
Optimizing Lambda@Edge for Performance and Cost Efficiency (CTD405-R2) - AWS ...
 
Pavlo Zhdanov "Java and Swift: How to Create Applications for Automotive Head...
Pavlo Zhdanov "Java and Swift: How to Create Applications for Automotive Head...Pavlo Zhdanov "Java and Swift: How to Create Applications for Automotive Head...
Pavlo Zhdanov "Java and Swift: How to Create Applications for Automotive Head...
 
Tadhack madrid June 2014: Joris Swinnen and WebRTC Nederland "Invite my colle...
Tadhack madrid June 2014: Joris Swinnen and WebRTC Nederland "Invite my colle...Tadhack madrid June 2014: Joris Swinnen and WebRTC Nederland "Invite my colle...
Tadhack madrid June 2014: Joris Swinnen and WebRTC Nederland "Invite my colle...
 

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
 
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc
 

Recently uploaded (20)

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...
 
Stronger Together: Developing an Organizational Strategy for Accessible Desig...
Stronger Together: Developing an Organizational Strategy for Accessible Desig...Stronger Together: Developing an Organizational Strategy for Accessible Desig...
Stronger Together: Developing an Organizational Strategy for Accessible Desig...
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
API Governance and Monetization - The evolution of API governance
API Governance and Monetization -  The evolution of API governanceAPI Governance and Monetization -  The evolution of API governance
API Governance and Monetization - The evolution of API governance
 
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
TrustArc Webinar - Unified Trust Center for Privacy, Security, Compliance, an...
 
Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
AI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by AnitarajAI in Action: Real World Use Cases by Anitaraj
AI in Action: Real World Use Cases by Anitaraj
 
Introduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDMIntroduction to use of FHIR Documents in ABDM
Introduction to use of FHIR Documents in ABDM
 
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
AI+A11Y 11MAY2024 HYDERBAD GAAD 2024 - HelloA11Y (11 May 2024)
 
TEST BANK For Principles of Anatomy and Physiology, 16th Edition by Gerard J....
TEST BANK For Principles of Anatomy and Physiology, 16th Edition by Gerard J....TEST BANK For Principles of Anatomy and Physiology, 16th Edition by Gerard J....
TEST BANK For Principles of Anatomy and Physiology, 16th Edition by Gerard J....
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Less Is More: Utilizing Ballerina to Architect a Cloud Data Platform
Less Is More: Utilizing Ballerina to Architect a Cloud Data PlatformLess Is More: Utilizing Ballerina to Architect a Cloud Data Platform
Less Is More: Utilizing Ballerina to Architect a Cloud Data Platform
 
Navigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern EnterpriseNavigating Identity and Access Management in the Modern Enterprise
Navigating Identity and Access Management in the Modern Enterprise
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and Insight
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Simplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptxSimplifying Mobile A11y Presentation.pptx
Simplifying Mobile A11y Presentation.pptx
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 

Creating an Uber Clone - Part XXV - Transcript.pdf

  • 1. Creating an Uber Clone - Part XXV Next we’ll go into the underlying business logic portion of the hailing process on the client
  • 2. LocationService ✦Mark hailing in websocket code ✦Server checks available area ✦Send push notifications to available drivers ✦Expand the circle of search © Codename One 2017 all rights reserved Up until now I kept hailing as a vague process. I think Uber probably has a far more elaborate system than the one I developed in an hour but this should work fine for most cases. Hailing includes the following phases: We mark that we are interested in hailing in the location service WebSocket code. The server checks which drivers are available in the area and returns to us their push keys We send push notifications to available drivers As time moves on we expand the circle of search for drivers
  • 3. public static final String CODENAME_ONE_PUSH_KEY = "----"; public static final String GOOGLE_PUSH_AUTH_KEY = "----"; public static final String APNS_DEV_PUSH_CERT = "----"; public static final String APNS_PROD_PUSH_CERT = "----"; public static final String APNS_DEV_PUSH_PASS = "----"; public static final String APNS_PROD_PUSH_PASS = "----"; public static final boolean APNS_PRODUCTION = false; Globals In order to do this I had to make some changes to the Websocket protocol in the LocationService class. But first we need some additional variables in the Globals class. I'll go into more details on those values in the next chapter but for now we need the variables only. 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 Right now we can leave these all blank and get to them in the next chapter.
  • 4. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService Let's move to the LocationService class and the variables I had to add When a driver accepts our hail the server sends a special message indicating that the hail was accepted
  • 5. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService Previously we had 2 modes for polling the server for searching and not searching. I added a 3rd mode that allows us to disable hailing
  • 6. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService I've made the LocationService into a singleton
  • 7. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService These represent whether we are hailing and if so to what radius? We use a Motion object to get a growing radius that will stretch over time to encapsulate a wider region for hailing
  • 8. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService Our source and destination values which we need to broadcast a hail
  • 9. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService When we send a push notification to a car we need to make sure we didn't already notify it
  • 10. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService The unique id of the driver we've found
  • 11. private static final short MESSAGE_TYPE_DRIVER_FOUND = 4; private static final short HAILING_OFF = 0; private static final short HAILING_ON = 1; private static final short HAILING_TURN_OFF = 2; private static LocationService instance; private int hailing; private Motion halingRadius; private String from; private String to; private List<String> notificationList; private long driverId; private CarAdded driverFound; LocationService This is the callback we invoke when a driver is found
  • 12. if(ll == lon && lt == lat && dir == direction && hailing == HAILING_OFF) { // no need to do an update return; } sendLocationUpdate Now that these are out of the way lets look at the other things that need doing. We changed the way we handle the communication protocol by and we added some additional details. First we need to ignore location changes when doing hailing which we can do by adding that condition.
  • 13. dos.writeDouble(lat); dos.writeDouble(lon); dos.writeFloat(dir); if(hailing == HAILING_ON) { dos.writeDouble(((double)halingRadius.getValue()) / 1000.0); dos.writeByte(HAILING_ON); byte[] fromBytes = from.getBytes("UTF-8"); byte[] toBytes = to.getBytes("UTF-8"); dos.writeShort(fromBytes.length); dos.write(fromBytes); dos.writeShort(toBytes.length); dos.write(toBytes); } else { dos.writeDouble(1); dos.writeByte(hailing); if(hailing == HAILING_TURN_OFF) { hailing = HAILING_OFF; } } dos.flush(); send(bos.toByteArray()); } catch(IOException err) { Log.e(err); } sendLocationUpdate Next we need to change the protocol a little bit This was previously limited to 0 only and now we check if we are in hailing mode
  • 14. dos.writeDouble(lat); dos.writeDouble(lon); dos.writeFloat(dir); if(hailing == HAILING_ON) { dos.writeDouble(((double)halingRadius.getValue()) / 1000.0); dos.writeByte(HAILING_ON); byte[] fromBytes = from.getBytes("UTF-8"); byte[] toBytes = to.getBytes("UTF-8"); dos.writeShort(fromBytes.length); dos.write(fromBytes); dos.writeShort(toBytes.length); dos.write(toBytes); } else { dos.writeDouble(1); dos.writeByte(hailing); if(hailing == HAILING_TURN_OFF) { hailing = HAILING_OFF; } } dos.flush(); send(bos.toByteArray()); } catch(IOException err) { Log.e(err); } sendLocationUpdate During hailing mode the radius of search grows over time
  • 15. dos.writeDouble(lat); dos.writeDouble(lon); dos.writeFloat(dir); if(hailing == HAILING_ON) { dos.writeDouble(((double)halingRadius.getValue()) / 1000.0); dos.writeByte(HAILING_ON); byte[] fromBytes = from.getBytes("UTF-8"); byte[] toBytes = to.getBytes("UTF-8"); dos.writeShort(fromBytes.length); dos.write(fromBytes); dos.writeShort(toBytes.length); dos.write(toBytes); } else { dos.writeDouble(1); dos.writeByte(hailing); if(hailing == HAILING_TURN_OFF) { hailing = HAILING_OFF; } } dos.flush(); send(bos.toByteArray()); } catch(IOException err) { Log.e(err); } sendLocationUpdate We send the from/to values as UTF-8 encoded strings which allows us to communicate locale specific locations
  • 16. dos.writeDouble(lat); dos.writeDouble(lon); dos.writeFloat(dir); if(hailing == HAILING_ON) { dos.writeDouble(((double)halingRadius.getValue()) / 1000.0); dos.writeByte(HAILING_ON); byte[] fromBytes = from.getBytes("UTF-8"); byte[] toBytes = to.getBytes("UTF-8"); dos.writeShort(fromBytes.length); dos.write(fromBytes); dos.writeShort(toBytes.length); dos.write(toBytes); } else { dos.writeDouble(1); dos.writeByte(hailing); if(hailing == HAILING_TURN_OFF) { hailing = HAILING_OFF; } } dos.flush(); send(bos.toByteArray()); } catch(IOException err) { Log.e(err); } sendLocationUpdate When we turn off hailing in the server it's a "one time thing". After it's off we can go back to the regular mode
  • 17. dos.writeDouble(lat); dos.writeDouble(lon); dos.writeFloat(dir); if(hailing == HAILING_ON) { dos.writeDouble(((double)halingRadius.getValue()) / 1000.0); dos.writeByte(HAILING_ON); byte[] fromBytes = from.getBytes("UTF-8"); byte[] toBytes = to.getBytes("UTF-8"); dos.writeShort(fromBytes.length); dos.write(fromBytes); dos.writeShort(toBytes.length); dos.write(toBytes); } else { dos.writeDouble(1); dos.writeByte(hailing); if(hailing == HAILING_TURN_OFF) { hailing = HAILING_OFF; } } dos.flush(); send(bos.toByteArray()); } catch(IOException err) { Log.e(err); } sendLocationUpdate This isn't likely as this is a RAM based stream
  • 18. @Override protected void onMessage(byte[] bytes) { try { DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); short response = dis.readShort(); if(response == MESSAGE_TYPE_DRIVER_FOUND) { driverId = dis.readLong(); User u = cars.get(driverId); if(u == null) { u = new User().id.set(driverId); cars.put(driverId, u); } u.car.set(dis.readUTF()). givenName.set(dis.readUTF()). surname.set(dis.readUTF()). currentRating.set(dis.readFloat()); final User finalUser = u; callSerially(() -> driverFound.carAdded(finalUser)); return; } int size = dis.readInt(); List<String> sendPush = null; for(int iter = 0 ; iter < size ; iter++) { onMessage We also need to handle message reception code. This is a new special case that provides us with details on the driver that picked up the ride. We are provided with the driver id, car & name. Notice we need the finalUser variable since car might change and a value that can change can't be passed to an inner class or lambda in Java.
  • 19. @Override protected void onMessage(byte[] bytes) { try { DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); short response = dis.readShort(); if(response == MESSAGE_TYPE_DRIVER_FOUND) { driverId = dis.readLong(); User u = cars.get(driverId); if(u == null) { u = new User().id.set(driverId); cars.put(driverId, u); } u.car.set(dis.readUTF()). givenName.set(dis.readUTF()). surname.set(dis.readUTF()). currentRating.set(dis.readFloat()); final User finalUser = u; callSerially(() -> driverFound.carAdded(finalUser)); return; } int size = dis.readInt(); List<String> sendPush = null; for(int iter = 0 ; iter < size ; iter++) { onMessage This is a list of push keys who we should notify
  • 20. List<String> sendPush = null; for(int iter = 0 ; iter < size ; iter++) { long id = dis.readLong(); User car = cars.get(id); if(car == null) { car = new User(). id.set(id). latitude.set(dis.readDouble()). longitude.set(dis.readDouble()). direction.set(dis.readFloat()). pushToken.set(dis.readUTF()); cars.put(id, car); User finalCar = car; callSerially(() -> carCallback.carAdded(finalCar)); } else { car.latitude.set(dis.readDouble()). longitude.set(dis.readDouble()). direction.set(dis.readFloat()). pushToken.set(dis.readUTF()); } if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){ if(!notificationList.contains(car.pushToken.get())) { notificationList.add(car.pushToken.get()); onMessage I added a push token to the driver details so we can send a push message to a specific driver
  • 21. if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){ if(!notificationList.contains(car.pushToken.get())) { notificationList.add(car.pushToken.get()); if(sendPush == null) { sendPush = new ArrayList<>(); } sendPush.add(car.pushToken.get()); } } } if(sendPush != null) { String[] devices = new String[sendPush.size()]; sendPush.toArray(devices); String apnsCert = APNS_DEV_PUSH_CERT; String apnsPass = APNS_DEV_PUSH_PASS; if(APNS_PRODUCTION) { apnsCert = APNS_PROD_PUSH_CERT; apnsPass = APNS_PROD_PUSH_PASS; } new Push(CODENAME_ONE_PUSH_KEY, "#" + UserService.getUser().id.getLong() + ";Ride pending from: " + from + " to: " + to, devices). onMessage If the car wasn't notified yet add it to the list of cars that we should notify
  • 22. if(hailing==HAILING_ON && response==MESSAGE_TYPE_AVAILBLE_DRIVER_POSITIONS){ if(!notificationList.contains(car.pushToken.get())) { notificationList.add(car.pushToken.get()); if(sendPush == null) { sendPush = new ArrayList<>(); } sendPush.add(car.pushToken.get()); } } } if(sendPush != null) { String[] devices = new String[sendPush.size()]; sendPush.toArray(devices); String apnsCert = APNS_DEV_PUSH_CERT; String apnsPass = APNS_DEV_PUSH_PASS; if(APNS_PRODUCTION) { apnsCert = APNS_PROD_PUSH_CERT; apnsPass = APNS_PROD_PUSH_PASS; } new Push(CODENAME_ONE_PUSH_KEY, "#" + UserService.getUser().id.getLong() + ";Ride pending from: " + from + " to: " + to, devices). onMessage We send the push message in a batch to speed this up
  • 23. } } if(sendPush != null) { String[] devices = new String[sendPush.size()]; sendPush.toArray(devices); String apnsCert = APNS_DEV_PUSH_CERT; String apnsPass = APNS_DEV_PUSH_PASS; if(APNS_PRODUCTION) { apnsCert = APNS_PROD_PUSH_CERT; apnsPass = APNS_PROD_PUSH_PASS; } new Push(CODENAME_ONE_PUSH_KEY, "#" + UserService.getUser().id.getLong() + ";Ride pending from: " + from + " to: " + to, devices). pushType(3). apnsAuth(apnsCert, apnsPass, APNS_PRODUCTION). gcmAuth(GOOGLE_PUSH_AUTH_KEY).sendAsync(); } } catch(IOException err) { Log.e(err); } } onMessage We send push type 3 which includes a data payload (the first section) and a visual payload which you see after the semicolon
  • 24. public final Property<String, User> pushToken = new Property<>("pushToken"); private final PropertyIndex idx = new PropertyIndex(this, "User", id, givenName, surname, phone, email, facebookId, googleId, driver, car, currentRating, latitude, longitude, direction, authToken, password, pushToken); User Before we can compile that code we need to add a pushToken attribute to the User class
  • 25. public static void hailRide(String from, String to, CarAdded callback) { instance.driverFound = callback; instance.from = from; instance.to = to; instance.notificationList = new ArrayList<>(); instance.halingRadius = Motion.createLinearMotion(500, 2000, 30000); instance.halingRadius.start(); instance.hailing = HAILING_ON; instance.server.sendLocationUpdate(); } hailRide Finally we have the hailRide method which is relatively simple. There isn't much we need to cover here. It just initializes the variables and starts the Motion object for the expanding radius. This should conclude the client side of the hailing process and now we need to address the server side…