Saturday, June 25, 2011

How to show turn by turn directions on Android

As you can probably make out, these days I am working on an Android project.  And I will tell you one thing, I am loving it!
I have worked on iOS, BREW and Simbian OS in the past but the productivity, while developing on Android is far better than the other platforms!

Excellent job Android guys!

Coming back to the post topic, recently I in one of the Android projects, we had a requirement that we wanted to show turn by turn directions between two geo points.

We are all familier with the excellent support for Turn by turn directions on the native Google Maps application.

Although, Android API permits us embed a maps view in the application, but it does not provide a way to show turn by turn directions between two geo points.

In early days of Android (SDK 1.0), Google had provided the DrivingDirections class.  Using this class it was possible to get the driving directions between two geo points.  However, since SDK 1.1, Google removed the DrivingDirections class from the API. 

Why would they remove something as useful as this?

Because it violated some of the legal agreements made by Google.  Extract from the Google Maps terms states that
8.3 Content License. Subject to these Terms (including but not limited to Section 9 (License Requirements) and Section 10 (License Restrictions)), Google gives you a personal, worldwide, royalty-free, non-transferable, non-assignable, and non-exclusive license to access, use, publicly perform and publicly display the Content in your Maps API Implementation, as the Content is provided in the Service, and in the manner permitted by the Terms. Specifically, you understand the following:


(a) Content (including but not limited to map data, traffic, directions, and places) is provided for planning purposes only. You may find that weather conditions, construction projects, closures, or other events may cause road conditions or directions to differ from the results depicted in the Content. You should exercise judgment in your use of the Content.
(b) Certain Content is provided under license from third parties, including Tele Atlas B.V. ("Tele Atlas"), and is subject to copyright and other intellectual property rights owned by or licensed to Tele Atlas and/or such third parties. You may be held liable for any unauthorized copying or disclosure of this content. Your use of Tele Atlas map data and certain other Content (including certain business listings Content) is subject to additional restrictions located in the Legal Notices page.

 In simple terms, Google has tied up with many Third party companies to get the mapping and other information.  The terms of the agreement do not allow them to expose those functionality in an open source project.  This is the reason why they removed the DrivingDirections API since SDK 1.1

All right fine!  Google could not expose that as API's, but we sill have this requirement to fulfill.  We want to show turn by turn directions between two geo points on Android platform.

How do we do it?

As we all know that the native Google Maps application (which comes pre-installed on all Android devices) does a great job of showing Turn by Turn driving directions.  The updated application also provides a link to Google Navigate (beta) which shows 3D maps and updates the route as the user location changes.

We should definitely try and make use of this feature for our requirement.  After all, why reinvent the wheel!

Hence now, our tasks are:
  • To invoke the native google maps application
  • Show turn by turn directions between two geo points on the native google maps application
One very big plus of the Android platform is that, it has multitasking support.  Because of this, when user clicks the back button on the phone, they will come back to our application, user does not feel they are navigating away from our application.  This enhances the user experience quite a lot!

So the questions is how do we invoke the native Google Maps application to show turn by turn direction between two geo points!

Inter process/activity communication on Android platform is done via an Intent.  Hence the answer to our question is: Via an Intent!

Yes and that's the correct answer!  But what Intent, what will be the URL, what should be the parameters?

Slow down, slow down, lets tackle one problem at a time!

This is how they do it!

Looking at this URL it seems that we can invoke the native maps application using the following URLs

One line description for the above url's is
Opens the Maps application to the given location or query. The Geo URI scheme (not fully supported) is currently under development.
The z field specifies the zoom level. A zoom level of 1 shows the whole Earth, centered at the given lat,lng. A zoom level of 2 shows a quarter of the Earth, and so on. The highest zoom level is 23. A larger zoom level will be clamped to 23.
Basically, the description says that, The above URLs will show the maps application which is centered around the given geo point

Hence,

This code will actuall open up the native map application which is centered around the given geo point. We have solved one task from our task list. We have managed to invoke the native google maps application!

But wait!  We want to show two points not just one point on the map.  We also want to show turn by turn directions between those points!

Using the URL i.e. the geo:x:y in the above format, we cannot specify two geo points.  Damn!

I tried many permutations & combinations to specify the second geo point, but nothing worked.

Finally, after a lot of trials and errors I found a workaround to this problem.

This is how its actually done!

The URL geo:x,y does not support the second geo point, hence we can't use that URL.  What is the other URL we could use, which will show turn by turn directions between two geo points?

After some more googling, I found that, we might be able to use the URL format used by  maps.google.com website.  The maps.google.com follows a simple url pattern

The maps.google.com uses two parameters "saddr" (source address) and "daddr" (destination address). If we pass those two parameters correctly it shows, turn by turn directions between those points.
Web Based Turn-By-Turn Directions on Google Maps

Why are we even bothered about viewing turn by turn directions on the web?

Because guess what, we can use the same web based URL pattern on the Android platform!

What?  Yes, we can use the URL http://maps.google.com/maps?saddr=43.0054446,-87.9678884&daddr=42.9257104,-88.0508355 to open up the Google Maps application to see turn by turn directions between two geo points!

The above code surely works! It shows the user, turn by turn directions between the geo points [43.0054446,-87.9678884] and [42.9257104,-88.0508355].

There is only one problem though, Android platform identifies that the above URL can be processed by two Activities!
  • The Browser Activity - Since the URL starts with http:// 
  • The Google Maps Activity - Since the address we are opening up is maps.google.com
Whenever Android platform has more than one options to process an intent, it leaves the choice to users, gives them an option of both the applications and lets them chose the application to fulfill the intent.
User gets an option to chose the application for the processing the Maps URL
User can chose either application, both show driving directions between those two geo points.

Driving directions using the browser application

Driving directions using the Native Google Maps application

How to make this solution better:

Although we have achieved what we intended for.  But there is one extra step that user has to perform, i.e. select the application of his choice to see the driving directions.  At this point, user might notice that he is going out of the current application and into a different application.  There is nothing wrong with this approach but, it does not integrate the Google Maps application seamlessly with our application.

How do we eliminate this extra step of choosing the application to view turn by turn directions?

The answer is simple and we can do this by providing a hint in the Intent we are firing.

The line intent.setClassName("com.google.android.apps.maps", "com.google.android.maps.MapsActivity"); does the trick. We are passing additional information in the Intent. We are informing that, the class name of the activity that should fulfill the intent is "com.google.android.apps.maps/com.google.android.maps.MapsActivity"

That's it! Now Android platform knows that, user wants to see the turn by turn directions on the native google maps application and not on the browser. The extra step of chosing the application is eliminated.

User directly goes to this view
There are many advantage of this approach
  • User can choose to start navigation using Google Navigate (beta) (without us doing anything).
  • User can click back button and return to our application as well!
As we can see, Android platform is pretty awesome, when it comes to ease of development!  Something as complex as showing turn by turn driving directions between two geo points can be achieved by writing 3 lines of code!
Have some Fun!