Use the reverse geocoding service within Google Play Services to take a Lat/Long value and turn it into a real physical address using an IntentService and the Geocoder class
- [Voiceover] So far, we've been mainly working with latitudes and longitudes, which are interesting, but have limited usefulness by themselves. Where things get really interesting is when you combine coordinate location data with geocoding, which is the process of turning location coordinates into physical address and places. In this example, we'll see how to use the Geocoder class to convert location coordinate data into a physical address. You should complete this example before moving to the next one, because the next exercise builds upon the work in this one.
First, let's start by looking at the Geocoder class, itself. The first important method to know about is the isPresent method, which is a static class function that you can use to determine if the device has access to a Geocoder. You can use this method to enable geocoding features in your application, and you should always check to see if a geocoder is present before you try to use it. Next, there is the getFromLocation method, which takes a set of coordinates and a desired number of maximum results, and returns a list of matching addresses.
Then there are a couple of versions of getFromLocationName, which returns a set of addresses that match a named location. To learn more about the Geocoder class, you can refer to this URL in the Android Developer documentation. All right, let's switch over to the code. In Android Studio, let's open up the address location example, and that's in our Exercise Files in Chapter 2, and we'll open Address Location, and we'll give that a moment to build.
All right. And then, in the Project Explorer, let's open up the Apps Manifest file, and here you can see that we request the, by now familiar, FINE_LOCATION permission, because this example will use the last known location to map to an address. We also need to define a service to handle the geocoding request. This is necessary because the geocoding process can take some time, and we don't want to block the UI thread in our app, and I'll cover this more a bit later, but you can see here, I've defined a service in my app called GeocodeService.
All right? Now let's take a look at the layout, so in the red section, let's open the main activity layout, and let's change that to API 23 on our 6P. Okay. So you can see that there's a text field that will be used to display the address lookup, and a button that will be used to trigger the process. So nothing too fancy here, and we'll use these controls in the next exercise, so ignore them for now. All right, let's take a look at the Java code for the app.
So let's open up our Java folder. The Constants.java file contains a class that holds a set of constant values that the example will use, and we'll see these used later in the app. There are some result codes and some strings that are used to pass data between the activity and the service. All right. Now let's open the MainActivity.java file, and most of this code should look familiar by now. With the life cycle functions for the activity and the Google Place services connection, so here, at the top of my activity class, I have a member variable that refers to an AddressReceiver.
This object will be used to obtain the address lookup from the geocoding service, and this class, AddressReceiver is defined down here, right here, okay, and you can see that it extends the base result ResultReceiver class. You can read more about the ResultReceiver class at this URL in the Android docs. It essentially provides a way to receive a callback result from elsewhere in your program, and before taking a look at the Geocode service class, let's take a moment to understand how the process will work.
Because the geocoding process is a blocking call, we want to perform this work off of the main UI thread so that our app stays responsive. Now it's beyond the scope of this course to go deeply into the various way to perform threading operations within Android. What I'm going to explain here is one potential way of moving intensive work off of the main UI thread. In this example, I'm going to use an intense service, which is the service that you saw earlier defined in our Manifest.XML file.
To do this, the code defines a Geocode service, which is my sub-class of Intense service. This code is going to run in its own thread. The main activity, which runs on the UI thread, will create a new Intent, set some parameters, including the AddressReceiver to store the result in, and then call startService to kick off the Geocode service. The service overrides a method called onHandleIntent, which will receive the intent that was created back in the activity.
The service then performs the address lookup, using the data in the Intent, and stores the result into the AddressReceiver that we gave it. The AddressReceiver object, back in the activity, then updates the UI with the result. You can read more about the IntentService class at this URL in the Android documentation. Okay, let's switch back to the code. In the MainActivity.java file, take another look at the AddressReceiver class. You can see that this class overrides the onReceiveResult method, which takes a result code and a Bundle of resultData.
This Bundle with contain the address that was found by the GeocodeService. And we just extract that data from the Bundle using one of the constants that we defined earlier and then update the UI. In the onCreate method, the code calls the getAddressFromLoc function when the user clicks the button. So let's go to that function, and here we are. In getAddressFromLoc, we get the last known location from the LocationServices API, which we've already seen how to do earlier, and if we get a valid location, then we need to trigger the GeocoderIntentService to perform the lookup.
So we create a new Intent, and then store some data into that Intent that the service is going to use. We set the Intent action to tell the service to look up an address from a location, which is a constant that I defined earlier. Then we store both the AddressReceiver object and the Location data into the Intent, and then, finally, the code calls startService to kick things off. Okay, now it's time to look at the GeocoderService.
So let's open that up. There are two function to look at here. So way down at the bottom is deliverResult, which takes a result code and a string, which will contain the found address, and stores them into the AddressReceiver. The send function is what triggers the onReceive result method back in the AddressReceiver class. Okay, let's scroll back up. The onHandleIntent function is where all of the geocoding work takes place.
This function is called when the service is started up, and the Intent that we created back in the MainActivity is passed to the service. The function starts by extracting the AddressReceiver from the Intent, along with whatever action we want to perform. And we do this because we're going to reuse this service in the next exercise. So, we need to be able to distinguish between the Location operation and the NamedAddress operation, which we're going to see later. A new Geocoder object is then created with the current Locale information, and there are some local variables for holding the Location and the resulting addresses.
Okay? Now we need to write our code to perform the geocoding process. So we're going to use the getFromLocation function to do this. It takes the latitude and longitude of the location, along with a desired maximum number of results. So right here, below the TODO, I'm going to write the code for this. So we'll write address = geocoder.getFromLocation, and we'll give it the location.getLatitude, and the location.getLongitude, and we only want to get one result.
Okay. So this function's going to return a list of addresses that are known to match that location. So this is going to come back with a list of addresses that are known to match that location, and this lookup process is a best guess estimate, and cannot be guaranteed to be accurate. It's just what the geocoder figures is at that location. So now, assuming that everything goes well, and no exceptions are thrown, the code will then flow down to this if statement.
So if we don't have any addresses to return, then we just deliver the error message. Otherwise, we get the first element of the resulting addresses list, and then we build up our address list by using various methods of the Address class, such as getAddressLine, getCountryName, getLocality, and so on. This strings then gets passed back to the MainActivity by the deliverResult function.
So, let's try it out. Let's go ahead and save, and I'm going to build, and I've got my emulator here, already running, so I'm going to go ahead and start the Maps application. Right? And, let's make sure that we've got a current location, so I'm going to click on the ellipses here, and I'm going to click the send button with the coordinates that are right there, which just happens to be the Google Plex. Okay, that looks good. All right.
So now, let's start our app up in the debugger, and I'll choose my emulator. All right, so it looks like everything succeeded and we're being prompted to allow our project to access the location, so we'll go ahead and click Allow, and we've already sent it a location, so let's go ahead and click on Get address from last location, and there you can see the address of the Google Plex. All right. Let's try it on a location. Let's bring our location settings dialog back up again, and let's type in, let's see, 40...
Oh, wait, actually, before we do this, let's go to the Maps app, and we'll bring up the Maps, because we want an application that's going to actually access the location, and we'll type in, let's see, 40.6892, and we'll type in, -74.0445, and we'll click Send. All right. And you can see that that's the Statue of Liberty, so now let's go back to our app.
Okay, there's our app, and once again, let's click on the button, and you can see the address of the Statue of Liberty. Okay, so, now we've seen how to retrieve an address via location coordinates. So we're going to build on this example by performing geocoding, given a named place, and we'll see how to do that in the next exercise.
Programming expert Joe Marini covers how to retrieve and work with a user's location and get continuous location updates with Location services; target and authenticate users and access their profile information with Google Sign-In; and work with the Google Drive API to store and retrieve content. Joe also shows you how to use the Google Places API to get information about places such as addresses, phone numbers, and website URLs, along with how to send messages to individual devices using Google Cloud Messaging. By the end of this course, you should be able to incorporate Google Play services into your own apps and take advantage of the various features it provides.
- Using Google Play services in your Android project
- How the Google Play services life cycle works
- Retrieving the user's location with Location services
- Working with location data
- Performing a geocoding operation
- Using Google Sign-In to access user information
- Storing information with the Google Drive API
- Working with the Google Places API
- Communicating via Google Cloud Messaging (GCM)