am cd70c873: Merge "Android U: Making Apps Location-Aware" into jb-mr1.1-docs

* commit 'cd70c8736919a2750777ba5c7f655c650584fcf4':
  Android U: Making Apps Location-Aware
This commit is contained in:
Joe Malin
2013-05-13 23:27:13 -07:00
committed by Android Git Automerger
17 changed files with 3652 additions and 514 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -4,13 +4,6 @@ page.title=Location and Maps
<div id="qv-wrapper"> <div id="qv-wrapper">
<div id="qv"> <div id="qv">
<h2>Quickview</h2>
<ul>
<li>Android provides a location framework that your application can use to determine the
device's location and bearing and register for updates</li>
<li>A Google Maps external library is available that lets you display and manage Maps data</li>
</ul>
<h2>In this document</h2> <h2>In this document</h2>
<ol> <ol>
<li><a href="#location">Location Services</a></li> <li><a href="#location">Location Services</a></li>
@ -19,8 +12,23 @@ device's location and bearing and register for updates</li>
</div> </div>
</div> </div>
<div class="note">
<p>Location and maps-based apps offer a compelling experience on mobile devices. You <p>
<strong>Note:</strong> This is a guide to the <i>Android framework</i> location APIs in the
package {@link android.location}. The Google Location Services API, part of Google Play
Services, provides a more powerful, high-level framework that automates tasks such as
location provider choice and power management. Location Services also provides new
features such as activity detection that aren't available in the framework API. Developers who
are using the framework API, as well as developers who are just now adding location-awareness
to their apps, should strongly consider using the Location Services API.
</p>
<p>
To learn more about the Location Services API, see
<a href="{@docRoot}google/play-services/location.html">Google Location Services for Android</a>.
</p>
</div>
<p>
Location and maps-based apps offer a compelling experience on mobile devices. You
can build these capabilities into your app using the classes of the {@link can build these capabilities into your app using the classes of the {@link
android.location} package and the Google Maps Android API. The sections below provide android.location} package and the Google Maps Android API. The sections below provide
an introduction to how you can add the features. an introduction to how you can add the features.

View File

@ -2,15 +2,8 @@ page.title=Location Strategies
page.tags="geolocation","maps","mapview" page.tags="geolocation","maps","mapview"
@jd:body @jd:body
<div id="qv-wrapper"> <div id="tb-wrapper">
<div id="qv"> <div id="tb">
<h2>Quickview</h2>
<ul>
<li>The Network Location Provider provides good location data without using GPS</li>
<li>Obtaining user location can consume a lot of battery, so be careful how
long you listen for updates</li>
</ul>
<h2>In this document</h2> <h2>In this document</h2>
<ol> <ol>
<li><a href="#Challenges">Challenges in Determining User Location</a></li> <li><a href="#Challenges">Challenges in Determining User Location</a></li>
@ -38,7 +31,21 @@ long you listen for updates</li>
</ol> </ol>
</div> </div>
</div> </div>
<div class="note">
<p>
<strong>Note:</strong> The strategies described in this guide apply to the platform location
API in {@link android.location}. The Google Location Services API, part of Google Play
Services, provides a more powerful, high-level framework that automatically handles location
providers, user movement, and location accuracy. It also handles
location update scheduling based on power consumption parameters you provide. In most cases,
you'll get better battery performance, as well as more appropriate accuracy, by using the
Location Services API.
</p>
<p>
To learn more about the Location Services API, see
<a href="{@docRoot}google/play-services/location.html">Google Location Services for Android</a>.
</p>
</div>
<p>Knowing where the user is allows your application to be smarter and deliver <p>Knowing where the user is allows your application to be smarter and deliver
better information to the user. When developing a location-aware application for Android, you can better information to the user. When developing a location-aware application for Android, you can
utilize GPS and Android's Network Location Provider to acquire the user location. Although utilize GPS and Android's Network Location Provider to acquire the user location. Although

View File

@ -1,163 +0,0 @@
page.title=Obtaining the Current Location
parent.title=Making Your App Location Aware
parent.link=index.html
trainingnavtop=true
previous.title=Using the Location Manager
previous.link=locationmanager.html
next.title=Displaying the Location Address
next.link=geocoding.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="currentlocation.html#TaskSetupLocationListener">Set Up the Location Listener</a></li>
<li><a href="currentlocation.html#TaskHandleLocationUpdates">Handle Multiple Sources of Location Updates</a></li>
<li><a href="currentlocation.html#TaskGetLastKnownLocation">Use getLastKnownLocation() Wisely</a></li>
<li><a href="currentlocation.html#TaskTerminateUpdates">Terminate Location Updates</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
the sample app</a>
<p class="filename">LocationAware.zip</p>
</div>
</div>
</div>
<p>After setting up your application to work with {@link android.location.LocationManager}, you can begin to obtain location updates.</p>
<h2 id="TaskSetupLocationListener">Set Up the Location Listener</h2>
<p>The {@link android.location.LocationManager} class exposes a number of methods for applications to receive location updates. In its simplest form, you register an event listener, identify the location manager from which you'd like to receive location updates, and specify the minimum time and distance intervals at which to receive location updates. The {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback will be invoked with the frequency that correlates with time and distance intervals.</p>
<p>
In the sample code snippet below, the location listener is set up to receive notifications at least every 10 seconds and if the device moves by more than 10 meters. The other callback methods notify the application any status change coming from the location provider.
</p>
<pre>
private final LocationListener listener = new LocationListener() {
&#064;Override
public void onLocationChanged(Location location) {
// A new location update is received. Do something useful with it. In this case,
// we're sending the update to a handler which then updates the UI with the new
// location.
Message.obtain(mHandler,
UPDATE_LATLNG,
location.getLatitude() + ", " +
location.getLongitude()).sendToTarget();
...
}
...
};
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
10000, // 10-second interval.
10, // 10 meters.
listener);
</pre>
<h2 id="TaskHandleLocationUpdates">Handle Multiple Sources of Location Updates</h2>
<p>Generally speaking, a location provider with greater accuracy (GPS) requires a longer fix time than one with lower accuracy (network-based). If you want to display location data as quickly as possible and update it as more accurate data becomes available, a common practice is to register a location listener with both GPS and network providers. In the {@link android.location.LocationListener#onLocationChanged(android.location.Location) onLocationChanged()} callback, you'll receive location updates from multiple location providers that may have different timestamps and varying levels of accuracy. You'll need to incorporate logic to disambiguate the location providers and discard updates that are stale and less accurate. The code snippet below demonstrates a sample implementation of this logic.</p>
<pre>
private static final int TWO_MINUTES = 1000 * 60 * 2;
/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
* @param currentBestLocation The current Location fix, to which you want to compare the new one
*/
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta &gt; TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta &lt; -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta &gt; 0;
boolean isMoreAccurate = accuracyDelta &lt; 0;
boolean isSignificantlyLessAccurate = accuracyDelta &gt; 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(),
currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer &amp;&amp; !isLessAccurate) {
return true;
} else if (isNewer &amp;&amp; !isSignificantlyLessAccurate &amp;&amp; isFromSameProvider) {
return true;
}
return false;
}
/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
</pre>
<h2 id="TaskGetLastKnownLocation">Use getLastKnownLocation() Wisely</h2>
<p>The setup time for getting a reasonable location fix may not be acceptable for certain applications. You should consider calling the {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} method which simply queries Android for the last location update previously received by any location providers. Keep in mind that the returned location may be stale. You should check the timestamp and accuracy of the returned location and decide whether it is useful for your application. If you elect to discard the location update returned from {@link android.location.LocationManager#getLastKnownLocation(java.lang.String) getLastKnownLocation()} and wait for fresh updates from the location provider(s), you should consider displaying an appropriate message before location data is received.</p>
<h2 id="TaskTerminateUpdates">Terminate Location Updates</h2>
<p>When you are done with using location data, you should terminate location update to reduce
unnecessary consumption of power and network bandwidth. For example, if the user navigates away
from an activity where location updates are displayed, you should stop location update by calling
{@link android.location.LocationManager#removeUpdates(android.location.LocationListener)
removeUpdates()} in {@link android.app.Activity#onStop()}. ({@link android.app.Activity#onStop()}
is called when the activity is no longer visible. If you want to learn more about activity
lifecycle, read up on the <a
href="{@docRoot}training/basics/activity-lifecycle/stopping.html">Stopping and Restarting an
Activity</a> lesson.</p>
<pre>
protected void onStop() {
super.onStop();
mLocationManager.removeUpdates(listener);
}
</pre>
<p class="note"><strong>Note:</strong> For applications that need to continuously receive and process location updates like a near-real time mapping application, it is best to incorporate the location update logic in a background service and make use of the system notification bar to make the user aware that location data is being used.</p>

View File

@ -1,98 +0,0 @@
page.title=Displaying the Location Address
parent.title=Making Your App Location Aware
parent.link=index.html
trainingnavtop=true
previous.title=Obtaining the Current Location
previous.link=currentlocation.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="geocoding.html#TaskReverseGeocoding">Perform Reverse Geocoding</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
the sample app</a>
<p class="filename">LocationAware.zip</p>
</div>
</div>
</div>
<p>As shown in previous lessons, location updates are received in the form of latitude and longitude coordinates. While this format is useful for calculating distance or displaying a pushpin on a map, the decimal numbers make no sense to most end users. If you need to display a location to user, it is much more preferable to display the address instead.</p>
<h2 id="TaskReverseGeocoding">Perform Reverse Geocoding</h2>
<p>Reverse-geocoding is the process of translating latitude longitude coordinates to a human-readable address. The {@link android.location.Geocoder} API is available for this purpose. Note that behind the scene, the API is dependent on a web service. If such service is unavailable on the device, the API will throw a "Service not Available exception" or return an empty list of addresses. A helper method called {@link android.location.Geocoder#isPresent()} was added in Android 2.3 (API level 9) to check for the existence of the service.</p>
<p>The following code snippet demonstrates the use of the {@link android.location.Geocoder} API to perform reverse-geocoding. Since the {@link android.location.Geocoder#getFromLocation(double, double, int) getFromLocation()} method is synchronous, you should not invoke it from the UI thread, hence an {@link android.os.AsyncTask} is used in the snippet.</p>
<pre>
private final LocationListener listener = new LocationListener() {
public void onLocationChanged(Location location) {
// Bypass reverse-geocoding if the Geocoder service is not available on the
// device. The isPresent() convenient method is only available on Gingerbread or above.
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.GINGERBREAD &amp;&amp; Geocoder.isPresent()) {
// Since the geocoding API is synchronous and may take a while. You don't want to lock
// up the UI thread. Invoking reverse geocoding in an AsyncTask.
(new ReverseGeocodingTask(this)).execute(new Location[] {location});
}
}
...
};
// AsyncTask encapsulating the reverse-geocoding API. Since the geocoder API is blocked,
// we do not want to invoke it from the UI thread.
private class ReverseGeocodingTask extends AsyncTask&lt;Location, Void, Void&gt; {
Context mContext;
public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
&#064;Override
protected Void doInBackground(Location... params) {
Geocoder geocoder = new Geocoder(mContext, Locale.getDefault());
Location loc = params[0];
List&lt;Address&gt; addresses = null;
try {
// Call the synchronous getFromLocation() method by passing in the lat/long values.
addresses = geocoder.getFromLocation(loc.getLatitude(), loc.getLongitude(), 1);
} catch (IOException e) {
e.printStackTrace();
// Update UI field with the exception.
Message.obtain(mHandler, UPDATE_ADDRESS, e.toString()).sendToTarget();
}
if (addresses != null &amp;&amp; addresses.size() &gt; 0) {
Address address = addresses.get(0);
// Format the first line of address (if available), city, and country name.
String addressText = String.format("&#037;s, &#037;s, &#037;s",
address.getMaxAddressLineIndex() &gt; 0 ? address.getAddressLine(0) : "",
address.getLocality(),
address.getCountryName());
// Update the UI via a message handler.
Message.obtain(mHandler, UPDATE_ADDRESS, addressText).sendToTarget();
}
return null;
}
}
</pre>

View File

@ -1,50 +0,0 @@
page.title=Making Your App Location Aware
page.tags="geolocation","maps"
trainingnavtop=true
startpage=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Android 1.0 or higher (2.3+ for the sample app)</li>
</ul>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
the sample app</a>
<p class="filename">LocationAware.zip</p>
</div>
</div>
</div>
<p>Users bring their mobile devices with them almost everywhere. One of the unique features available to mobile applications is location awareness. Knowing the location and using the information wisely can bring a more contextual experience to your users.</p>
<p>This class teaches you how to incorporate location based services in your Android application. You'll learn a number of methods to receive location updates and related best practices.</p>
<h2>Lessons</h2>
<dl>
<dt><b><a href="locationmanager.html">Using the Location Manager</a></b></dt>
<dd>Learn how to set up your application before it can receive location updates in Android.</dd>
<dt><b><a href="currentlocation.html">Obtaining the Current Location</a></b></dt>
<dd>Learn how to work with underlying location technologies available on the platform to obtain current location.</dd>
<dt><b><a href="geocoding.html">Displaying a Location Address</a></b></dt>
<dd>Learn how to translate location coordinates into addresses that are readable to users.</dd>
</dl>

View File

@ -1,120 +0,0 @@
page.title=Using the Location Manager
parent.title=Making Your App Location Aware
parent.link=index.html
trainingnavtop=true
next.title=Obtaining the Current Location
next.link=currentlocation.html
@jd:body
<!-- This is the training bar -->
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="locationmanager.html#TaskDeclarePermissions">Declare Proper Permissions in Android Manifest</a></li>
<li><a href="locationmanager.html#TaskGetLocationManagerRef">Get a Reference to LocationManager</a></li>
<li><a href="locationmanager.html#TaskPickLocationProvider">Pick a Location Provider</a></li>
<li><a href="locationmanager.html#TaskVerifyProvider">Verify the Location Provider is Enabled</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li><a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a></li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationAware.zip" class="button">Download
the sample app</a>
<p class="filename">LocationAware.zip</p>
</div>
</div>
</div>
<p>Before your application can begin receiving location updates, it needs to perform some simple steps to set up access. In this lesson, you'll learn what these steps entail.</p>
<h2 id="TaskDeclarePermissions">Declare Proper Permissions in Android Manifest</h2>
<p>The first step of setting up location update access is to declare proper permissions in the manifest. If permissions are missing, the application will get a {@link java.lang.SecurityException} at runtime.</p>
<p>Depending on the {@link android.location.LocationManager} methods used, either {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission is needed. For example, you need to declare the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} permission if your application uses a network-based location provider only. The more accurate GPS requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
Note that declaring the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission implies {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} already.</p>
<p>Also, if a network-based location provider is used in the application, you'll need to declare the internet permission as well.</p>
<pre>
&lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /&gt;
&lt;uses-permission android:name="android.permission.INTERNET" /&gt;
</pre>
<h2 id="TaskGetLocationManagerRef">Get a Reference to LocationManager</h2>
<p>{@link android.location.LocationManager} is the main class through which your application can access location services on Android. Similar to other system services, a reference can be obtained from calling the {@link android.content.Context#getSystemService(java.lang.String) getSystemService()} method. If your application intends to receive location updates in the foreground (within an {@link android.app.Activity}), you should usually perform this step in the {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method.</p>
<pre>
LocationManager locationManager =
(LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
</pre>
<h2 id="TaskPickLocationProvider">Pick a Location Provider</h2>
<p>While not required, most modern Android-powered devices can receive location updates through multiple underlying technologies, which are abstracted to an application as {@link android.location.LocationProvider} objects. Location providers may have different performance characteristics in terms of time-to-fix, accuracy, monetary cost, power consumption, and so on. Generally, a location provider with a greater accuracy, like the GPS, requires a longer fix time than a less accurate one, such as a network-based location provider.</p>
<p>Depending on your application's use case, you have to choose a specific location provider, or multiple providers, based on similar tradeoffs. For example, a points of interest check-in application would require higher location accuracy than say, a retail store locator where a city level location fix would suffice. The snippet below asks for a provider backed by the GPS.</p>
<pre>
LocationProvider provider =
locationManager.getProvider(LocationManager.GPS_PROVIDER);
</pre>
<p>Alternatively, you can provide some input criteria such as accuracy, power requirement, monetary cost, and so on, and let Android decide a closest match location provider. The snippet below asks for a location provider with fine accuracy and no monetary cost. Note that the criteria may not resolve to any providers, in which case a null will be returned. Your application should be prepared to gracefully handle the situation.</p>
<pre>
// Retrieve a list of location providers that have fine accuracy, no monetary cost, etc
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setCostAllowed(false);
...
String providerName = locManager.getBestProvider(criteria, true);
// If no suitable provider is found, null is returned.
if (providerName != null) {
...
}
</pre>
<h2 id="TaskVerifyProvider">Verify the Location Provider is Enabled</h2>
<p>Some location providers such as the GPS can be disabled in Settings. It is good practice to check whether the desired location provider is currently enabled by calling the {@link android.location.LocationManager#isProviderEnabled(java.lang.String) isProviderEnabled()} method. If the location provider is disabled, you can offer the user an opportunity to enable it in Settings by firing an {@link android.content.Intent} with the {@link android.provider.Settings#ACTION_LOCATION_SOURCE_SETTINGS} action.</p>
<pre>
&#64;Override
protected void onStart() {
super.onStart();
// This verification should be done during onStart() because the system calls
// this method when the user returns to the activity, which ensures the desired
// location provider is enabled each time the activity resumes from the stopped state.
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!gpsEnabled) {
// Build an alert dialog here that requests that the user enable
// the location services, then when the user clicks the "OK" button,
// call enableLocationSettings()
}
}
private void enableLocationSettings() {
Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
}
</pre>

View File

@ -0,0 +1,781 @@
page.title=Recognizing the User's Current Activity
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#RequestUpdates">Request Activity Recognition Updates</a></li>
<li><a href="#HandleUpdates">Handle Activity Updates</a>
<li><a href="#RemoveUpdates">Stop Activity Recognition Updates</a>
</ol>
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
</li>
<li>
<a href="receive-location-updates.html">Receiving Location Updates</a>
</li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/ActivityRecognition.zip" class="button">Download the sample</a>
<p class="filename">ActivityRecognition.zip</p>
</div>
</div>
</div>
<p>
This lesson shows you how to request activity recognition updates from Location Services.
Activity recognition tries to detect the user's current physical activity, such as walking,
driving, or standing still. Requests for updates go through an activity recognition client,
which, while different from the location client used by location or geofencing, follows a
similar pattern. Based on the update interval you choose, Location Services sends out
activity information containing one or more possible activities and the confidence level for
each one.
</p>
<h2 id="RequestUpdates">Request Activity Recognition Updates</h2>
<p>
Requesting activity recognition updates from Location Services is similar to requesting
periodic location updates. You send the request through a client, and Location Services sends
updates back to your app by means of a {@link android.app.PendingIntent}. However, you need to
request a special permission before you request activity updates, and you use a different type
of client to make requests. The following sections show how to request the permission,
connect the client, and request updates.
</p>
<h3>Request permission to receive updates</h3>
<p>
An app that wants to get activity recognition updates must have the permission
{@code com.google.android.gms.permission.ACTIVITY_RECOGNITION}. To request this permission for
your app, add the following XML element to your manifest as a child element of the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
element:
</p>
<pre>
&lt;uses-permission
android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION"/&gt;
</pre>
<p>
Activity recognition does not require the permissions
{@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
{@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}.
</p>
<!-- Check for Google Play services -->
<h3>Check for Google Play Services</h3>
<p>
Location Services is part of the Google Play services APK. Since it's hard to anticipate the
state of the user's device, you should always check that the APK is installed before you attempt
to connect to Location Services. To check that the APK is installed, call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
which returns one of the
integer result codes listed in the API reference documentation. If you encounter an error,
call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
to retrieve localized dialog that prompts users to take the correct action, then display
the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
user to correct the problem, in which case Google Play services may send a result back to your
activity. To handle this result, override the method
{@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
</p>
<p class="note">
<strong>Note:</strong> To make your app compatible with
platform version 1.6 and later, the activity that displays the
{@link android.support.v4.app.DialogFragment} must subclass
{@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
{@link android.support.v4.app.FragmentActivity} also allows you to call
{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
</p>
<p>
Since you usually need to check for Google Play services in more than one place in your code,
define a method that encapsulates the check, then call the method before each connection
attempt. The following snippet contains all of the code required to check for Google
Play services:
</p>
<pre>
public class MainActivity extends FragmentActivity {
...
// Global constants
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
&#64;Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
&#64;Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
...
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Activity Recognition",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
return false;
}
}
...
}
</pre>
<p>
Snippets in the following sections call this method to verify that Google Play services is
available.
</p>
<h3>Send the activity update request</h3>
<p>
Send the update request from an {@link android.app.Activity} or
{@link android.support.v4.app.Fragment} that implements the callback methods required by
Location Services. Making the request is an asynchronous process that starts when you request
a connection to an activity recognition client. When the client is connected, Location Services
invokes your implementation of
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
In this method, you can send the update request to Location Services; this request is
synchronous. Once you've made the request, you can disconnect the client.
</p>
<p>
This process is described in the following snippets.
</p>
<h4 id="DefineActivity">Define the Activity or Fragment</h4>
<p>
Define an {@link android.support.v4.app.FragmentActivity} or
{@link android.support.v4.app.Fragment} that implements the following interfaces:
</p>
<dl>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
</dt>
<dd>
Specifies methods that Location Services calls when the client is connected or
disconnected.
</dd>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
</dt>
<dd>
Specifies a method that Location Services calls if an error occurs while attempting to
connect the client.
</dd>
</dl>
<p>
For example:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
}
</pre>
<p>
Next, define global variables and constants. Define constants for the update interval,
add a variable for the activity recognition client, and another for the
{@link android.app.PendingIntent} that Location Services uses to send updates to your app:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Constants that define the activity detection interval
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 20;
public static final int DETECTION_INTERVAL_MILLISECONDS =
MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
...
/*
* Store the PendingIntent used to send activity recognition events
* back to the app
*/
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;
...
}
</pre>
<p>
In {@link android.app.Activity#onCreate onCreate()}, instantiate the activity recognition
client and the {@link android.app.PendingIntent}:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
&#64;Override
onCreate(Bundle savedInstanceState) {
...
/*
* Instantiate a new activity recognition client. Since the
* parent Activity implements the connection listener and
* connection failure listener, the constructor uses "this"
* to specify the values of those parameters.
*/
mActivityRecognitionClient =
new ActivityRecognitionClient(mContext, this, this);
/*
* Create the PendingIntent that Location Services uses
* to send activity recognition updates back to this app.
*/
Intent intent = new Intent(
mContext, ActivityRecognitionIntentService.class);
/*
* Return a PendingIntent that starts the IntentService.
*/
mActivityRecognitionPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
...
}
...
}
</pre>
<h4>Start the request process</h4>
<p>
Define a method that requests activity recognition updates. In the method, request a
connection to Location Services. You can call this method from anywhere in your activity; its
purpose is to start the chain of method calls for requesting updates.
</p>
<p>
To guard against race conditions that might arise if your app tries to start another request
before the first one finishes, define a boolean flag that tracks the state of the current
request. Set the flag to {@code true} when you start a request, and then set it to
{@code false} when the request completes.
</p>
<p>
The following snippet shows how to start a request for updates:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Global constants
...
// Flag that indicates if a request is underway.
private boolean mInProgress;
...
&#64;Override
onCreate(Bundle savedInstanceState) {
...
// Start with the request flag set to false
mInProgress = false;
...
}
...
/**
* Request activity recognition updates based on the current
* detection interval.
*
*/
public void startUpdates() {
// Check for Google Play services
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mActivityRecognitionClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
}
...
}
</pre>
<p>
Implement
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>.
In this method, request activity recognition updates from Location Services. When Location
Services finishes connecting to the client and calls
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
the update request is called immediately:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/*
* Called by Location Services once the location client is connected.
*
* Continue by requesting activity updates.
*/
&#64;Override
public void onConnected(Bundle dataBundle) {
/*
* Request activity recognition updates using the preset
* detection interval and PendingIntent. This call is
* synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
/*
* Since the preceding call is synchronous, turn off the
* in progress flag and disconnect the client
*/
mInProgress = false;
mActivityRecognitionClient.disconnect();
}
...
}
</pre>
<h3>Handle disconnections</h3>
<p>
In some cases, Location Services may disconnect from the activity recognition client before
you call
<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#disconnect()">disconnect()</a></code>.
To handle this situation, implement <code>
<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>.
In this method, set the request flag to indicate that a request is not in progress, and
delete the client:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/*
* Called by Location Services once the activity recognition
* client is disconnected.
*/
&#64;Override
public void onDisconnected() {
// Turn off the request flag
mInProgress = false;
// Delete the client
mActivityRecognitionClient = null;
}
...
}
</pre>
<!-- Handle connection errors -->
<h3>Handle connection errors</h3>
<p>
Besides handling the normal callbacks from Location Services, you have to provide a callback
method that Location Services calls if a connection error occurs. This callback method
can re-use the {@link android.support.v4.app.DialogFragment} class that you defined to
handle the check for Google Play services. It can also re-use the override you defined
for {@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()} that
receives any Google Play services results that occur when the user interacts with the
error dialog. The following snippet shows you a sample implementation of the callback method:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
// Implementation of OnConnectionFailedListener.onConnectionFailed
&#64;Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Turn off the request flag
mInProgress = false;
/*
* If the error has a resolution, start a Google Play services
* activity to resolve it.
*/
if (connectionResult.hasResolution()) {
try {
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (SendIntentException e) {
// Log the error
e.printStackTrace();
}
// If no resolution is available, display an error dialog
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Activity Recognition");
}
}
...
}
...
}
</pre>
<!-- Create Intent Service -->
<h2 id="HandleUpdates">Handle Activity Updates</h2>
<p>
To handle the {@link android.content.Intent} that Location Services sends for each update
interval, define an {@link android.app.IntentService} and its required method
{@link android.app.IntentService#onHandleIntent onHandleIntent()}. Location Services
sends out activity recognition updates as {@link android.content.Intent} objects, using the
the {@link android.app.PendingIntent} you provided when you called
<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
Since you provided an explicit intent for the {@link android.app.PendingIntent}, the only
component that receives the intent is the {@link android.app.IntentService} you're defining.
</p>
<p>
The following snippets demonstrate how to examine the data in an activity recognition
update.
</p>
<h3>Define an IntentService</h3>
<p>
Start by defining the class and the required method
{@link android.app.IntentService#onHandleIntent onHandleIntent()}:
</p>
<pre>
/**
* Service that receives ActivityRecognition updates. It receives
* updates in the background, even if the main Activity is not visible.
*/
public class ActivityRecognitionIntentService extends IntentService {
...
/**
* Called when a new activity detection update is available.
*/
&#64;Override
protected void onHandleIntent(Intent intent) {
...
}
...
}
</pre>
<p>
Next, examine the data in the intent. From the update, you can get a list of possible activities
and the probability of each one. The following snippet shows how to get the most probable
activity, the confidence level for the activity (the probability that this is the actual
activity), and its type:
</p>
<pre>
public class ActivityRecognitionIntentService extends IntentService {
...
&#64;Override
protected void onHandleIntent(Intent intent) {
...
// If the incoming intent contains an update
if (ActivityRecognitionResult.hasResult(intent)) {
// Get the update
ActivityRecognitionResult result =
ActivityRecognitionResult.extractResult(intent);
// Get the most probable activity
DetectedActivity mostProbableActivity =
result.getMostProbableActivity();
/*
* Get the probability that this activity is the
* the user's actual activity
*/
int confidence = mostProbableActivity.getConfidence();
/*
* Get an integer describing the type of activity
*/
int activityType = mostProbableActivity.getType();
String activityName = getNameFromType(activityType);
/*
* At this point, you have retrieved all the information
* for the current update. You can display this
* information to the user in a notification, or
* send it to an Activity or Service in a broadcast
* Intent.
*/
...
} else {
/*
* This implementation ignores intents that don't contain
* an activity update. If you wish, you can report them as
* errors.
*/
}
...
}
...
}
</pre>
<p>
The method {@code getNameFromType()} converts activity types into descriptive
strings. In a production app, you should retrieve the strings from resources instead of
using fixed values:
</p>
<pre>
public class ActivityRecognitionIntentService extends IntentService {
...
/**
* Map detected activity types to strings
*&#64;param activityType The detected activity type
*&#64;return A user-readable name for the type
*/
private String getNameFromType(int activityType) {
switch(activityType) {
case DetectedActivity.IN_VEHICLE:
return "in_vehicle";
case DetectedActivity.ON_BICYCLE:
return "on_bicycle";
case DetectedActivity.ON_FOOT:
return "on_foot";
case DetectedActivity.STILL:
return "still";
case DetectedActivity.UNKNOWN:
return "unknown";
case DetectedActivity.TILTING:
return "tilting";
}
return "unknown";
}
...
}
</pre>
<!-- Define IntentService -->
<h3>Specify the IntentService in the manifest</h3>
<p>
To identify the {@link android.app.IntentService} to the system, add a
<code><a href="{@docRoot}guide/topics/manifest/service-element.html">&lt;service&gt;</a></code>
element to the app manifest. For example:
</p>
<pre>
&lt;service
android:name="com.example.android.location.ActivityRecognitionIntentService"
android:label="&#64;string/app_name"
android:exported="false"&gt;
&lt;/service&gt;
</pre>
<p>
Notice that you don't have to specify intent filters for the service, because it only receives
explicit intents. How the incoming activity update intents are created is described in the
section <a id="DefineActivity">Define the Activity or Fragment</a>.
</p>
<h2 id="RemoveUpdates">Stop Activity Recognition Updates</h2>
<p>
To stop activity recognition updates, use the same pattern you used to request updates,
but call <code>
<a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>
instead of <code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#requestActivityUpdates(long, android.app.PendingIntent)">requestActivityUpdates()</a></code>.
</p>
<p>
<p>
Since removing updates uses some of the methods you use to add updates, start by defining
request types for the two operations:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public enum REQUEST_TYPE = {START, STOP}
private REQUEST_TYPE mRequestType;
...
}
</pre>
<p>
Modify the code that starts activity recognition so that it uses the {@code START}
request type:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public void startUpdates() {
// Set the request type to START
mRequestType = START;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn't present, the proper request type
* can be restarted.
*/
if (!servicesConnected()) {
return;
}
...
}
...
public void onConnected(Bundle dataBundle) {
switch (mRequestType) {
case START :
/*
* Request activity recognition updates using the
* preset detection interval and PendingIntent.
* This call is synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent());
break;
...
}
...
}
...
}
</pre>
<h3>Start the process</h3>
<p>
Define a method that requests a stop to activity recognition updates. In the method,
set the request type and then request a connection to Location Services. You can call this
method from anywhere in your activity; its purpose is to start the chain of method calls that
stop activity updates:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
/**
* Turn off activity recognition updates
*
*/
public void stopUpdates() {
// Set the request type to STOP
mRequestType = STOP;
/*
* Test for Google Play services after setting the request type.
* If Google Play services isn't present, the request can be
* restarted.
*/
if (!servicesConnected()) {
return;
}
// If a request is not already underway
if (!mInProgress) {
// Indicate that a request is in progress
mInProgress = true;
// Request a connection to Location Services
mActivityRecognitionClient.connect();
//
} else {
/*
* A request is already underway. You can handle
* this situation by disconnecting the client,
* re-setting the flag, and then re-trying the
* request.
*/
}
...
}
...
}
</pre>
<p>
In
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected()</a></code>,
if the request type is STOP, call
<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>.
Pass the {@link android.app.PendingIntent} you used to start updates as the parameter to
<code><a href="{@docRoot}reference/com/google/android/gms/location/ActivityRecognitionClient.html#removeActivityUpdates(android.app.PendingIntent)">removeActivityUpdates()</a></code>:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
public void onConnected(Bundle dataBundle) {
switch (mRequestType) {
...
case STOP :
mActivityRecognitionClient.removeActivityUpdates(
mActivityRecognitionPendingIntent);
break;
}
...
}
...
}
</pre>
<p>
You do not have to modify your implementation of
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onDisconnected()">onDisconnected()</a></code>
or
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed()</a></code>,
because these methods do not depend on the request type.
</p>
<p>
You now have the basic structure of an app that implements activity recognition. You can combine
activity recognition with other location-aware features, such as periodic location updates or
geofencing, which are described in other lessons in this class.
</p>

View File

@ -0,0 +1,280 @@
page.title=Displaying a Location Address
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#DefineTask">Define the Address Lookup Task</a></li>
<li><a href="#DisplayResults">Define a Method to Display the Results</a></li>
<li><a href="#RunTask">Run the Lookup Task</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
</li>
<li>
<a href="retrieve-current.html">Retrieving the Current Location</a>
</li>
<li>
<a href="receive-location-updates.html">Receiving Location Updates</a>
</li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download
the sample app</a>
<p class="filename">LocationUpdates.zip</p>
</div>
</div>
</div>
<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>
&lt;ProgressBar
android:id="&#64;+id/address_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:visibility="gone" /&gt;
</pre>
<p>
To create the background task, define a subclass of {@link android.os.AsyncTask} that calls
{@link android.location.Geocoder#getFromLocation getFromLocation()} and returns an address.
Define a {@link android.widget.TextView} object {@code mAddress} to contain the returned
address, and a {@link android.widget.ProgressBar} object that allows you to control the
indeterminate activity indicator. For example:
</p>
<pre>
public class MainActivity extends FragmentActivity {
...
private TextView mAddress;
private ProgressBar mActivityIndicator;
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mAddress = (TextView) findViewById(R.id.address);
mActivityIndicator =
(ProgressBar) findViewById(R.id.address_progress);
}
...
/**
* A subclass of AsyncTask that calls getFromLocation() in the
* background. The class definition has these generic types:
* Location - A {@link android.location.Location} object containing
* the current location.
* Void - indicates that progress units are not used
* String - An address passed to onPostExecute()
*/
private class GetAddressTask extends
AsyncTask&lt;Location, Void, String&gt; {
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
*
* &#64;params params One or more Location objects
* &#64;return A string containing the address of the current
* location, or an empty string if no address can be found,
* or an error message
*/
&#64;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&lt;Address&gt; 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 &amp;&amp; addresses.size() &gt; 0) {
// Get the first address
Address address = addresses.get(0);
/*
* Format the first line of address (if available),
* city, and country name.
*/
String addressText = String.format(
"&#037;s, &#037;s, &#037;s",
// If there's a street address, add it
address.getMaxAddressLineIndex() &gt; 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>
<p>
The next section shows you how to display the address in the user interface.
</p>
<!-- Define a method to display the address -->
<h2 id="DisplayResults">Define a Method to Display the Results</h2>
<p>
{@link android.os.AsyncTask#doInBackground doInBackground()} returns the result of the address
lookup as a {@link java.lang.String}. This value is passed to
{@link android.os.AsyncTask#onPostExecute onPostExecute()}, where you do further processing
on the results. Since {@link android.os.AsyncTask#onPostExecute onPostExecute()}
runs on the UI thread, it can update the user interface; for example, it can turn off the
activity indicator and display the results to the user:
</p>
<pre>
private class GetAddressTask extends
AsyncTask&lt;Location, Void, String&gt; {
...
/**
* A method that's called once doInBackground() completes. Turn
* off the indeterminate activity indicator and set
* the text of the UI element that shows the address. If the
* lookup failed, display the error message.
*/
&#64;Override
protected void onPostExecute(String address) {
// Set activity indicator visibility to "gone"
mActivityIndicator.setVisibility(View.GONE);
// Display the results of the lookup.
mAddress.setText(address);
}
...
}
</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.
*
* &#64;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 &gt;=
Build.VERSION_CODES.GINGERBREAD
&amp;&amp;
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>
<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>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
page.title=Making Your App Location-Aware
page.tags="location","geofence", "geofencing", "activity recognition", "activity detection", "gps"
trainingnavtop=true
startpage=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
<h2>Dependencies and prerequisites</h2>
<ul>
<li>Google Play services client library (latest version)</li>
<li>Android version 2.2 (API level 8) or later</li>
</ul>
<!-- related docs (NOT javadocs) -->
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
</li>
</ul>
</div>
</div>
<p>
One of the unique features of mobile applications is location awareness. Mobile users bring
their devices with them everywhere, and adding location awareness to your app offers users a
more contextual experience. The new Location Services API available in Google Play services
facilitates adding location awareness to your app with automated location tracking,
geofencing, and activity recognition. This API adds significant advantages over the plaform's
location API.
</p>
<p>
This class shows you how to use Location Services in your app to get the current location,
get periodic location updates, look up addresses, create and monitor geofences, and
detect user activities. The class includes sample apps and code snippets that you can use as a
starting point for adding location awareness to your own app.
</p>
<p class="note">
<strong>Note:</strong> Since this class is based on the Google Play services client library,
make sure you install the latest version before using the sample apps or code snippets. To learn
how to set up the client library with the latest version, see
<a href="{@docRoot}google/play-services/setup.html">Setup</a> in the Google Play services guide.
</p>
<h2>Lessons</h2>
<dl>
<dt>
<b><a href="retrieve-current.html">Retrieving the Current Location</a></b>
</dt>
<dd>
Learn how to retrieve the user's current location.
</dd>
<dt>
<b><a href="receive-location-updates.html">Receiving Location Updates</a></b>
</dt>
<dd>
Learn how to request and receive periodic location updates.
</dd>
<dt>
<b><a href="display-address.html">Displaying a Location Address</a></b>
</dt>
<dd>
Learn how to convert a location's latitude and longitude into an address (reverse
geocoding).
</dd>
<dt>
<b>
<a href="geofencing.html">Creating and Monitoring Geofences</a>
</b>
</dt>
<dd>
Learn how to define one or more geographic areas as locations of interest, called geofences,
and detect when the user is close to or inside a geofence.
</dd>
<dt>
<b><a href="activity-recognition.html">Recognizing the User's Current Activity</a></b>
</dt>
<dd>
Learn how to recognize the user's current activity, such as walking, bicycling,
or driving a car, and how to use this information to modify your app's location strategy.
</dd>
</dl>

View File

@ -0,0 +1,590 @@
page.title=Receiving Location Updates
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#Permissions">Request Location Permission</a></li>
<li><a href="#PlayServices">Check for Google Play Services</a></li>
<li><a href="#DefineCallbacks">Define Location Services Callbacks</a></li>
<li><a href="#UpdateParameters">Specify Update Parameters</a></li>
<li><a href="#StartUpdates">Start Location Updates</a></li>
<li><a href="#StopUpdates">Stop Location Updates</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
</li>
<li>
<a href="retrieve-current.html">Retrieving the Current Location</a>
</li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download the sample</a>
<p class="filename">LocationUpdates.zip</p>
</div>
</div>
</div>
<p>
If your app does navigation or tracking, you probably want to get the user's
location at regular intervals. While you can do this with
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">LocationClient.getLastLocation()</a></code>,
a more direct approach is to request periodic updates from Location Services. In
response, Location Services automatically updates your app with the best available location,
based on the currently-available location providers such as WiFi and GPS.
</p>
<p>
To get periodic location updates from Location Services, you send a request using a location
client. Depending on the form of the request, Location Services either invokes a callback
method and passes in a {@link android.location.Location} object, or issues an
{@link android.content.Intent} that contains the location in its extended data. The accuracy and
frequency of the updates are affected by the location permissions you've requested and the
parameters you pass to Location Services with the request.
</p>
<!-- Request permission -->
<h2 id="Permissions">Specify App Permissions</h2>
<p>
Apps that use Location Services must request location permissions. Android has two location
permissions, {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}
and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The
permission you choose affects the accuracy of the location updates you receive.
For example, If you request only coarse location permission, Location Services obfuscates the
updated location to an accuracy that's roughly equivalent to a city block.
</p>
<p>
Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies
a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}.
</p>
<p>
For example, to add the coarse location permission to your manifest, insert the following as a
child element of
the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
element:
</p>
<pre>
&lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/&gt;
</pre>
<!-- Check for Google Play services -->
<h2 id="PlayServices">Check for Google Play Services</h2>
<p>
Location Services is part of the Google Play services APK. Since it's hard to anticipate the
state of the user's device, you should always check that the APK is installed before you attempt
to connect to Location Services. To check that the APK is installed, call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
which returns one of the
integer result codes listed in the API reference documentation. If you encounter an error,
call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
to retrieve localized dialog that prompts users to take the correct action, then display
the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
user to correct the problem, in which case Google Play services may send a result back to your
activity. To handle this result, override the method
{@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}
</p>
<p class="note">
<strong>Note:</strong> To make your app compatible with
platform version 1.6 and later, the activity that displays the
{@link android.support.v4.app.DialogFragment} must subclass
{@link android.support.v4.app.FragmentActivity} instead of {@link android.app.Activity}. Using
{@link android.support.v4.app.FragmentActivity} also allows you to call
{@link android.support.v4.app.FragmentActivity#getSupportFragmentManager
getSupportFragmentManager()} to display the {@link android.support.v4.app.DialogFragment}.
</p>
<p>
Since you usually need to check for Google Play services in more than one place in your code,
define a method that encapsulates the check, then call the method before each connection
attempt. The following snippet contains all of the code required to check for Google
Play services:
</p>
<pre>
public class MainActivity extends FragmentActivity {
...
// Global constants
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
&#64;Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
&#64;Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
...
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Location Updates",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(
getSupportFragmentManager(),
"Location Updates");
}
}
}
...
}
</pre>
<p>
Snippets in the following sections call this method to verify that Google Play services is
available.
</p>
<!--
Define Location Services Callbacks
-->
<h2 id="DefineCallbacks">Define Location Services Callbacks</h2>
<p>
Before you request location updates, you must first implement the interfaces that Location
Services uses to communicate connection status to your app:
</p>
<dl>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
</dt>
<dd>
Specifies methods that Location Services calls when a location client is connected or
disconnected.
</dd>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
</dt>
<dd>
Specifies a method that Location Services calls if an error occurs while attempting to
connect the location client. This method uses the previously-defined {@code showErrorDialog}
method to display an error dialog that attempts to fix the problem using Google Play
services.
</dd>
</dl>
<p>
The following snippet shows how to specify the interfaces and define the methods:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
&#64;Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
&#64;Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.",
Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
&#64;Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
showErrorDialog(connectionResult.getErrorCode());
}
}
...
}
</pre>
<h3>Define the location update callback</h3>
<p>
Location Services sends location updates to your app either as an {@link android.content.Intent}
or as an argument passed to a callback method you define. This lesson shows you how to get the
update using a callback method, because that pattern works best for most use cases. If you want
to receive updates in the form of an {@link android.content.Intent}, read the lesson
<a href="activity-recognition.html">Recognizing the User's Current Activity</a>, which
presents a similar pattern.
</p>
<p>
The callback method that Location Services invokes to send a location update to your app is
specified in the
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">LocationListener</a></code>
interface, in the method
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html#onLocationChanged(android.location.Location)">onLocationChanged()</a></code>.
The incoming argument is a {@link android.location.Location} object containing the location's
latitude and longitude. The following snippet shows how to specify the interface and define
the method:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Define the callback method that receives location updates
&#64;Override
public void onLocationChanged(Location location) {
// Report to the UI that the location was updated
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
...
}
</pre>
<p>
Now that you have the callbacks prepared, you can set up the request for location updates.
The first step is to specify the parameters that control the updates.
</p>
<!-- Specify update parameters -->
<h2 id="UpdateParameters">Specify Update Parameters</h2>
<p>
Location Services allows you to control the interval between updates and the location accuracy
you want, by setting the values in a
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html">LocationRequest</a></code>
object and then sending this object as part of your request to start updates.
</p>
<p>
First, set the following interval parameters:
</p>
<dl>
<dt>
Update interval
</dt>
<dd>
Set by
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
This method sets the rate in milliseconds at which your app prefers to receive location
updates. If no other apps are receiving updates from Location Services, your app will
receive updates at this rate.
</dd>
<dt>
Fastest update interval
</dt>
<dd>
Set by
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>.
This method sets the <b>fastest</b> rate in milliseconds at which your app can handle
location updates. You need to set this rate because other apps also affect the rate
at which updates are sent. Location Services sends out updates at the fastest rate that any
app requested by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
If this rate is faster than your app can handle, you may encounter problems with UI flicker
or data overflow. To prevent this, call
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>
to set an upper limit to the update rate.
<p>
Calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>
also helps to save power. When you request a preferred update rate by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>,
and a maximum rate by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setFastestInterval(long)">LocationRequest.setFastestInterval()</a></code>,
then your app gets the same update rate as the fastest rate in the system. If other
apps have requested a faster rate, you get the benefit of a faster rate. If no other
apps have a faster rate request outstanding, your app receives updates at the rate you specified
with
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)">LocationRequest.setInterval()</a></code>.
</p>
</dd>
</dl>
<p>
Next, set the accuracy parameter. In a foreground app, you need constant location updates with
high accuracy, so use the setting
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#PRIORITY_HIGH_ACCURACY">LocationRequest.PRIORITY_HIGH_ACCURACY</a></code>.
</p>
<p>
The following snippet shows how to set the update interval and accuracy in
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Global constants
...
// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// Update frequency in seconds
public static final int UPDATE_INTERVAL_IN_SECONDS = 5;
// Update frequency in milliseconds
private static final long UPDATE_INTERVAL =
MILLISECONDS_PER_SECOND * UPDATE_INTERVAL_IN SECONDS;
// The fastest update frequency, in seconds
private static final int FASTEST_INTERVAL_IN_SECONDS = 1;
// A fast frequency ceiling in milliseconds
private static final long FASTEST_INTERVAL =
MILLISECONDS_PER_SECOND * FASTEST_INTERVAL_IN_SECONDS;
...
// Define an object that holds accuracy and frequency parameters
LocationResult mLocationRequest;
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(
LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(UPDATE_INTERVAL);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
...
}
...
}
</pre>
<p class="note">
<strong>Note:</strong> If your app accesses the network or does other long-running work after
receiving a location update, adjust the fastest interval to a slower value. This prevents your
app from receiving updates it can't use. Once the long-running work is done, set the fastest
interval back to a fast value.
</p>
<!-- Start Location Updates -->
<h2 id="StartUpdates">Start Location Updates</h2>
<p>
To send the request for location updates, create a location client in
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, then connect it and make
the request by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)">requestLocationUpdates()</a></code>.
Since your client must be connected for your app to receive updates, you should
connect the client and make the request in
{@link android.support.v4.app.FragmentActivity#onStart onStart()}. This ensures that you always
have a valid, connected client while your app is visible.
</p>
<p>
Remember that the user may want to turn off location updates for various reasons. You should
provide a way for the user to do this, and you should ensure that you don't start updates in
{@link android.support.v4.app.FragmentActivity#onStart onStart()} if updates were previously
turned off. To track the user's preference, store it in your app's
{@link android.content.SharedPreferences} in
{@link android.support.v4.app.FragmentActivity#onPause onPause()} and retrieve it in
{@link android.support.v4.app.FragmentActivity#onResume onResume()}.
</p>
<p>
The following snippet shows how to set up the client in
{@link android.support.v4.app.FragmentActivity#onCreate onCreate()}, and how to connect it
and request updates in {@link android.support.v4.app.FragmentActivity#onStart onStart()}:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
// Global variables
...
LocationClient mLocationClient;
boolean mUpdatesRequested;
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
...
// Open the shared preferences
mPrefs = getSharedPreferences("SharedPreferences",
Context.MODE_PRIVATE);
// Get a SharedPreferences editor
mEditor = mPrefs.edit();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
// Start with updates turned off
mUpdatesRequested = false;
...
}
...
&#64;Override
protected void onPause() {
// Save the current setting for updates
mEditor.putBoolean("KEY_UPDATES_ON", mUpdatesRequested);
mEditor.commit();
super.onPause();
}
...
&#64;Override
protected void onStart() {
...
mLocationClient.connect();
}
...
&#64;Override
protected void onResume() {
/*
* Get any previous setting for location updates
* Gets "false" if an error occurs
*/
if (mPrefs.contains("KEY_UPDATES_ON")) {
mUpdatesRequested =
mPrefs.getBoolean("KEY_UPDATES_ON", false);
// Otherwise, turn off location updates
} else {
mEditor.putBoolean("KEY_UPDATES_ON", false);
mEditor.commit();
}
}
...
}
</pre>
<p>
For more information about saving preferences, read
<a href="{@docRoot}training/basics/data-storage/shared-preferences.html">Saving Key-Value Sets</a>.
</p>
<!--
Stop Location Updates
-->
<h2 id="StopUpdates">Stop Location Updates</h2>
<p>
To stop location updates, save the state of the update flag in
{@link android.support.v4.app.FragmentActivity#onPause onPause()}, and stop updates in
{@link android.support.v4.app.FragmentActivity#onStop onStop()} by calling
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#removeLocationUpdates(com.google.android.gms.location.LocationListener)">removeLocationUpdates(LocationListener)</a></code>.
For example:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,
LocationListener {
...
/*
* Called when the Activity is no longer visible at all.
* Stop updates and disconnect.
*/
&#64;Override
protected void onStop() {
// If the client is connected
if (mLocationClient.isConnected()) {
stopPeriodicUpdates();
}
/*
* After disconnect() is called, the client is
* considered "dead".
*/
mLocationClient.disconnect();
super.onStop();
}
...
}
</pre>
<p>
You now have the basic structure of an app that requests and receives periodic location updates.
You can combine the features described in this lesson with the geofencing, activity recognition,
or reverse geocoding features described in other lessons in this class.
</p>
<p>
The next lesson, <a href="display-address.html">Displaying a Location Address</a>, shows you how
to use the current location to display the current street address.
</p>

View File

@ -0,0 +1,387 @@
page.title=Retrieving the Current Location
trainingnavtop=true
@jd:body
<div id="tb-wrapper">
<div id="tb">
<h2>This lesson teaches you to</h2>
<ol>
<li><a href="#AppPermissions">Specify App Permissions</a></li>
<li><a href="#CheckServices">Check for Google Play services</a></li>
<li><a href="#DefineCallbacks">Define Location Services Callbacks</a></li>
<li><a href="#ConnectClient">Connect the Location Client</a></li>
<li><a href="#GetLocation">Get the Current Location</a></li>
</ol>
<h2>You should also read</h2>
<ul>
<li>
<a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
</li>
</ul>
<h2>Try it out</h2>
<div class="download-box">
<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download the sample</a>
<p class="filename">LocationUpdates.zip</p>
</div>
</div>
</div>
<p>
Location Services automatically maintains the user's current location, so all your app has to do
is retrieve it as needed. The location's accuracy is based on the location permissions you've
requested and location sensors that are currently active for the device.
<p>
Location Services sends the current location to your app through a location client, which is
an instance of the Location Services class
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html">LocationClient</a></code>.
All requests for location information go through this client.
</p>
<p class="note">
<strong>Note:</strong> Before you start the lesson, be sure that your development environment
and test device are set up correctly. To learn more about this, read the
<a href="{@docRoot}google/play-services/setup.html">Setup</a> section in the Google Play
services guide.
</p>
<!--
Specify App Permissions
-->
<h2 id="AppPermissions">Specify App Permissions</h2>
<p>
Apps that use Location Services must request location permissions. Android has two location
permissions: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}
and {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION}. The
permission you choose controls the accuracy of the current location. If you request only coarse
location permission, Location Services obfuscates the returned location to an accuracy
that's roughly equivalent to a city block.
</p>
<p>
Requesting {@link android.Manifest.permission#ACCESS_FINE_LOCATION ACCESS_FINE_LOCATION} implies
a request for {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION}.
</p>
<p>
For example, to add {@link android.Manifest.permission#ACCESS_COARSE_LOCATION
ACCESS_COARSE_LOCATION}, insert the following as a child element of the
<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>
element:
</p>
<pre>
&lt;uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/&gt;
</pre>
<!--
Check for Google Play Services
-->
<h2 id="CheckServices">Check for Google Play Services</h2>
<p>
Location Services is part of the Google Play services APK. Since it's hard to anticipate the
state of the user's device, you should always check that the APK is installed before you attempt
to connect to Location Services. To check that the APK is installed, call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)">GooglePlayServicesUtil.isGooglePlayServicesAvailable()</a></code>,
which returns one of the
integer result codes listed in the reference documentation for
<code><a href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html">ConnectionResult</a></code>.
If you encounter an error, call
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)">GooglePlayServicesUtil.getErrorDialog()</a></code>
to retrieve localized dialog that prompts users to take the correct action, then display
the dialog in a {@link android.support.v4.app.DialogFragment}. The dialog may allow the
user to correct the problem, in which case Google Play services may send a result back to your
activity. To handle this result, override the method
{@link android.support.v4.app.FragmentActivity#onActivityResult onActivityResult()}.
</p>
<p>
Since you usually need to check for Google Play services in more than one place in your code,
define a method that encapsulates the check, then call the method before each connection
attempt. The following snippet contains all of the code required to check for Google
Play services:
</p>
<pre>
public class MainActivity extends FragmentActivity {
...
// Global constants
/*
* Define a request code to send to Google Play services
* This code is returned in Activity.onActivityResult
*/
private final static int
CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
...
// Define a DialogFragment that displays the error dialog
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
// Default constructor. Sets the dialog field to null
public ErrorDialogFragment() {
super();
mDialog = null;
}
// Set the dialog to display
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
// Return a Dialog to the DialogFragment.
&#64;Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
...
/*
* Handle results returned to the FragmentActivity
* by Google Play services
*/
&#64;Override
protected void onActivityResult(
int requestCode, int resultCode, Intent data) {
// Decide what to do based on the original request code
switch (requestCode) {
...
case CONNECTION_FAILURE_RESOLUTION_REQUEST :
/*
* If the result code is Activity.RESULT_OK, try
* to connect again
*/
switch (resultCode) {
case Activity.RESULT_OK :
/*
* Try the request again
*/
...
break;
}
...
}
}
...
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d("Location Updates",
"Google Play services is available.");
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Get the error code
int errorCode = connectionResult.getErrorCode();
// Get the error dialog from Google Play services
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(
errorCode,
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment for the error dialog
ErrorDialogFragment errorFragment =
new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(getSupportFragmentManager(),
"Location Updates");
}
}
}
...
}
</pre>
<p>
Snippets in the following sections call this method to verify that Google Play services is
available.
</p>
<!--
Define Location Services Callbacks
-->
<h2 id="DefineCallbacks">Define Location Services Callbacks</h2>
<p>
To get the current location, create a location client, connect it
to Location Services, and then call its
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">getLastLocation()</a></code>
method. The return value is the best, most recent location, based on the permissions your
app requested and the currently-enabled location sensors.
<p>
<p>
Before you create the location client, implement the interfaces that Location Services uses to
communicate with your app:
</p>
<dl>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">ConnectionCallbacks</a></code>
</dt>
<dd>
Specifies methods that Location Services calls when a location client is connected or
disconnected.
</dd>
<dt>
<code><a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html">OnConnectionFailedListener</a></code>
</dt>
<dd>
Specifies a method that Location Services calls if an error occurs while attempting to
connect the location client. This method uses the previously-defined {@code showErrorDialog}
method to display an error dialog that attempts to fix the problem using Google Play
services.
</dd>
</dl>
<p>
The following snippet shows how to specify the interfaces and define the methods:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
/*
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
&#64;Override
public void onConnected(Bundle dataBundle) {
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
&#64;Override
public void onDisconnected() {
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.",
Toast.LENGTH_SHORT).show();
}
...
/*
* Called by Location Services if the attempt to
* Location Services fails.
*/
&#64;Override
public void onConnectionFailed(ConnectionResult connectionResult) {
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(
this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
/*
* Thrown if Google Play services canceled the original
* PendingIntent
*/
} catch (IntentSender.SendIntentException e) {
// Log the error
e.printStackTrace();
}
} else {
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
showErrorDialog(connectionResult.getErrorCode());
}
}
...
}
</pre>
<!--
Connect the Location Client
-->
<h2 id="ConnectClient">Connect the Location Client</h2>
<p>
Now that the callback methods are in place, create the location client and connect it to
Location Services.
</p>
<p>
You should create the location client in {@link android.support.v4.app.FragmentActivity#onCreate
onCreate()}, then connect it in
{@link android.support.v4.app.FragmentActivity#onStart onStart()}, so that Location Services
maintains the current location while your activity is fully visible. Disconnect the client in
{@link android.support.v4.app.FragmentActivity#onStop onStop()}, so that when your app is not
visible, Location Services is not maintaining the current location. Following this pattern of
connection and disconnection helps save battery power. For example:
</p>
<p class="note">
<strong>Note:</strong> The current location is only maintained while a location client is
connected to Location Service. Assuming that no other apps are connected to Location Services,
if you disconnect the client and then sometime later call
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">getLastLocation()</a></code>,
the result may be out of date.
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
...
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
mLocationClient = new LocationClient(this, this, this);
...
}
...
/*
* Called when the Activity becomes visible.
*/
&#64;Override
protected void onStart() {
super.onStart();
// Connect the client.
mLocationClient.connect();
}
...
/*
* Called when the Activity is no longer visible.
*/
&#64;Override
protected void onStop() {
// Disconnecting the client invalidates it.
mLocationClient.disconnect();
super.onStop();
}
...
}
</pre>
<!--
Get the Current Location
-->
<h2 id="GetLocation">Get the Current Location</h2>
<p>
To get the current location, call
<code><a href="{@docRoot}reference/com/google/android/gms/location/LocationClient.html#getLastLocation()">getLastLocation()</a></code>.
For example:
</p>
<pre>
public class MainActivity extends FragmentActivity implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener {
...
// Global variable to hold the current location
Location mCurrentLocation;
...
mCurrentLocation = mLocationClient.getLastLocation();
...
}
</pre>
<p>
The next lesson, <a href="receive-location-updates.html">Receiving Location Updates</a>, shows
you how to receive periodic location updates from Location Services.
</p>

View File

@ -541,25 +541,35 @@
<li class="nav-section"> <li class="nav-section">
<div class="nav-section-header"> <div class="nav-section-header">
<a href="<?cs var:toroot ?>training/basics/location/index.html" <a href="<?cs var:toroot ?>training/location/index.html"
description= description="How to add location-aware features to your app by getting the user's current location.">
"How to add location-aware features to your app by aqcuiring the user's current Making Your App Location-Aware
location." </a>
>Making Your App Location Aware</a>
</div> </div>
<ul> <ul>
<li><a href="<?cs var:toroot ?>training/basics/location/locationmanager.html"> <li>
Using the Location Manager <a href="<?cs var:toroot ?>training/location/retrieve-current.html">
Retrieving the Current Location
</a> </a>
</li> </li>
<li><a href="<?cs var:toroot ?>training/basics/location/currentlocation.html"> <li>
Obtaining the Current Location <a href="<?cs var:toroot ?>training/location/receive-location-updates.html">
Receiving Location Updates
</a> </a>
</li> </li>
<li><a href="<?cs var:toroot ?>training/basics/location/geocoding.html"> <li>
<a href="<?cs var:toroot ?>training/location/display-address.html">
Displaying a Location Address Displaying a Location Address
</a> </a>
</li> </li>
<li><a href="<?cs var:toroot ?>training/location/geofencing.html">
Creating and Monitoring Geofences
</a>
</li>
<li><a href="<?cs var:toroot ?>training/location/activity-recognition.html">
Recognizing the User's Current Activity
</a>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

View File

@ -1,9 +1,20 @@
<html> <html>
<body> <body>
<p>Contains classes that define Android location-based and related services.</p> <p>Contains the framework API classes that define Android location-based and related services.</p>
<p class="note">
<strong>Note:</strong> The Google Location Services API, part of Google Play
Services, provides a more powerful, high-level framework that automates tasks such as
location provider choice and power management. Location Services also provides new
features such as activity detection that aren't available in the framework API. Developers who
are using the framework API, as well as developers who are just now adding location-awareness
to their apps, should strongly consider using the Location Services API.
<br/>
To learn more about the Location Services API, see
<a href="{@docRoot}google/play-services/location.html">Location APIs</a>.
</p>
<p>For more information, see the <p>For more information about the framework API, see the
<a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a> guide.</p> <a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a> guide.</p>
{@more} {@more}