In this article, we will develop an Android application which facilitates users to tap two locations in the Google Map. The first tap point in the map will be the source of the route and the second tap point in the map will be the destination of the route. On taping the second point, a driving route will be drawn in the Google Map Android API V2
using Google Directions API
.
The output of the application is shown below

This application is developed in Android Studio (3.4.1).
1. Create New Android Project
Create a new Android project with Google Maps activity.
- Name : RouteBetweenTwoLocations
- Package name : com.wingsquare.RouteBetweenTwoLocations
- Language : JavaMinimum
- API Level : 15
- Ensure to tick the checkbox
Use androidx.* artifacts
2. Get Google Maps Api key
Open the file google_maps_api.xml
available in the values folder and follow the link provided in it. At the end, we will get an API key and that can be copied to YOUR_KEY_HERE of google_maps_api.xml file which is shown below :
<resources> <string name="google_maps_key" translatable="false" templateMergeStrategy="preserve"> YOUR_KEY_HERE </string> </resources>
3. Run the application
Using Android Studio, run the application in an emulator or in a real device to check whether the map is loading correctly or not. If success, we will get a screen as shown below:

4. Enable Google Directions API
In step2, we got Api key from project which is automatically created in Google cloud platform. In the same project, enable Google Directions API under APIs menu.
5. Create a new class DirectionsJSONParser
Create the class DirectionsJSONParser in the file app/src/main/java/com/wingsquare/routebetweentwolocations/DirectionsJSONParser.java. This class is used to parse the data obtained from Google’s Directions Api.
package com.wingsquare.routebetweentwolocations; import com.google.android.gms.maps.model.LatLng; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class DirectionsJSONParser { /** Receives a JSONObject and returns a list of lists containing latitude and longitude */ public List<List<HashMap<String,String>>> parse(JSONObject jObject){ List<List<HashMap<String, String>>> routes = new ArrayList<>() ; JSONArray jRoutes = null; JSONArray jLegs = null; JSONArray jSteps = null; try { jRoutes = jObject.getJSONArray("routes"); /** Traversing all routes */ for(int i=0;i<jRoutes.length();i++){ jLegs = ( (JSONObject)jRoutes.get(i)).getJSONArray("legs"); List path = new ArrayList<HashMap<String, String>>(); /** Traversing all legs */ for(int j=0;j<jLegs.length();j++){ jSteps = ( (JSONObject)jLegs.get(j)).getJSONArray("steps"); /** Traversing all steps */ for(int k=0;k<jSteps.length();k++){ String polyline = ""; polyline = (String)((JSONObject)((JSONObject)jSteps.get(k)).get("polyline")).get("points"); List<LatLng> list = decodePoly(polyline); /** Traversing all points */ for(int l=0;l<list.size();l++){ HashMap<String, String> hm = new HashMap<String, String>(); hm.put("lat", Double.toString(((LatLng)list.get(l)).latitude) ); hm.put("lng", Double.toString(((LatLng)list.get(l)).longitude) ); path.add(hm); } } routes.add(path); } } } catch (JSONException e) { e.printStackTrace(); }catch (Exception e){ } return routes; } /** * Method to decode polyline points * Courtesy : jeffreysambells.com/2010/05/27/decoding-polylines-from-google-maps-direction-api-with-java * */ private List<LatLng> decodePoly(String encoded) { List<LatLng> poly = new ArrayList<LatLng>(); int index = 0, len = encoded.length(); int lat = 0, lng = 0; while (index < len) { int b, shift = 0, result = 0; do { b = encoded.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lat += dlat; shift = 0; result = 0; do { b = encoded.charAt(index++) - 63; result |= (b & 0x1f) << shift; shift += 5; } while (b >= 0x20); int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); lng += dlng; LatLng p = new LatLng((((double) lat / 1E5)), (((double) lng / 1E5))); poly.add(p); } return poly; } }
6. Update MapsActivity.java
package com.wingsquare.routebetweentwolocations; import androidx.fragment.app.FragmentActivity; import android.Manifest; import android.content.pm.PackageManager; import android.graphics.Color; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.Polyline; import com.google.android.gms.maps.model.PolylineOptions; import org.json.JSONObject; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; public class MapsActivity extends FragmentActivity implements OnMapReadyCallback{ private GoogleMap mMap; private LatLng mOrigin; private LatLng mDestination; private Polyline mPolyline; ArrayList<LatLng> mMarkerPoints; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_maps); // Obtain the SupportMapFragment and get notified when the map is ready to be used. SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); mMarkerPoints = new ArrayList<>(); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() { @Override public void onMapClick(LatLng point) { // Already two locations if(mMarkerPoints.size()>1){ mMarkerPoints.clear(); mMap.clear(); } // Adding new item to the ArrayList mMarkerPoints.add(point); // Creating MarkerOptions MarkerOptions options = new MarkerOptions(); // Setting the position of the marker options.position(point); /** * For the start location, the color of marker is GREEN and * for the end location, the color of marker is RED. */ if(mMarkerPoints.size()==1){ options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)); }else if(mMarkerPoints.size()==2){ options.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)); } // Add new marker to the Google Map Android API V2 mMap.addMarker(options); // Checks, whether start and end locations are captured if(mMarkerPoints.size() >= 2){ mOrigin = mMarkerPoints.get(0); mDestination = mMarkerPoints.get(1); drawRoute(); } } }); } private void drawRoute(){ // Getting URL to the Google Directions API String url = getDirectionsUrl(mOrigin, mDestination); DownloadTask downloadTask = new DownloadTask(); // Start downloading json data from Google Directions API downloadTask.execute(url); } private String getDirectionsUrl(LatLng origin,LatLng dest){ // Origin of route String str_origin = "origin="+origin.latitude+","+origin.longitude; // Destination of route String str_dest = "destination="+dest.latitude+","+dest.longitude; // Key String key = "key=" + getString(R.string.google_maps_key); // Building the parameters to the web service String parameters = str_origin+"&"+str_dest+"&"+key; // Output format String output = "json"; // Building the url to the web service String url = "https://maps.googleapis.com/maps/api/directions/"+output+"?"+parameters; return url; } /** A method to download json data from url */ private String downloadUrl(String strUrl) throws IOException { String data = ""; InputStream iStream = null; HttpURLConnection urlConnection = null; try{ URL url = new URL(strUrl); // Creating an http connection to communicate with url urlConnection = (HttpURLConnection) url.openConnection(); // Connecting to url urlConnection.connect(); // Reading data from url iStream = urlConnection.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(iStream)); StringBuffer sb = new StringBuffer(); String line = ""; while( ( line = br.readLine()) != null){ sb.append(line); } data = sb.toString(); br.close(); }catch(Exception e){ Log.d("Exception on download", e.toString()); }finally{ iStream.close(); urlConnection.disconnect(); } return data; } /** A class to download data from Google Directions URL */ private class DownloadTask extends AsyncTask<String, Void, String> { // Downloading data in non-ui thread @Override protected String doInBackground(String... url) { // For storing data from web service String data = ""; try{ // Fetching the data from web service data = downloadUrl(url[0]); Log.d("DownloadTask","DownloadTask : " + data); }catch(Exception e){ Log.d("Background Task",e.toString()); } return data; } // Executes in UI thread, after the execution of // doInBackground() @Override protected void onPostExecute(String result) { super.onPostExecute(result); ParserTask parserTask = new ParserTask(); // Invokes the thread for parsing the JSON data parserTask.execute(result); } } /** A class to parse the Google Directions in JSON format */ private class ParserTask extends AsyncTask<String, Integer, List<List<HashMap<String,String>>> >{ // Parsing the data in non-ui thread @Override protected List<List<HashMap<String, String>>> doInBackground(String... jsonData) { JSONObject jObject; List<List<HashMap<String, String>>> routes = null; try{ jObject = new JSONObject(jsonData[0]); DirectionsJSONParser parser = new DirectionsJSONParser(); // Starts parsing data routes = parser.parse(jObject); }catch(Exception e){ e.printStackTrace(); } return routes; } // Executes in UI thread, after the parsing process @Override protected void onPostExecute(List<List<HashMap<String, String>>> result) { ArrayList<LatLng> points = null; PolylineOptions lineOptions = null; // Traversing through all the routes for(int i=0;i<result.size();i++){ points = new ArrayList<LatLng>(); lineOptions = new PolylineOptions(); // Fetching i-th route List<HashMap<String, String>> path = result.get(i); // Fetching all the points in i-th route for(int j=0;j<path.size();j++){ HashMap<String,String> point = path.get(j); double lat = Double.parseDouble(point.get("lat")); double lng = Double.parseDouble(point.get("lng")); LatLng position = new LatLng(lat, lng); points.add(position); } // Adding all the points in the route to LineOptions lineOptions.addAll(points); lineOptions.width(8); lineOptions.color(Color.RED); } // Drawing polyline in the Google Map for the i-th route if(lineOptions != null) { if(mPolyline != null){ mPolyline.remove(); } mPolyline = mMap.addPolyline(lineOptions); }else Toast.makeText(getApplicationContext(),"No route is found", Toast.LENGTH_LONG).show(); } } }
7. Run the application
Run the application from Android studio. Then we will get a screen as shown in the screen below :

8. Download the source
Source code for this application is available at https://github.com/Wingsquare/RouteBetweenTwoLocations
The app crashes as soon as I select both the locations. I am unable to locate the source of the crash. I have turned the Directions API and Places API for the key used. And I have replicated the code exactly as provided. But unable to achieve the shown output.
Please check logcat to get some clues.