SlideShare a Scribd company logo
1 of 25
Download to read offline
Creating an Uber Clone - Part XXX
In this very explicit section we are going to get into the flow of hailing a ride between driver and user
User/Driver Flow
✦User: Confirm Hailing - Server Responds
✦User: Sends push messages
✦Driver: Shown push, if he clicks he’s shown ride details
✦Driver: Accepts route, notifies server
✦User: Receives WebSocket message of acceptance
✦Driver: Shown Dialog to indicate pickup
✦Driver: Shown dialog to finish the ride. Location
updates set the route of the ride
© Codename One 2017 all rights reserved
With the MapForm we are finally implementing the visual aspects of the driver app. Ideally we'd hide the search button and do a lot of refinement work on the UI but I
don't have the space to address all of that...

Before we dive into the code lets look at the flow from a birds eye view:

- A user confirms hailing and sends the details to the server. The server answers with a list of drivers in the area and their push tokens

- The user starts sending push messages to the drivers in the region

- The driver app receives a push message. It shows a ToastBar message which the driver can click. If the driver clicks the ToastBar message he is presented with details
of a potential ride specifically the route

- If the driver accepts the route the server is notified

- The User is sent a WebSocket message that a driver accepted the ride. The User sees a notice with the name of the driver, rating etc.

- The driver is shown a dialog that allows him to cancel the ride or mark that he picked up the passenger. Once the driver clicks that he picked up the passenger the ride
starts

- The driver is shown a Dialog that allows him to finish the ride . During the ride the location of the driver is used to gather the route to the destination.

This is a gross oversimplification of the Uber user flow. The actual app has a lot of nuances within this flow that include many edge cases. I didn't even go into the
public void showRide(long userId) {
InteractionDialog id = new InteractionDialog(new BorderLayout());
id.setTitle("Loading Ride Details");
id.add(CENTER, new InfiniteProgress());
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
DriverService.fetchRideDetails(userId, ride -> {
id.setTitle("Building Ride Path");
final Coord[] locations = new Coord[2];
if(ride.from.get() == null) {
id.dispose();
ToastBar.showErrorMessage("Ride no longer available...");
return;
}
SearchService.findLocation(ride.from.get(), fromLocation -> {
locations[0] = fromLocation;
onShowRideResponse(id, ride, locations);
});
SearchService.findLocation(ride.destination.get(), toLocation -> {
locations[1] = toLocation;
onShowRideResponse(id, ride, locations);
});
});
}
showRide
Let's see how this all comes together in the changes made to MapForm. The first method we saw in the callback from the push notification is the showRide method.

We use an InteractionDialog for simplicity, I show it in the bottom of the form. The arguments for position indicate the distance from each edge so it touches all the edges
other than the top edge. As a side note notice that the regular Dialog doesn't work well on top of maps.
public void showRide(long userId) {
InteractionDialog id = new InteractionDialog(new BorderLayout());
id.setTitle("Loading Ride Details");
id.add(CENTER, new InfiniteProgress());
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
DriverService.fetchRideDetails(userId, ride -> {
id.setTitle("Building Ride Path");
final Coord[] locations = new Coord[2];
if(ride.from.get() == null) {
id.dispose();
ToastBar.showErrorMessage("Ride no longer available...");
return;
}
SearchService.findLocation(ride.from.get(), fromLocation -> {
locations[0] = fromLocation;
onShowRideResponse(id, ride, locations);
});
SearchService.findLocation(ride.destination.get(), toLocation -> {
locations[1] = toLocation;
onShowRideResponse(id, ride, locations);
});
});
}
showRide
By the time the callback is returned from fetchRideDetails the ride might no longer be available so we can just dispose the UI and continue as usual
public void showRide(long userId) {
InteractionDialog id = new InteractionDialog(new BorderLayout());
id.setTitle("Loading Ride Details");
id.add(CENTER, new InfiniteProgress());
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
DriverService.fetchRideDetails(userId, ride -> {
id.setTitle("Building Ride Path");
final Coord[] locations = new Coord[2];
if(ride.from.get() == null) {
id.dispose();
ToastBar.showErrorMessage("Ride no longer available...");
return;
}
SearchService.findLocation(ride.from.get(), fromLocation -> {
locations[0] = fromLocation;
onShowRideResponse(id, ride, locations);
});
SearchService.findLocation(ride.destination.get(), toLocation -> {
locations[1] = toLocation;
onShowRideResponse(id, ride, locations);
});
});
}
showRide
We need to find the coordinates on the map of the source and destination. I could have just transferred the map coordinates in the ride data (but I didn't)... If findLocation
was synchronous I could just invoke find on from then on destination and everything would work but it would be slow as it would require that we make the first
WebService call and then the second one. In this way both calls might happen concurrently. The first one to complete will have one location in the locations array and
the second one will have both. The onShowRideResponse doesn't do anything unless both locations are set… 

A race condition won’t be possible here since the callbacks are invoked on the EDT so they will always happen in sequence.
void onShowRideResponse(InteractionDialog dlg, Ride ride,
Coord[] locations) {
if(locations[0] == null || locations[1] == null) {
return;
}
SearchService.directions(toLocation(locations[0]), toLocation(locations[1]),
(path, duration, distance) -> {
dlg.dispose();
String from = ride.from.get();
String to = ride.destination.get();
Component fromComponent = createNavigationTag(
trimmedString(from), duration / 60);
Component toComponent = createNavigationTag(trimmedString(to), -1);
MapContainer.MapObject pathObject = addPath(path,
fromComponent, toComponent, duration);
InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Accept", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.setAnimateShow(false);
id.add(acceptButton);
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
onShowRideResponse
The obvious next step is the onShowRideResponse method

As I mentioned before the from/destination values are fetched concurrently so this method will be invoked twice. Only the second time would be valid
void onShowRideResponse(InteractionDialog dlg, Ride ride,
Coord[] locations) {
if(locations[0] == null || locations[1] == null) {
return;
}
SearchService.directions(toLocation(locations[0]), toLocation(locations[1]),
(path, duration, distance) -> {
dlg.dispose();
String from = ride.from.get();
String to = ride.destination.get();
Component fromComponent = createNavigationTag(
trimmedString(from), duration / 60);
Component toComponent = createNavigationTag(trimmedString(to), -1);
MapContainer.MapObject pathObject = addPath(path,
fromComponent, toComponent, duration);
InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Accept", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.setAnimateShow(false);
id.add(acceptButton);
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
onShowRideResponse
I used Coord at some points and Location at others. The reason for both objects is mostly historic where the map API works with one and the Location API's work with
another so the toLocation method just translates Coord to the Location object type
void onShowRideResponse(InteractionDialog dlg, Ride ride,
Coord[] locations) {
if(locations[0] == null || locations[1] == null) {
return;
}
SearchService.directions(toLocation(locations[0]), toLocation(locations[1]),
(path, duration, distance) -> {
dlg.dispose();
String from = ride.from.get();
String to = ride.destination.get();
Component fromComponent = createNavigationTag(
trimmedString(from), duration / 60);
Component toComponent = createNavigationTag(trimmedString(to), -1);
MapContainer.MapObject pathObject = addPath(path,
fromComponent, toComponent, duration);
InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Accept", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.setAnimateShow(false);
id.add(acceptButton);
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
onShowRideResponse
This creates a map path similar to the one the user sees on his phone so the driver and user have a similar view of the trip properties and duration. I'll discuss the
addPath method later, I refactored some code from the enterNavigationMode method into that new method
void onShowRideResponse(InteractionDialog dlg, Ride ride,
Coord[] locations) {
if(locations[0] == null || locations[1] == null) {
return;
}
SearchService.directions(toLocation(locations[0]), toLocation(locations[1]),
(path, duration, distance) -> {
dlg.dispose();
String from = ride.from.get();
String to = ride.destination.get();
Component fromComponent = createNavigationTag(
trimmedString(from), duration / 60);
Component toComponent = createNavigationTag(trimmedString(to), -1);
MapContainer.MapObject pathObject = addPath(path,
fromComponent, toComponent, duration);
InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Accept", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.setAnimateShow(false);
id.add(acceptButton);
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
onShowRideResponse
We create a new InteractionDialog to prompt the driver whether he is interested in accepting this ride or not. It would look roughly like this image
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
id.dispose();
});
acceptButton.addActionListener(e -> {
boolean accept = DriverService.acceptRide(ride.userId.getLong());
callSerially(() -> {
if(accept) {
id.dispose();
pickUpPassenger(pathObject, ride, fromComponent,
toComponent);
} else {
id.dispose();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
getAnimationManager().flushAnimation(() ->
ToastBar.showErrorMessage("Failed to grab ride"));
}
});
});
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
onShowRideResponse
In the case he doesn't want to accept the ride the work is pretty easy, we just remove everything we added
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
id.dispose();
});
acceptButton.addActionListener(e -> {
boolean accept = DriverService.acceptRide(ride.userId.getLong());
callSerially(() -> {
if(accept) {
id.dispose();
pickUpPassenger(pathObject, ride, fromComponent,
toComponent);
} else {
id.dispose();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
getAnimationManager().flushAnimation(() ->
ToastBar.showErrorMessage("Failed to grab ride"));
}
});
});
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
onShowRideResponse
If he does accept the ride we trigger the accept method. Notice that the accept call is a blocking call so just to be safe I used callSerially afterwards to flush any EDT
related animation before showing the next UI
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
id.dispose();
});
acceptButton.addActionListener(e -> {
boolean accept = DriverService.acceptRide(ride.userId.getLong());
callSerially(() -> {
if(accept) {
id.dispose();
pickUpPassenger(pathObject, ride, fromComponent,
toComponent);
} else {
id.dispose();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
getAnimationManager().flushAnimation(() ->
ToastBar.showErrorMessage("Failed to grab ride"));
}
});
});
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
onShowRideResponse
This shows the pickup UI and passes all the components/objects we'll need to eventually "clean up" such as the path and the components on the map
id.add(cancelButton);
cancelButton.addActionListener(e -> {
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
id.dispose();
});
acceptButton.addActionListener(e -> {
boolean accept = DriverService.acceptRide(ride.userId.getLong());
callSerially(() -> {
if(accept) {
id.dispose();
pickUpPassenger(pathObject, ride, fromComponent,
toComponent);
} else {
id.dispose();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
getAnimationManager().flushAnimation(() ->
ToastBar.showErrorMessage("Failed to grab ride"));
}
});
});
id.show(getHeight() - id.getPreferredH(), 0, 0, 0);
onShowRideResponse
This is essentially identical to the code we have for canceling the ride
private Location toLocation(Coord crd) {
return new Location(crd.getLatitude(), crd.getLongitude());
}
toLocation
For completeness the toLocation method is this. It converts Coord objects to Location objects
public void pickUpPassenger(MapContainer.MapObject pathObject,
Ride ride,
Component fromComponent, Component toComponent) {
InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Picked Up", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.add(acceptButton);
id.add(cancelButton);
acceptButton.addActionListener(e -> {
DriverService.startRide();
id.dispose();
InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y());
dlg.add(new Label(ride.name.get(), "RideTitle"));
Button finishButton = new Button("Finished Ride", "BlackButton");
dlg.add(finishButton);
finishButton.addActionListener(ee -> {
DriverService.finishRide();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
dlg.dispose();
});
dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0);
});
cancelButton.addActionListener(e -> {
fromComponent.remove();
pickUpPassenger
Now we can proceed to the pickUpPassenger method where we handle the rest of the ride.

This is again a pretty standard approach by now I'm creating another dialog with the Picked up button as the accept option
public void pickUpPassenger(MapContainer.MapObject pathObject,
Ride ride,
Component fromComponent, Component toComponent) {
InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Picked Up", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.add(acceptButton);
id.add(cancelButton);
acceptButton.addActionListener(e -> {
DriverService.startRide();
id.dispose();
InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y());
dlg.add(new Label(ride.name.get(), "RideTitle"));
Button finishButton = new Button("Finished Ride", "BlackButton");
dlg.add(finishButton);
finishButton.addActionListener(ee -> {
DriverService.finishRide();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
dlg.dispose();
});
dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0);
});
cancelButton.addActionListener(e -> {
fromComponent.remove();
pickUpPassenger
The Accept button leads us to the last step which is the Finish button
public void pickUpPassenger(MapContainer.MapObject pathObject,
Ride ride,
Component fromComponent, Component toComponent) {
InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y());
id.add(new Label(ride.name.get(), "RideTitle"));
Button acceptButton = new Button("Picked Up", "BlackButton");
Button cancelButton = new Button("Cancel", "BlackButton");
id.add(acceptButton);
id.add(cancelButton);
acceptButton.addActionListener(e -> {
DriverService.startRide();
id.dispose();
InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y());
dlg.add(new Label(ride.name.get(), "RideTitle"));
Button finishButton = new Button("Finished Ride", "BlackButton");
dlg.add(finishButton);
finishButton.addActionListener(ee -> {
DriverService.finishRide();
fromComponent.remove();
toComponent.remove();
mc.removeMapObject(pathObject);
dlg.dispose();
});
dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0);
});
cancelButton.addActionListener(e -> {
fromComponent.remove();
pickUpPassenger
Once we picked up a passenger we can only finish the ride, not cancel it. Since I didn't integrate any billing I think it makes sense. With billing we'll obviously need a way
to pay and refund a ride
private MapContainer.MapObject addPath(List<Coord> path,
Component fromComponent, Component toComponent, int duration) {
Coord[] pathCoords = new Coord[path.size()];
path.toArray(pathCoords);
MapContainer.MapObject pathObject = mc.addPath(pathCoords);
BoundingBox bb = BoundingBox.create(pathCoords).
extend(new BoundingBox(pathCoords[0], 0.01, 0.01)).
extend(new BoundingBox(pathCoords[pathCoords.length-1], 0.01, 0.01));
mc.fitBounds(bb);
MapLayout.setHorizontalAlignment(fromComponent,
MapLayout.HALIGN.RIGHT);
mapLayer.add(pathCoords[0], fromComponent);
mapLayer.add(pathCoords[pathCoords.length - 1], toComponent);
return pathObject;
}
addPath
I used the addPath method in the onShowRideResponse so I’m showing it here for completeness.

The addPath method generalizes some of the common code for adding a path and zooming into the map. I won't go into the details of this method as all the code in here
existed in enterNavigationMode so it should be pretty familiar.
private void hailRideImpl(User car, final Container pinLayer) {
pinLayer.getUnselectedStyle().setBgTransparency(0);
pinLayer.removeAll();
String driverName = car.givenName.get();
String carBrand = car.car.get();
SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand);
Container stars = new Container(new FlowLayout(CENTER));
for(int iter = 0 ; iter < 5 ; iter++) {
if(iter + 1 >= car.currentRating.getFloat()) {
Label fullStar = new Label("", "Star");
FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR);
stars.add(fullStar);
} else {
if(iter + 1 >= Math.round(car.currentRating.getFloat())) {
Label halfStar = new Label("", "Star");
FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF);
stars.add(halfStar);
} else {
break;
}
}
}
Button ok = new Button("OK", "BlackButton");
Container dialog = BoxLayout.encloseY(driver, stars, ok);
dialog.setUIID("SearchingDialog");
pinLayer.add(SOUTH, dialog);
revalidate();
hailRideImpl
With that lets move back to the “end user experience”. We discussed the perception from the driver side but what does the passenger see during this whole process? 

That’s exactly what hailRideImpl does…

Both fields include no actual data currently since we don't have the UI to fill in the driver or user names
private void hailRideImpl(User car, final Container pinLayer) {
pinLayer.getUnselectedStyle().setBgTransparency(0);
pinLayer.removeAll();
String driverName = car.givenName.get();
String carBrand = car.car.get();
SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand);
Container stars = new Container(new FlowLayout(CENTER));
for(int iter = 0 ; iter < 5 ; iter++) {
if(iter + 1 >= car.currentRating.getFloat()) {
Label fullStar = new Label("", "Star");
FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR);
stars.add(fullStar);
} else {
if(iter + 1 >= Math.round(car.currentRating.getFloat())) {
Label halfStar = new Label("", "Star");
FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF);
stars.add(halfStar);
} else {
break;
}
}
}
Button ok = new Button("OK", "BlackButton");
Container dialog = BoxLayout.encloseY(driver, stars, ok);
dialog.setUIID("SearchingDialog");
pinLayer.add(SOUTH, dialog);
revalidate();
hailRideImpl
We build the text and add stars based on the level of ranking, this is essentially a set of star labels in a container based on the value of the rank field
private void hailRideImpl(User car, final Container pinLayer) {
pinLayer.getUnselectedStyle().setBgTransparency(0);
pinLayer.removeAll();
String driverName = car.givenName.get();
String carBrand = car.car.get();
SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand);
Container stars = new Container(new FlowLayout(CENTER));
for(int iter = 0 ; iter < 5 ; iter++) {
if(iter + 1 >= car.currentRating.getFloat()) {
Label fullStar = new Label("", "Star");
FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR);
stars.add(fullStar);
} else {
if(iter + 1 >= Math.round(car.currentRating.getFloat())) {
Label halfStar = new Label("", "Star");
FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF);
stars.add(halfStar);
} else {
break;
}
}
}
Button ok = new Button("OK", "BlackButton");
Container dialog = BoxLayout.encloseY(driver, stars, ok);
dialog.setUIID("SearchingDialog");
pinLayer.add(SOUTH, dialog);
revalidate();
hailRideImpl
We call this a "dialog" but it's really just a Container with a special style like before
Star
© Codename One 2017 all rights reserved
In order to produce these stars I need a Star UIID which is suitably yellow and transparent
Star
© Codename One 2017 all rights reserved
I reduced the padding on the sides so the stars remain close to one another
Star
© Codename One 2017 all rights reserved
Margin however, is still 0
Star
© Codename One 2017 all rights reserved
The font is large so the stars appear large

More Related Content

Similar to Creating an Uber Clone - Part XXX Flow

How to use geolocation in react native apps
How to use geolocation in react native appsHow to use geolocation in react native apps
How to use geolocation in react native appsInnovationM
 
Creating an Uber Clone - Part XXIV - Transcript.pdf
Creating an Uber Clone - Part XXIV - Transcript.pdfCreating an Uber Clone - Part XXIV - Transcript.pdf
Creating an Uber Clone - Part XXIV - Transcript.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
Geolocation on Rails
Geolocation on RailsGeolocation on Rails
Geolocation on Railsnebirhos
 
Creating an Uber Clone - Part VII - Transcript.pdf
Creating an Uber Clone - Part VII - Transcript.pdfCreating an Uber Clone - Part VII - Transcript.pdf
Creating an Uber Clone - Part VII - Transcript.pdfShaiAlmog1
 
Creating an Uber Clone - Part XXV - Transcript.pdf
Creating an Uber Clone - Part XXV - Transcript.pdfCreating an Uber Clone - Part XXV - Transcript.pdf
Creating an Uber Clone - Part XXV - Transcript.pdfShaiAlmog1
 
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...Chris Adamson
 
Average- An android project
Average- An android projectAverage- An android project
Average- An android projectIpsit Dash
 
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.pdfShaiAlmog1
 
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.pdfShaiAlmog1
 
Creating an Uber Clone - Part XVII.pdf
Creating an Uber Clone - Part XVII.pdfCreating an Uber Clone - Part XVII.pdf
Creating an Uber Clone - Part XVII.pdfShaiAlmog1
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - ComponentsVisual Engineering
 
HTML5勉強会#23_GeoHex
HTML5勉強会#23_GeoHexHTML5勉強会#23_GeoHex
HTML5勉強会#23_GeoHexTadayasu Sasada
 
Das Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesDas Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesStephan Schmidt
 
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.pdfShaiAlmog1
 
Creating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfCreating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfShaiAlmog1
 
Gmaps Railscamp2008
Gmaps Railscamp2008Gmaps Railscamp2008
Gmaps Railscamp2008xilinus
 

Similar to Creating an Uber Clone - Part XXX Flow (20)

Google Maps Api
Google Maps ApiGoogle Maps Api
Google Maps Api
 
TripThru_API_Doc_v1
TripThru_API_Doc_v1TripThru_API_Doc_v1
TripThru_API_Doc_v1
 
How to use geolocation in react native apps
How to use geolocation in react native appsHow to use geolocation in react native apps
How to use geolocation in react native apps
 
Creating an Uber Clone - Part XXIV - Transcript.pdf
Creating an Uber Clone - Part XXIV - Transcript.pdfCreating an Uber Clone - Part XXIV - Transcript.pdf
Creating an Uber Clone - Part XXIV - Transcript.pdf
 
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
 
Geolocation on Rails
Geolocation on RailsGeolocation on Rails
Geolocation on Rails
 
Creating an Uber Clone - Part VII - Transcript.pdf
Creating an Uber Clone - Part VII - Transcript.pdfCreating an Uber Clone - Part VII - Transcript.pdf
Creating an Uber Clone - Part VII - Transcript.pdf
 
Creating an Uber Clone - Part XXV - Transcript.pdf
Creating an Uber Clone - Part XXV - Transcript.pdfCreating an Uber Clone - Part XXV - Transcript.pdf
Creating an Uber Clone - Part XXV - Transcript.pdf
 
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
Core Location and Map Kit: Bringing Your Own Maps [Voices That Matter: iPhone...
 
Average- An android project
Average- An android projectAverage- An android project
Average- An android project
 
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
 
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
 
Creating an Uber Clone - Part XVII.pdf
Creating an Uber Clone - Part XVII.pdfCreating an Uber Clone - Part XVII.pdf
Creating an Uber Clone - Part XVII.pdf
 
Workshop 25: React Native - Components
Workshop 25: React Native - ComponentsWorkshop 25: React Native - Components
Workshop 25: React Native - Components
 
HTML5勉強会#23_GeoHex
HTML5勉強会#23_GeoHexHTML5勉強会#23_GeoHex
HTML5勉強会#23_GeoHex
 
Express JS
Express JSExpress JS
Express JS
 
Das Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based ServicesDas Web Wird Mobil - Geolocation und Location Based Services
Das Web Wird Mobil - Geolocation und Location Based Services
 
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
 
Creating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdfCreating an Uber Clone - Part XV.pdf
Creating an Uber Clone - Part XV.pdf
 
Gmaps Railscamp2008
Gmaps Railscamp2008Gmaps Railscamp2008
Gmaps Railscamp2008
 

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

Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024BookNet Canada
 
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
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Wonjun Hwang
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersThousandEyes
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsAndrey Dotsenko
 
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
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
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
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Alan Dix
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
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
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 

Recently uploaded (20)

Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC BiblioShare - Tech Forum 2024
 
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
 
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
Bun (KitWorks Team Study 노별마루 발표 2024.4.22)
 
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for PartnersEnhancing Worker Digital Experience: A Hands-on Workshop for Partners
Enhancing Worker Digital Experience: A Hands-on Workshop for Partners
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
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...
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
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
 
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...Swan(sea) Song – personal research during my six years at Swansea ... and bey...
Swan(sea) Song – personal research during my six years at Swansea ... and bey...
 
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort ServiceHot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
Hot Sexy call girls in Panjabi Bagh 🔝 9953056974 🔝 Delhi escort Service
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
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
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 

Creating an Uber Clone - Part XXX Flow

  • 1. Creating an Uber Clone - Part XXX In this very explicit section we are going to get into the flow of hailing a ride between driver and user
  • 2. User/Driver Flow ✦User: Confirm Hailing - Server Responds ✦User: Sends push messages ✦Driver: Shown push, if he clicks he’s shown ride details ✦Driver: Accepts route, notifies server ✦User: Receives WebSocket message of acceptance ✦Driver: Shown Dialog to indicate pickup ✦Driver: Shown dialog to finish the ride. Location updates set the route of the ride © Codename One 2017 all rights reserved With the MapForm we are finally implementing the visual aspects of the driver app. Ideally we'd hide the search button and do a lot of refinement work on the UI but I don't have the space to address all of that... Before we dive into the code lets look at the flow from a birds eye view: - A user confirms hailing and sends the details to the server. The server answers with a list of drivers in the area and their push tokens - The user starts sending push messages to the drivers in the region - The driver app receives a push message. It shows a ToastBar message which the driver can click. If the driver clicks the ToastBar message he is presented with details of a potential ride specifically the route - If the driver accepts the route the server is notified - The User is sent a WebSocket message that a driver accepted the ride. The User sees a notice with the name of the driver, rating etc. - The driver is shown a dialog that allows him to cancel the ride or mark that he picked up the passenger. Once the driver clicks that he picked up the passenger the ride starts
 - The driver is shown a Dialog that allows him to finish the ride . During the ride the location of the driver is used to gather the route to the destination.
 This is a gross oversimplification of the Uber user flow. The actual app has a lot of nuances within this flow that include many edge cases. I didn't even go into the
  • 3. public void showRide(long userId) { InteractionDialog id = new InteractionDialog(new BorderLayout()); id.setTitle("Loading Ride Details"); id.add(CENTER, new InfiniteProgress()); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); DriverService.fetchRideDetails(userId, ride -> { id.setTitle("Building Ride Path"); final Coord[] locations = new Coord[2]; if(ride.from.get() == null) { id.dispose(); ToastBar.showErrorMessage("Ride no longer available..."); return; } SearchService.findLocation(ride.from.get(), fromLocation -> { locations[0] = fromLocation; onShowRideResponse(id, ride, locations); }); SearchService.findLocation(ride.destination.get(), toLocation -> { locations[1] = toLocation; onShowRideResponse(id, ride, locations); }); }); } showRide Let's see how this all comes together in the changes made to MapForm. The first method we saw in the callback from the push notification is the showRide method. We use an InteractionDialog for simplicity, I show it in the bottom of the form. The arguments for position indicate the distance from each edge so it touches all the edges other than the top edge. As a side note notice that the regular Dialog doesn't work well on top of maps.
  • 4. public void showRide(long userId) { InteractionDialog id = new InteractionDialog(new BorderLayout()); id.setTitle("Loading Ride Details"); id.add(CENTER, new InfiniteProgress()); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); DriverService.fetchRideDetails(userId, ride -> { id.setTitle("Building Ride Path"); final Coord[] locations = new Coord[2]; if(ride.from.get() == null) { id.dispose(); ToastBar.showErrorMessage("Ride no longer available..."); return; } SearchService.findLocation(ride.from.get(), fromLocation -> { locations[0] = fromLocation; onShowRideResponse(id, ride, locations); }); SearchService.findLocation(ride.destination.get(), toLocation -> { locations[1] = toLocation; onShowRideResponse(id, ride, locations); }); }); } showRide By the time the callback is returned from fetchRideDetails the ride might no longer be available so we can just dispose the UI and continue as usual
  • 5. public void showRide(long userId) { InteractionDialog id = new InteractionDialog(new BorderLayout()); id.setTitle("Loading Ride Details"); id.add(CENTER, new InfiniteProgress()); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); DriverService.fetchRideDetails(userId, ride -> { id.setTitle("Building Ride Path"); final Coord[] locations = new Coord[2]; if(ride.from.get() == null) { id.dispose(); ToastBar.showErrorMessage("Ride no longer available..."); return; } SearchService.findLocation(ride.from.get(), fromLocation -> { locations[0] = fromLocation; onShowRideResponse(id, ride, locations); }); SearchService.findLocation(ride.destination.get(), toLocation -> { locations[1] = toLocation; onShowRideResponse(id, ride, locations); }); }); } showRide We need to find the coordinates on the map of the source and destination. I could have just transferred the map coordinates in the ride data (but I didn't)... If findLocation was synchronous I could just invoke find on from then on destination and everything would work but it would be slow as it would require that we make the first WebService call and then the second one. In this way both calls might happen concurrently. The first one to complete will have one location in the locations array and the second one will have both. The onShowRideResponse doesn't do anything unless both locations are set… 
 A race condition won’t be possible here since the callbacks are invoked on the EDT so they will always happen in sequence.
  • 6. void onShowRideResponse(InteractionDialog dlg, Ride ride, Coord[] locations) { if(locations[0] == null || locations[1] == null) { return; } SearchService.directions(toLocation(locations[0]), toLocation(locations[1]), (path, duration, distance) -> { dlg.dispose(); String from = ride.from.get(); String to = ride.destination.get(); Component fromComponent = createNavigationTag( trimmedString(from), duration / 60); Component toComponent = createNavigationTag(trimmedString(to), -1); MapContainer.MapObject pathObject = addPath(path, fromComponent, toComponent, duration); InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Accept", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.setAnimateShow(false); id.add(acceptButton); id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); onShowRideResponse The obvious next step is the onShowRideResponse method As I mentioned before the from/destination values are fetched concurrently so this method will be invoked twice. Only the second time would be valid
  • 7. void onShowRideResponse(InteractionDialog dlg, Ride ride, Coord[] locations) { if(locations[0] == null || locations[1] == null) { return; } SearchService.directions(toLocation(locations[0]), toLocation(locations[1]), (path, duration, distance) -> { dlg.dispose(); String from = ride.from.get(); String to = ride.destination.get(); Component fromComponent = createNavigationTag( trimmedString(from), duration / 60); Component toComponent = createNavigationTag(trimmedString(to), -1); MapContainer.MapObject pathObject = addPath(path, fromComponent, toComponent, duration); InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Accept", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.setAnimateShow(false); id.add(acceptButton); id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); onShowRideResponse I used Coord at some points and Location at others. The reason for both objects is mostly historic where the map API works with one and the Location API's work with another so the toLocation method just translates Coord to the Location object type
  • 8. void onShowRideResponse(InteractionDialog dlg, Ride ride, Coord[] locations) { if(locations[0] == null || locations[1] == null) { return; } SearchService.directions(toLocation(locations[0]), toLocation(locations[1]), (path, duration, distance) -> { dlg.dispose(); String from = ride.from.get(); String to = ride.destination.get(); Component fromComponent = createNavigationTag( trimmedString(from), duration / 60); Component toComponent = createNavigationTag(trimmedString(to), -1); MapContainer.MapObject pathObject = addPath(path, fromComponent, toComponent, duration); InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Accept", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.setAnimateShow(false); id.add(acceptButton); id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); onShowRideResponse This creates a map path similar to the one the user sees on his phone so the driver and user have a similar view of the trip properties and duration. I'll discuss the addPath method later, I refactored some code from the enterNavigationMode method into that new method
  • 9. void onShowRideResponse(InteractionDialog dlg, Ride ride, Coord[] locations) { if(locations[0] == null || locations[1] == null) { return; } SearchService.directions(toLocation(locations[0]), toLocation(locations[1]), (path, duration, distance) -> { dlg.dispose(); String from = ride.from.get(); String to = ride.destination.get(); Component fromComponent = createNavigationTag( trimmedString(from), duration / 60); Component toComponent = createNavigationTag(trimmedString(to), -1); MapContainer.MapObject pathObject = addPath(path, fromComponent, toComponent, duration); InteractionDialog id = new InteractionDialog("Ride", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Accept", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.setAnimateShow(false); id.add(acceptButton); id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); onShowRideResponse We create a new InteractionDialog to prompt the driver whether he is interested in accepting this ride or not. It would look roughly like this image
  • 10. id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); id.dispose(); }); acceptButton.addActionListener(e -> { boolean accept = DriverService.acceptRide(ride.userId.getLong()); callSerially(() -> { if(accept) { id.dispose(); pickUpPassenger(pathObject, ride, fromComponent, toComponent); } else { id.dispose(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); getAnimationManager().flushAnimation(() -> ToastBar.showErrorMessage("Failed to grab ride")); } }); }); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); onShowRideResponse In the case he doesn't want to accept the ride the work is pretty easy, we just remove everything we added
  • 11. id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); id.dispose(); }); acceptButton.addActionListener(e -> { boolean accept = DriverService.acceptRide(ride.userId.getLong()); callSerially(() -> { if(accept) { id.dispose(); pickUpPassenger(pathObject, ride, fromComponent, toComponent); } else { id.dispose(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); getAnimationManager().flushAnimation(() -> ToastBar.showErrorMessage("Failed to grab ride")); } }); }); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); onShowRideResponse If he does accept the ride we trigger the accept method. Notice that the accept call is a blocking call so just to be safe I used callSerially afterwards to flush any EDT related animation before showing the next UI
  • 12. id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); id.dispose(); }); acceptButton.addActionListener(e -> { boolean accept = DriverService.acceptRide(ride.userId.getLong()); callSerially(() -> { if(accept) { id.dispose(); pickUpPassenger(pathObject, ride, fromComponent, toComponent); } else { id.dispose(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); getAnimationManager().flushAnimation(() -> ToastBar.showErrorMessage("Failed to grab ride")); } }); }); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); onShowRideResponse This shows the pickup UI and passes all the components/objects we'll need to eventually "clean up" such as the path and the components on the map
  • 13. id.add(cancelButton); cancelButton.addActionListener(e -> { fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); id.dispose(); }); acceptButton.addActionListener(e -> { boolean accept = DriverService.acceptRide(ride.userId.getLong()); callSerially(() -> { if(accept) { id.dispose(); pickUpPassenger(pathObject, ride, fromComponent, toComponent); } else { id.dispose(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); getAnimationManager().flushAnimation(() -> ToastBar.showErrorMessage("Failed to grab ride")); } }); }); id.show(getHeight() - id.getPreferredH(), 0, 0, 0); onShowRideResponse This is essentially identical to the code we have for canceling the ride
  • 14. private Location toLocation(Coord crd) { return new Location(crd.getLatitude(), crd.getLongitude()); } toLocation For completeness the toLocation method is this. It converts Coord objects to Location objects
  • 15. public void pickUpPassenger(MapContainer.MapObject pathObject, Ride ride, Component fromComponent, Component toComponent) { InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Picked Up", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.add(acceptButton); id.add(cancelButton); acceptButton.addActionListener(e -> { DriverService.startRide(); id.dispose(); InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y()); dlg.add(new Label(ride.name.get(), "RideTitle")); Button finishButton = new Button("Finished Ride", "BlackButton"); dlg.add(finishButton); finishButton.addActionListener(ee -> { DriverService.finishRide(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); dlg.dispose(); }); dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0); }); cancelButton.addActionListener(e -> { fromComponent.remove(); pickUpPassenger Now we can proceed to the pickUpPassenger method where we handle the rest of the ride. This is again a pretty standard approach by now I'm creating another dialog with the Picked up button as the accept option
  • 16. public void pickUpPassenger(MapContainer.MapObject pathObject, Ride ride, Component fromComponent, Component toComponent) { InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Picked Up", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.add(acceptButton); id.add(cancelButton); acceptButton.addActionListener(e -> { DriverService.startRide(); id.dispose(); InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y()); dlg.add(new Label(ride.name.get(), "RideTitle")); Button finishButton = new Button("Finished Ride", "BlackButton"); dlg.add(finishButton); finishButton.addActionListener(ee -> { DriverService.finishRide(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); dlg.dispose(); }); dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0); }); cancelButton.addActionListener(e -> { fromComponent.remove(); pickUpPassenger The Accept button leads us to the last step which is the Finish button
  • 17. public void pickUpPassenger(MapContainer.MapObject pathObject, Ride ride, Component fromComponent, Component toComponent) { InteractionDialog id = new InteractionDialog("Pick Up", BoxLayout.y()); id.add(new Label(ride.name.get(), "RideTitle")); Button acceptButton = new Button("Picked Up", "BlackButton"); Button cancelButton = new Button("Cancel", "BlackButton"); id.add(acceptButton); id.add(cancelButton); acceptButton.addActionListener(e -> { DriverService.startRide(); id.dispose(); InteractionDialog dlg = new InteractionDialog("Driving...", BoxLayout.y()); dlg.add(new Label(ride.name.get(), "RideTitle")); Button finishButton = new Button("Finished Ride", "BlackButton"); dlg.add(finishButton); finishButton.addActionListener(ee -> { DriverService.finishRide(); fromComponent.remove(); toComponent.remove(); mc.removeMapObject(pathObject); dlg.dispose(); }); dlg.show(getHeight() - dlg.getPreferredH(), 0, 0, 0); }); cancelButton.addActionListener(e -> { fromComponent.remove(); pickUpPassenger Once we picked up a passenger we can only finish the ride, not cancel it. Since I didn't integrate any billing I think it makes sense. With billing we'll obviously need a way to pay and refund a ride
  • 18. private MapContainer.MapObject addPath(List<Coord> path, Component fromComponent, Component toComponent, int duration) { Coord[] pathCoords = new Coord[path.size()]; path.toArray(pathCoords); MapContainer.MapObject pathObject = mc.addPath(pathCoords); BoundingBox bb = BoundingBox.create(pathCoords). extend(new BoundingBox(pathCoords[0], 0.01, 0.01)). extend(new BoundingBox(pathCoords[pathCoords.length-1], 0.01, 0.01)); mc.fitBounds(bb); MapLayout.setHorizontalAlignment(fromComponent, MapLayout.HALIGN.RIGHT); mapLayer.add(pathCoords[0], fromComponent); mapLayer.add(pathCoords[pathCoords.length - 1], toComponent); return pathObject; } addPath I used the addPath method in the onShowRideResponse so I’m showing it here for completeness. The addPath method generalizes some of the common code for adding a path and zooming into the map. I won't go into the details of this method as all the code in here existed in enterNavigationMode so it should be pretty familiar.
  • 19. private void hailRideImpl(User car, final Container pinLayer) { pinLayer.getUnselectedStyle().setBgTransparency(0); pinLayer.removeAll(); String driverName = car.givenName.get(); String carBrand = car.car.get(); SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand); Container stars = new Container(new FlowLayout(CENTER)); for(int iter = 0 ; iter < 5 ; iter++) { if(iter + 1 >= car.currentRating.getFloat()) { Label fullStar = new Label("", "Star"); FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR); stars.add(fullStar); } else { if(iter + 1 >= Math.round(car.currentRating.getFloat())) { Label halfStar = new Label("", "Star"); FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF); stars.add(halfStar); } else { break; } } } Button ok = new Button("OK", "BlackButton"); Container dialog = BoxLayout.encloseY(driver, stars, ok); dialog.setUIID("SearchingDialog"); pinLayer.add(SOUTH, dialog); revalidate(); hailRideImpl With that lets move back to the “end user experience”. We discussed the perception from the driver side but what does the passenger see during this whole process? That’s exactly what hailRideImpl does… Both fields include no actual data currently since we don't have the UI to fill in the driver or user names
  • 20. private void hailRideImpl(User car, final Container pinLayer) { pinLayer.getUnselectedStyle().setBgTransparency(0); pinLayer.removeAll(); String driverName = car.givenName.get(); String carBrand = car.car.get(); SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand); Container stars = new Container(new FlowLayout(CENTER)); for(int iter = 0 ; iter < 5 ; iter++) { if(iter + 1 >= car.currentRating.getFloat()) { Label fullStar = new Label("", "Star"); FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR); stars.add(fullStar); } else { if(iter + 1 >= Math.round(car.currentRating.getFloat())) { Label halfStar = new Label("", "Star"); FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF); stars.add(halfStar); } else { break; } } } Button ok = new Button("OK", "BlackButton"); Container dialog = BoxLayout.encloseY(driver, stars, ok); dialog.setUIID("SearchingDialog"); pinLayer.add(SOUTH, dialog); revalidate(); hailRideImpl We build the text and add stars based on the level of ranking, this is essentially a set of star labels in a container based on the value of the rank field
  • 21. private void hailRideImpl(User car, final Container pinLayer) { pinLayer.getUnselectedStyle().setBgTransparency(0); pinLayer.removeAll(); String driverName = car.givenName.get(); String carBrand = car.car.get(); SpanLabel driver = new SpanLabel("Driver found " + driverName + "n" + carBrand); Container stars = new Container(new FlowLayout(CENTER)); for(int iter = 0 ; iter < 5 ; iter++) { if(iter + 1 >= car.currentRating.getFloat()) { Label fullStar = new Label("", "Star"); FontImage.setMaterialIcon(fullStar, FontImage.MATERIAL_STAR); stars.add(fullStar); } else { if(iter + 1 >= Math.round(car.currentRating.getFloat())) { Label halfStar = new Label("", "Star"); FontImage.setMaterialIcon(halfStar, FontImage.MATERIAL_STAR_HALF); stars.add(halfStar); } else { break; } } } Button ok = new Button("OK", "BlackButton"); Container dialog = BoxLayout.encloseY(driver, stars, ok); dialog.setUIID("SearchingDialog"); pinLayer.add(SOUTH, dialog); revalidate(); hailRideImpl We call this a "dialog" but it's really just a Container with a special style like before
  • 22. Star © Codename One 2017 all rights reserved In order to produce these stars I need a Star UIID which is suitably yellow and transparent
  • 23. Star © Codename One 2017 all rights reserved I reduced the padding on the sides so the stars remain close to one another
  • 24. Star © Codename One 2017 all rights reserved Margin however, is still 0
  • 25. Star © Codename One 2017 all rights reserved The font is large so the stars appear large