am 83f40802: am 95226c98: am 912cacde: Merge "docs: Adds new geocoding sample to Location API training. Uses GoogleApiClient to connect to Play services. Uses the latest Fused Location Provider API." into lmp-docs
* commit '83f40802148e922d78222ffc56377534b8ee92be': docs: Adds new geocoding sample to Location API training. Uses GoogleApiClient to connect to Play services. Uses the latest Fused Location Provider API.
This commit is contained in:
@@ -1,280 +1,468 @@
|
|||||||
page.title=Displaying a Location Address
|
page.title=Displaying a Location Address
|
||||||
|
|
||||||
trainingnavtop=true
|
trainingnavtop=true
|
||||||
|
|
||||||
@jd:body
|
@jd:body
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div id="tb-wrapper">
|
<div id="tb-wrapper">
|
||||||
<div id="tb">
|
<div id="tb">
|
||||||
|
|
||||||
<h2>This lesson teaches you to</h2>
|
<h2>This lesson teaches you how to</h2>
|
||||||
<ol>
|
<ol>
|
||||||
<li><a href="#DefineTask">Define the Address Lookup Task</a></li>
|
<li><a href="#connect">Get a Geographic Location</a></li>
|
||||||
<li><a href="#DisplayResults">Define a Method to Display the Results</a></li>
|
<li><a href="#fetch-address">Define an Intent Service to Fetch the
|
||||||
<li><a href="#RunTask">Run the Lookup Task</a></li>
|
Address</a></li>
|
||||||
</ol>
|
<li><a href="#start-intent">Start the Intent Service</a></li>
|
||||||
|
<li><a href="#result-receiver">Receive the Geocoding Results</a></li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<h2>You should also read</h2>
|
<h2>You should also read</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
|
<a href="{@docRoot}google/play-services/setup.html">Setting up Google
|
||||||
</li>
|
Play Services</a>
|
||||||
<li>
|
</li>
|
||||||
<a href="retrieve-current.html">Retrieving the Current Location</a>
|
<li>
|
||||||
</li>
|
<a href="retrieve-current.html">Getting the Last Known Location</a>
|
||||||
<li>
|
</li>
|
||||||
<a href="receive-location-updates.html">Receiving Location Updates</a>
|
<li>
|
||||||
</li>
|
<a href="receive-location-updates.html">Receiving Location Updates</a>
|
||||||
</ul>
|
</li>
|
||||||
<h2>Try it out</h2>
|
</ul>
|
||||||
|
<h2>Try it out</h2>
|
||||||
|
|
||||||
<div class="download-box">
|
<ul>
|
||||||
<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download
|
<li>
|
||||||
the sample app</a>
|
<a href="https://github.com/googlesamples/android-play-location/tree/master/LocationAddress" class="external-link">LocationAddress</a>
|
||||||
<p class="filename">LocationUpdates.zip</p>
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<p>The lessons <a href="retrieve-current.html">Getting the Last Known
|
||||||
</div>
|
Location</a> and <a href="receive-location-updates.html">Receiving Location
|
||||||
|
Updates</a> describe how to get the user's location in the form of a
|
||||||
|
{@link android.location.Location} object that contains latitude and longitude
|
||||||
|
coordinates. Although latitude and longitude are useful for calculating
|
||||||
|
distance or displaying a map position, in many cases the address of the
|
||||||
|
location is more useful. For example, if you want to let your users know where
|
||||||
|
they are or what is close by, a street address is more meaningful than the
|
||||||
|
geographic coordinates (latitude/longitude) of the location.</p>
|
||||||
|
|
||||||
|
<p>Using the {@link android.location.Geocoder} class in the Android framework
|
||||||
|
location APIs, you can convert an address to the corresponding geographic
|
||||||
|
coordinates. This process is called <em>geocoding</em>. Alternatively, you can
|
||||||
|
convert a geographic location to an address. The address lookup feature is
|
||||||
|
also known as <em>reverse geocoding</em>.</p>
|
||||||
|
|
||||||
|
<p>This lesson shows you how to use the
|
||||||
|
{@link android.location.Geocoder#getFromLocation getFromLocation()} method to
|
||||||
|
convert a geographic location to an address. The method returns an estimated
|
||||||
|
street address corresponding to a given latitude and longitude.</p>
|
||||||
|
|
||||||
|
<h2 id="connect">Get a Geographic Location</h2>
|
||||||
|
|
||||||
|
<p>The last known location of the device is a useful starting point for the
|
||||||
|
address lookup feature. The lesson on
|
||||||
|
<a href="retrieve-current.html">Getting the Last Known Location</a> shows you
|
||||||
|
how to use the
|
||||||
|
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>
|
||||||
|
method provided by the
|
||||||
|
<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused
|
||||||
|
location provider</a> to find the latest location of the device.</p>
|
||||||
|
|
||||||
|
<p>To access the fused location provider, you need to create an instance of the
|
||||||
|
Google Play services API client. To learn how to connect your client, see
|
||||||
|
<a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect
|
||||||
|
to Google Play Services</a>.</p>
|
||||||
|
|
||||||
|
<p>In order for the fused location provider to retrieve a precise street
|
||||||
|
address, set the location permission in your app manifest to
|
||||||
|
{@code ACCESS_FINE_LOCATION}, as shown in the following example:</p>
|
||||||
|
|
||||||
<p>
|
|
||||||
The lessons <a href="retrieve-current.html">Retrieving the Current Location</a> and
|
|
||||||
<a href="receive-location-updates.html">Receiving Location Updates</a> describe how to get the
|
|
||||||
user's current location in the form of a {@link android.location.Location} object that
|
|
||||||
contains latitude and longitude coordinates. Although latitude and longitude are useful for
|
|
||||||
calculating distance or displaying a map position, in many cases the address of the location is
|
|
||||||
more useful.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The Android platform API provides a feature that returns an estimated street addresses for
|
|
||||||
latitude and longitude values. This lesson shows you how to use this address lookup feature.
|
|
||||||
</p>
|
|
||||||
<p class="note">
|
|
||||||
<strong>Note:</strong> Address lookup requires a backend service that is not included in the
|
|
||||||
core Android framework. If this backend service is not available,
|
|
||||||
{@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()} returns an empty
|
|
||||||
list. The helper method {@link android.location.Geocoder#isPresent isPresent()}, available
|
|
||||||
in API level 9 and later, checks to see if the backend service is available.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The snippets in the following sections assume that your app has already retrieved the
|
|
||||||
current location and stored it as a {@link android.location.Location} object in the global
|
|
||||||
variable {@code mLocation}.
|
|
||||||
</p>
|
|
||||||
<!--
|
|
||||||
Define the address lookup task
|
|
||||||
-->
|
|
||||||
<h2 id="DefineTask">Define the Address Lookup Task</h2>
|
|
||||||
<p>
|
|
||||||
To get an address for a given latitude and longitude, call
|
|
||||||
{@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()}, which returns a
|
|
||||||
list of addresses. The method is synchronous, and may take a long time to do its work, so you
|
|
||||||
should call the method from the {@link android.os.AsyncTask#doInBackground
|
|
||||||
doInBackground()} method of an {@link android.os.AsyncTask}.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
While your app is getting the address, display an indeterminate activity
|
|
||||||
indicator to show that your app is working in the background. Set the indicator's initial state
|
|
||||||
to {@code android:visibility="gone"}, to make it invisible and remove it from the layout
|
|
||||||
hierarchy. When you start the address lookup, you set its visibility to "visible".
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
The following snippet shows how to add an indeterminate {@link android.widget.ProgressBar} to
|
|
||||||
your layout file:
|
|
||||||
</p>
|
|
||||||
<pre>
|
<pre>
|
||||||
<ProgressBar
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:id="@+id/address_progress"
|
package="com.google.android.gms.location.sample.locationupdates" >
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||||
android:layout_centerHorizontal="true"
|
</manifest>
|
||||||
android:indeterminate="true"
|
|
||||||
android:visibility="gone" />
|
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
|
||||||
To create the background task, define a subclass of {@link android.os.AsyncTask} that calls
|
<h2 id="fetch-address">Define an Intent Service to Fetch the Address</h2>
|
||||||
{@link android.location.Geocoder#getFromLocation getFromLocation()} and returns an address.
|
|
||||||
Define a {@link android.widget.TextView} object {@code mAddress} to contain the returned
|
<p>The {@link android.location.Geocoder#getFromLocation getFromLocation()}
|
||||||
address, and a {@link android.widget.ProgressBar} object that allows you to control the
|
method provided by the {@link android.location.Geocoder} class accepts a
|
||||||
indeterminate activity indicator. For example:
|
latitude and longitude, and returns a list of addresses. The method is
|
||||||
</p>
|
synchronous, and may take a long time to do its work, so you should not call
|
||||||
|
it from the main, user interface (UI) thread of your app.</p>
|
||||||
|
|
||||||
|
<p>The {@link android.app.IntentService IntentService} class provides a
|
||||||
|
structure for running a task on a background thread. Using this class, you can
|
||||||
|
handle a long-running operation without affecting your UI's responsiveness.
|
||||||
|
Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to
|
||||||
|
perform background operations, but it's designed for short operations. An
|
||||||
|
{@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if
|
||||||
|
the activity is recreated, for example when the device is rotated. In
|
||||||
|
contrast, an {@link android.app.IntentService IntentService} doesn't need to
|
||||||
|
be cancelled when the activity is rebuilt.</p>
|
||||||
|
|
||||||
|
<p>Define a {@code FetchAddressIntentService} class that extends
|
||||||
|
{@link android.app.IntentService}. This class is your address lookup service.
|
||||||
|
The intent service handles an intent asynchronously on a worker thread, and
|
||||||
|
stops itself when it runs out of work. The intent extras provide the data
|
||||||
|
needed by the service, including a {@link android.location.Location} object
|
||||||
|
for conversion to an address, and a {@link android.os.ResultReceiver} object
|
||||||
|
to handle the results of the address lookup. The service uses a {@link
|
||||||
|
android.location.Geocoder} to fetch the address for the location, and sends
|
||||||
|
the results to the {@link android.os.ResultReceiver}.</p>
|
||||||
|
|
||||||
|
<h3>Define the Intent Service in your App Manifest</h3>
|
||||||
|
|
||||||
|
<p>Add an entry to your app manifest defining the intent service:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
public class MainActivity extends FragmentActivity {
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.google.android.gms.location.sample.locationaddress" >
|
||||||
|
<application
|
||||||
|
...
|
||||||
|
<service
|
||||||
|
android:name=".FetchAddressIntentService"
|
||||||
|
android:exported="false"/>
|
||||||
|
</application>
|
||||||
...
|
...
|
||||||
private TextView mAddress;
|
</manifest>
|
||||||
private ProgressBar mActivityIndicator;
|
</pre>
|
||||||
|
|
||||||
|
<p class="note"><strong>Note:</strong> The {@code <service>} element in
|
||||||
|
the manifest doesn't need to include an intent filter, because your main
|
||||||
|
activity creates an explicit intent by specifying the name of the class to use
|
||||||
|
for the intent.</p>
|
||||||
|
|
||||||
|
<h3>Create a Geocoder</h3>
|
||||||
|
|
||||||
|
<p>The process of converting a geographic location to an address is called
|
||||||
|
<em>reverse geocoding</em>. To perform the main work of the intent service,
|
||||||
|
that is, your reverse geocoding request, implement
|
||||||
|
{@link android.app.IntentService#onHandleIntent onHandleIntent()} within the
|
||||||
|
{@code FetchAddressIntentService} class. Create a
|
||||||
|
{@link android.location.Geocoder} object to handle the reverse geocoding.</p>
|
||||||
|
|
||||||
|
<p>A locale represents a specific geographical or linguistic region. Locale
|
||||||
|
objects are used to adjust the presentation of information, such as numbers or
|
||||||
|
dates, to suit the conventions in the region represented by the locale. Pass a
|
||||||
|
<a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object
|
||||||
|
to the {@link android.location.Geocoder} object, to ensure that the resulting
|
||||||
|
address is localized to the user's geographic region.</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
|
||||||
|
...
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="retrieve-street-address">Retrieve the street address data</h3>
|
||||||
|
|
||||||
|
<p>The next step is to retrieve the street address from the geocoder, handle
|
||||||
|
any errors that may occur, and send the results back to the activity that
|
||||||
|
requested the address. To report the results of the geocoding
|
||||||
|
process, you need two numeric constants that indicate success or failure.
|
||||||
|
Define a {@code Constants} class to contain the values, as shown in this code
|
||||||
|
snippet:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
public final class Constants {
|
||||||
|
public static final int SUCCESS_RESULT = 0;
|
||||||
|
public static final int FAILURE_RESULT = 1;
|
||||||
|
public static final String PACKAGE_NAME =
|
||||||
|
"com.google.android.gms.location.sample.locationaddress";
|
||||||
|
public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER";
|
||||||
|
public static final String RESULT_DATA_KEY = PACKAGE_NAME +
|
||||||
|
".RESULT_DATA_KEY";
|
||||||
|
public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
|
||||||
|
".LOCATION_DATA_EXTRA";
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>To get a street address corresponding to a geographical location, call
|
||||||
|
{@link android.location.Geocoder#getFromLocation getFromLocation()},
|
||||||
|
passing it the latitude and longitude from the location object, and the
|
||||||
|
maximum number of addresses you want returned. In this case, you want just one
|
||||||
|
address. The geocoder returns an array of addresses. If no addresses were
|
||||||
|
found to match the given location, it returns an empty list. If there is no
|
||||||
|
backend geocoding service available, the geocoder returns null.</p>
|
||||||
|
|
||||||
|
<p>Check for the following errors as shown in the code sample below. If an error
|
||||||
|
occurs, place the corresponding error message in the {@code errorMessage}
|
||||||
|
variable, so you can send it back to the requesting activity:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><strong>No location data provided</strong> - The intent extras do not
|
||||||
|
include the {@link android.location.Location} object required for reverse
|
||||||
|
geocoding.</li>
|
||||||
|
<li><strong>Invalid latitude or longitude used</strong> - The latitude
|
||||||
|
and/or longitude values provided in the {@link android.location.Location}
|
||||||
|
object are invalid.</li>
|
||||||
|
<li><strong>No geocoder available</strong> - The background geocoding service
|
||||||
|
is not available, due to a network error or IO exception.</li>
|
||||||
|
<li><strong>Sorry, no address found</strong> - The geocoder could not find an
|
||||||
|
address for the given latitude/longitude.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>To get the individual lines of an address object, use the
|
||||||
|
{@link android.location.Address#getAddressLine getAddressLine()}
|
||||||
|
method provided by the {@link android.location.Address} class. Then join the
|
||||||
|
lines into a list of address fragments ready to return to the activity that
|
||||||
|
requested the address.</p>
|
||||||
|
|
||||||
|
<p>To send the results back to the requesting activity, call the
|
||||||
|
{@code deliverResultToReceiver()} method (defined in
|
||||||
|
<a href="#return-address">Return the address to the requestor</a>). The
|
||||||
|
results consist of the previously-mentioned numeric success/failure code and
|
||||||
|
a string. In the case of a successful reverse geocoding, the string contains
|
||||||
|
the address. In the case of a failure, the string contains the error message,
|
||||||
|
as shown in the code sample below:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
@Override
|
||||||
|
protected void onHandleIntent(Intent intent) {
|
||||||
|
String errorMessage = "";
|
||||||
|
|
||||||
|
// Get the location passed to this service through an extra.
|
||||||
|
Location location = intent.getParcelableExtra(
|
||||||
|
Constants.LOCATION_DATA_EXTRA);
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
List<Address> addresses = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
addresses = geocoder.getFromLocation(
|
||||||
|
location.getLatitude(),
|
||||||
|
location.getLongitude(),
|
||||||
|
// In this sample, get just a single address.
|
||||||
|
1);
|
||||||
|
} catch (IOException ioException) {
|
||||||
|
// Catch network or other I/O problems.
|
||||||
|
errorMessage = getString(R.string.service_not_available);
|
||||||
|
Log.e(TAG, errorMessage, ioException);
|
||||||
|
} catch (IllegalArgumentException illegalArgumentException) {
|
||||||
|
// Catch invalid latitude or longitude values.
|
||||||
|
errorMessage = getString(R.string.invalid_lat_long_used);
|
||||||
|
Log.e(TAG, errorMessage + ". " +
|
||||||
|
"Latitude = " + location.getLatitude() +
|
||||||
|
", Longitude = " +
|
||||||
|
location.getLongitude(), illegalArgumentException);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case where no address was found.
|
||||||
|
if (addresses == null || addresses.size() == 0) {
|
||||||
|
if (errorMessage.isEmpty()) {
|
||||||
|
errorMessage = getString(R.string.no_address_found);
|
||||||
|
Log.e(TAG, errorMessage);
|
||||||
|
}
|
||||||
|
deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
|
||||||
|
} else {
|
||||||
|
Address address = addresses.get(0);
|
||||||
|
ArrayList<String> addressFragments = new ArrayList<String>();
|
||||||
|
|
||||||
|
// Fetch the address lines using {@code getAddressLine},
|
||||||
|
// join them, and send them to the thread.
|
||||||
|
for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
|
||||||
|
addressFragments.add(address.getAddressLine(i));
|
||||||
|
}
|
||||||
|
Log.i(TAG, getString(R.string.address_found));
|
||||||
|
deliverResultToReceiver(Constants.SUCCESS_RESULT,
|
||||||
|
TextUtils.join(System.getProperty("line.separator"),
|
||||||
|
addressFragments));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h3 id="return-address">Return the address to the requestor</h3>
|
||||||
|
|
||||||
|
<p>The final thing the intent service must do is send the address back to a
|
||||||
|
{@link android.os.ResultReceiver} in the activity that started the service.
|
||||||
|
The {@link android.os.ResultReceiver} class allows you to send a
|
||||||
|
numeric result code as well as a message containing the result data. The
|
||||||
|
numeric code is useful for reporting the success or failure of the geocoding
|
||||||
|
request. In the case of a successful reverse geocoding, the message contains
|
||||||
|
the address. In the case of a failure, the message contains some text
|
||||||
|
describing the reason for failure.</p>
|
||||||
|
|
||||||
|
<p>You have already retrieved the address from the geocoder, trapped any errors
|
||||||
|
that may occur, and called the {@code deliverResultToReceiver()} method. Now
|
||||||
|
you need to define the {@code deliverResultToReceiver()} method that sends
|
||||||
|
a result code and message bundle to the result receiver.</p>
|
||||||
|
|
||||||
|
<p>For the result code, use the value that you've passed to the
|
||||||
|
{@code deliverResultToReceiver()} method in the {@code resultCode} parameter.
|
||||||
|
To construct the message bundle, concatenate the {@code RESULT_DATA_KEY}
|
||||||
|
constant from your {@code Constants} class (defined in
|
||||||
|
<a href="#retrieve-street-address">Retrieve the street address data</a>) and
|
||||||
|
the value in the {@code message} parameter passed to the
|
||||||
|
{@code deliverResultToReceiver()} method, as shown in the following sample:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
public class FetchAddressIntentService extends IntentService {
|
||||||
|
protected ResultReceiver mReceiver;
|
||||||
|
...
|
||||||
|
private void deliverResultToReceiver(int resultCode, String message) {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putString(Constants.RESULT_DATA_KEY, message);
|
||||||
|
mReceiver.send(resultCode, bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<h2 id="start-intent">Start the Intent Service</h2>
|
||||||
|
|
||||||
|
<p>The intent service, as defined in the previous section, runs in the
|
||||||
|
background and is responsible for fetching the address corresponding to a
|
||||||
|
given geographic location. When you start the service, the Android framework
|
||||||
|
instantiates and starts the service if it isn't already running, and creates a
|
||||||
|
process if needed. If the service is already running then it remains running.
|
||||||
|
Because the service extends {@link android.app.IntentService IntentService},
|
||||||
|
it shuts down automatically when all intents have been processed.</p>
|
||||||
|
|
||||||
|
<p>Start the service from your app's main activity,
|
||||||
|
and create an {@link android.content.Intent} to pass data to the service. You
|
||||||
|
need an <em>explicit</em> intent, because you want only your service
|
||||||
|
to respond to the intent. For more information, see
|
||||||
|
<a href="{@docRoot}guide/components/intents-filters.html#Types">Intent
|
||||||
|
Types</a>.</p>
|
||||||
|
|
||||||
|
<p>To create an explicit intent, specify the name of the
|
||||||
|
class to use for the service: {@code FetchAddressIntentService.class}.
|
||||||
|
Pass two pieces of information in the intent extras:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>A {@link android.os.ResultReceiver} to handle the results of the address
|
||||||
|
lookup.</li>
|
||||||
|
<li>A {@link android.location.Location} object containing the latitude and
|
||||||
|
longitude that you want to convert to an address.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p>The following code sample shows you how to start the intent service:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
public class MainActivity extends ActionBarActivity implements
|
||||||
|
ConnectionCallbacks, OnConnectionFailedListener {
|
||||||
|
|
||||||
|
protected Location mLastLocation;
|
||||||
|
private AddressResultReceiver mResultReceiver;
|
||||||
|
...
|
||||||
|
|
||||||
|
protected void startIntentService() {
|
||||||
|
Intent intent = new Intent(this, FetchAddressIntentService.class);
|
||||||
|
intent.putExtra(Constants.RECEIVER, mResultReceiver);
|
||||||
|
intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
|
||||||
|
startService(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>Call the above {@code startIntentService()} method when the
|
||||||
|
user takes an action that requires a geocoding address lookup. For example,
|
||||||
|
the user may press a <em>Fetch address</em> button on your app's UI. Before
|
||||||
|
starting the intent service, you need to check that the connection to Google
|
||||||
|
Play services is present. The following code snippet shows the call to the
|
||||||
|
{@code startIntentService()} method in the button handler:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
public void fetchAddressButtonHandler(View view) {
|
||||||
|
// Only start the service to fetch the address if GoogleApiClient is
|
||||||
|
// connected.
|
||||||
|
if (mGoogleApiClient.isConnected() && mLastLocation != null) {
|
||||||
|
startIntentService();
|
||||||
|
}
|
||||||
|
// If GoogleApiClient isn't connected, process the user's request by
|
||||||
|
// setting mAddressRequested to true. Later, when GoogleApiClient connects,
|
||||||
|
// launch the service to fetch the address. As far as the user is
|
||||||
|
// concerned, pressing the Fetch Address button
|
||||||
|
// immediately kicks off the process of getting the address.
|
||||||
|
mAddressRequested = true;
|
||||||
|
updateUIWidgets();
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
|
||||||
|
<p>You must also start the intent service when the connection to Google Play
|
||||||
|
services is established, if the user has already clicked the button on your
|
||||||
|
app's UI. The following code snippet shows the call to the
|
||||||
|
{@code startIntentService()} method in the
|
||||||
|
<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
|
||||||
|
callback provided by the Google API Client:</p>
|
||||||
|
|
||||||
|
<pre>
|
||||||
|
public class MainActivity extends ActionBarActivity implements
|
||||||
|
ConnectionCallbacks, OnConnectionFailedListener {
|
||||||
...
|
...
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
public void onConnected(Bundle connectionHint) {
|
||||||
super.onCreate(savedInstanceState);
|
// Gets the best and most recent location currently available,
|
||||||
...
|
// which may be null in rare cases when a location is not available.
|
||||||
mAddress = (TextView) findViewById(R.id.address);
|
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
|
||||||
mActivityIndicator =
|
mGoogleApiClient);
|
||||||
(ProgressBar) findViewById(R.id.address_progress);
|
|
||||||
}
|
if (mLastLocation != null) {
|
||||||
...
|
// Determine whether a Geocoder is available.
|
||||||
/**
|
if (!Geocoder.isPresent()) {
|
||||||
* A subclass of AsyncTask that calls getFromLocation() in the
|
Toast.makeText(this, R.string.no_geocoder_available,
|
||||||
* background. The class definition has these generic types:
|
Toast.LENGTH_LONG).show();
|
||||||
* Location - A {@link android.location.Location} object containing
|
return;
|
||||||
* the current location.
|
|
||||||
* Void - indicates that progress units are not used
|
|
||||||
* String - An address passed to onPostExecute()
|
|
||||||
*/
|
|
||||||
private class GetAddressTask extends
|
|
||||||
AsyncTask<Location, Void, String> {
|
|
||||||
Context mContext;
|
|
||||||
public GetAddressTask(Context context) {
|
|
||||||
super();
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
...
|
|
||||||
/**
|
|
||||||
* Get a Geocoder instance, get the latitude and longitude
|
|
||||||
* look up the address, and return it
|
|
||||||
*
|
|
||||||
* @params params One or more Location objects
|
|
||||||
* @return A string containing the address of the current
|
|
||||||
* location, or an empty string if no address can be found,
|
|
||||||
* or an error message
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected String doInBackground(Location... params) {
|
|
||||||
Geocoder geocoder =
|
|
||||||
new Geocoder(mContext, Locale.getDefault());
|
|
||||||
// Get the current location from the input parameter list
|
|
||||||
Location loc = params[0];
|
|
||||||
// Create a list to contain the result address
|
|
||||||
List<Address> addresses = null;
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
* Return 1 address.
|
|
||||||
*/
|
|
||||||
addresses = geocoder.getFromLocation(loc.getLatitude(),
|
|
||||||
loc.getLongitude(), 1);
|
|
||||||
} catch (IOException e1) {
|
|
||||||
Log.e("LocationSampleActivity",
|
|
||||||
"IO Exception in getFromLocation()");
|
|
||||||
e1.printStackTrace();
|
|
||||||
return ("IO Exception trying to get address");
|
|
||||||
} catch (IllegalArgumentException e2) {
|
|
||||||
// Error message to post in the log
|
|
||||||
String errorString = "Illegal arguments " +
|
|
||||||
Double.toString(loc.getLatitude()) +
|
|
||||||
" , " +
|
|
||||||
Double.toString(loc.getLongitude()) +
|
|
||||||
" passed to address service";
|
|
||||||
Log.e("LocationSampleActivity", errorString);
|
|
||||||
e2.printStackTrace();
|
|
||||||
return errorString;
|
|
||||||
}
|
}
|
||||||
// If the reverse geocode returned an address
|
|
||||||
if (addresses != null && addresses.size() > 0) {
|
if (mAddressRequested) {
|
||||||
// Get the first address
|
startIntentService();
|
||||||
Address address = addresses.get(0);
|
|
||||||
/*
|
|
||||||
* Format the first line of address (if available),
|
|
||||||
* city, and country name.
|
|
||||||
*/
|
|
||||||
String addressText = String.format(
|
|
||||||
"%s, %s, %s",
|
|
||||||
// If there's a street address, add it
|
|
||||||
address.getMaxAddressLineIndex() > 0 ?
|
|
||||||
address.getAddressLine(0) : "",
|
|
||||||
// Locality is usually a city
|
|
||||||
address.getLocality(),
|
|
||||||
// The country of the address
|
|
||||||
address.getCountryName());
|
|
||||||
// Return the text
|
|
||||||
return addressText;
|
|
||||||
} else {
|
|
||||||
return "No address found";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
...
|
|
||||||
}
|
}
|
||||||
...
|
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
|
||||||
The next section shows you how to display the address in the user interface.
|
<h2 id="result-receiver">Receive the Geocoding Results</h2>
|
||||||
</p>
|
|
||||||
<!-- Define a method to display the address -->
|
<p>The intent service has handled the geocoding request, and uses a
|
||||||
<h2 id="DisplayResults">Define a Method to Display the Results</h2>
|
{@link android.os.ResultReceiver} to return the results to the activity that
|
||||||
<p>
|
made the request. In the activity that makes the request, define an
|
||||||
{@link android.os.AsyncTask#doInBackground doInBackground()} returns the result of the address
|
{@code AddressResultReceiver} that extends {@link android.os.ResultReceiver}
|
||||||
lookup as a {@link java.lang.String}. This value is passed to
|
to handle the response from {@code FetchAddressIntentService}.</p>
|
||||||
{@link android.os.AsyncTask#onPostExecute onPostExecute()}, where you do further processing
|
|
||||||
on the results. Since {@link android.os.AsyncTask#onPostExecute onPostExecute()}
|
<p>The result includes a numeric result code (<code>resultCode</code>) as well
|
||||||
runs on the UI thread, it can update the user interface; for example, it can turn off the
|
as a message containing the result data (<code>resultData</code>). If the
|
||||||
activity indicator and display the results to the user:
|
reverse geocoding process was successful, the <code>resultData</code> contains
|
||||||
</p>
|
the address. In the case of a failure, the <code>resultData</code> contains
|
||||||
|
text describing the reason for failure. For details of the possible errors,
|
||||||
|
see <a href="#return-address">Return the address to the requestor</a>.</p>
|
||||||
|
|
||||||
|
<p>Override the
|
||||||
|
{@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method
|
||||||
|
to handle the results delivered to the result receiver, as shown in the
|
||||||
|
following code sample:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
private class GetAddressTask extends
|
public class MainActivity extends ActionBarActivity implements
|
||||||
AsyncTask<Location, Void, String> {
|
ConnectionCallbacks, OnConnectionFailedListener {
|
||||||
...
|
...
|
||||||
/**
|
class AddressResultReceiver extends ResultReceiver {
|
||||||
* A method that's called once doInBackground() completes. Turn
|
public AddressResultReceiver(Handler handler) {
|
||||||
* off the indeterminate activity indicator and set
|
super(handler);
|
||||||
* the text of the UI element that shows the address. If the
|
}
|
||||||
* lookup failed, display the error message.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(String address) {
|
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||||
// Set activity indicator visibility to "gone"
|
|
||||||
mActivityIndicator.setVisibility(View.GONE);
|
// Display the address string
|
||||||
// Display the results of the lookup.
|
// or an error message sent from the intent service.
|
||||||
mAddress.setText(address);
|
mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
|
||||||
|
displayAddressOutput();
|
||||||
|
|
||||||
|
// Show a toast message if an address was found.
|
||||||
|
if (resultCode == Constants.SUCCESS_RESULT) {
|
||||||
|
showToast(getString(R.string.address_found));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
...
|
|
||||||
}
|
}
|
||||||
</pre>
|
|
||||||
<p>
|
|
||||||
The final step is to run the address lookup.
|
|
||||||
</p>
|
|
||||||
<!-- Get and display the address -->
|
|
||||||
<h2 id="RunTask">Run the Lookup Task</h2>
|
|
||||||
<p>
|
|
||||||
To get the address, call {@link android.os.AsyncTask#execute execute()}. For example, the
|
|
||||||
following snippet starts the address lookup when the user clicks the "Get Address" button:
|
|
||||||
</p>
|
|
||||||
<pre>
|
|
||||||
public class MainActivity extends FragmentActivity {
|
|
||||||
...
|
|
||||||
/**
|
|
||||||
* The "Get Address" button in the UI is defined with
|
|
||||||
* android:onClick="getAddress". The method is invoked whenever the
|
|
||||||
* user clicks the button.
|
|
||||||
*
|
|
||||||
* @param v The view object associated with this method,
|
|
||||||
* in this case a Button.
|
|
||||||
*/
|
|
||||||
public void getAddress(View v) {
|
|
||||||
// Ensure that a Geocoder services is available
|
|
||||||
if (Build.VERSION.SDK_INT >=
|
|
||||||
Build.VERSION_CODES.GINGERBREAD
|
|
||||||
&&
|
|
||||||
Geocoder.isPresent()) {
|
|
||||||
// Show the activity indicator
|
|
||||||
mActivityIndicator.setVisibility(View.VISIBLE);
|
|
||||||
/*
|
|
||||||
* Reverse geocoding is long-running and synchronous.
|
|
||||||
* Run it on a background thread.
|
|
||||||
* Pass the current location to the background task.
|
|
||||||
* When the task finishes,
|
|
||||||
* onPostExecute() displays the address.
|
|
||||||
*/
|
|
||||||
(new GetAddressTask(this)).execute(mLocation);
|
|
||||||
}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
...
|
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
<p>
|
|
||||||
The next lesson, <a href="geofencing.html">Creating and Monitoring Geofences</a>, demonstrates
|
|
||||||
how to define locations of interest called <b>geofences</b> and how to use geofence monitoring
|
|
||||||
to detect the user's proximity to a location of interest.
|
|
||||||
</p>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user