page.title=Detecting Location on Android Wear page.tags="gps" page.article=true @jd:body <div id="tb-wrapper"> <div id="tb"> <h2>In this document</h2> <ol class="nolist"> <li><a href="#Connect">Connect to Google Play Services</a></li> <li><a href="#Request">Request Location Updates</a></li> <li><a href="#DetectGPS">Detect On-Board GPS</a></li> <li><a href="#Disconnection">Handle Disconnection Events</a></li> <li><a href="#Notify">Handle Location Not Found</a></li> <li><a href="#Synchronize">Synchronize Data</a></li> </ol> <!-- Required platform, tools, add-ons, devices, knowledge, etc. --> <h2>Dependencies and prerequisites</h2> <ul> <li>Android 4.3 (API Level 18) or higher on the handset device</li> <li><a href="{@docRoot}google/play-services/index.html">Google Play services</a> 6.1 or higher</li> <li>An Android Wear device</li> </ul> <h2>See also</h2> <ul> <li><a href="{@docRoot}training/location/index.html">Making Your App Location-Aware </a></li> </ul> </div></div> <p>Location awareness on wearable devices enables you to create apps that give users a better understanding of their geographic position, movement and what's around them. With the small form factor and glanceable nature of a wearable device, you can build low-friction apps that record and respond to location data.</p> <p>Some wearable devices include a GPS sensor that can retrieve location data without another tethered device. However, when you request location data in a wearable app, you don't have to worry about where the location data originates; the system retrieves the location updates using the most power-efficient method. Your app should be able to handle loss of location data, in case the wear device loses connection with its paired device and does not have a built-in GPS sensor.</p> <p>This document shows you how to check for on-device location sensors, receive location data, and monitor tethered data connections.</p> <p class="note"><b>Note:</b> The article assumes that you know how to use the Google Play services API to retrieve location data. For more information, see <a href="{@docRoot}training/ location/index.html">Making Your App Location-Aware</a>.</p> <h2 id="Connect">Connect to Google Play Services</h2> <p>Location data on wearable devices is obtained though the Google Play services location APIs. You use the <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html"> <code>FusedLocationProviderApi</code></a> and its accompanying classes to obtain this data. To access location services, create an instance of <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"> <code>GoogleApiClient</code></a>, which is the main entry point for any of the Google Play services APIs. </p> <p class="caution"><b>Caution:</b> Do not use the existing <a href="{@docRoot}reference/android/location/package-summary.html">Location</a> APIs in the Android framework. The best practice for retrieving location updates is through the Google Play services API as outlined in this article.</p> <p>To connect to Google Play services, configure your app to create an instance of <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"> <code>GoogleApiClient</code></a>:</p> <ol> <li>Create an activity that specifies an implementation for the interfaces <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html" >{@code ConnectionCallbacks}</a>, <a href="{@docRoot}reference/com/google/android/gms/common/api/ GoogleApiClient.OnConnectionFailedListener.html">{@code OnConnectionFailedListener}</a>, and <a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code LocationListener}</a>.</li> <li>In your activity's {@link android.app.Activity#onCreate onCreate()} method, create an instance of <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code> GoogleApiClient</code></a> and add the Location service. </li> <li>To gracefully manage the lifecycle of the connection, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"> {@code connect()}</a> in the {@link android.app.Activity#onResume onResume()} method and <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#disconnect()"> {@code disconnect()}</a> in the {@link android.app.Activity#onPause onPause()} method. </li> </ol> <p>The following code example shows an implementation of an activity that implements the <a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html"> {@code LocationListener}</a> interface:</p> <pre> public class WearableMainActivity extends Activity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { private GoogleApiClient mGoogleApiClient; ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mGoogleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API) .addApi(Wearable.API) // used for data layer API .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .build(); } @Override protected void onResume() { super.onResume(); mGoogleApiClient.connect(); ... } @Override protected void onPause() { super.onPause(); ... mGoogleApiClient.disconnect(); } } </pre> <p>For more information on connecting to Google Play services, see <a href="{@docRoot}google/auth /api-client.html">Accessing Google APIs</a>.</p> <h2 id="Request">Request Location Updates</h2> <p>After your app has connected to the Google Play services API, it is ready to start receiving location updates. When the system invokes the <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"> <code>onConnected()</code></a> callback for your client, you build the location data request as follows:</p> <ol> <li>Create a <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html" >{@code LocationRequest}</a> object and set any options using methods like <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setPriority(int)" >{@code setPriority()}</a>. </li> <li>Request location updates using <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#requestLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationRequest, com.google.android.gms.location.LocationListener)"> <code>requestLocationUpdates()</code></a>. </li> <li>Remove location updates using <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#removeLocationUpdates(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.location.LocationListener)"> <code>removeLocationUpdates()</code></a> in the {@link android.app.Activity#onPause onPause()} method. </li> </ol> <p>The following example shows how to retrieve and remove location updates:</p> <pre> @Override public void onConnected(Bundle bundle) { LocationRequest locationRequest = LocationRequest.create() .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) .setInterval(UPDATE_INTERVAL_MS) .setFastestInterval(FASTEST_INTERVAL_MS); LocationServices.FusedLocationApi .requestLocationUpdates(mGoogleApiClient, locationRequest, this) .setResultCallback(new ResultCallback<Status>() { @Override public void onResult(Status status) { if (status.getStatus().isSuccess()) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Successfully requested location updates"); } } else { Log.e(TAG, "Failed in requesting location updates, " + "status code: " + status.getStatusCode() + ", message: " + status.getStatusMessage()); } } }); } @Override protected void onPause() { super.onPause(); if (mGoogleApiClient.isConnected()) { LocationServices.FusedLocationApi .removeLocationUpdates(mGoogleApiClient, this); } mGoogleApiClient.disconnect(); } @Override public void onConnectionSuspended(int i) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "connection to location client suspended"); } } </pre> <p>Now that you have enabled location updates, the system calls the {@link android.location.LocationListener#onLocationChanged onLocationChanged()} method with the updated location at the interval specified in <a href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)"> {@code setInterval()}</a> </p> <h2 id="DetectGPS">Detect On-Board GPS</h2> <p>Not all wearables have a GPS sensor. If your user goes out for a run and leaves their phone at home, your wearable app cannot receive location data through a tethered connection. If the wearable device does not have a sensor, you should detect this situation and warn the user that location functionality is not available. <p>To determine whether your Android Wear device has a built-in GPS sensor, use the {@link android.content.pm.PackageManager#hasSystemFeature hasSystemFeature()} method. The following code detects whether the device has built-in GPS when you start an activity: </p> <pre> protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); if (!hasGps()) { Log.d(TAG, "This hardware doesn't have GPS."); // Fall back to functionality that does not use location or // warn the user that location function is not available. } ... } private boolean hasGps() { return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); } </pre> <h2 id="Disconnection">Handle Disconnection Events</h2> <p>Wearable devices relying on a tethered connection for location data may lose their connections abruptly. If your wearable app expects a constant stream of data, you must handle the disconnection based upon where that data is interrupted or unavailable. On a wearable device with no onboard GPS sensor, loss of location data occurs when the device loses its tethered data connection. </p> <p>In cases where your app depends on a tethered data connection for location data and the wear device does not have a GPS sensor, you should detect the loss of that connection, warn the user, and gracefully degrade the functionality of your app.</p> <p>To detect the loss of a tethered data connection:</p> <ol> <li>Extend a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"> <code>WearableListenerService</code></a> that lets you listen for important data layer events. </li> <li>Declare an intent filter in your Android manifest to notify the system about your <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code> WearableListenerService</code></a>. This filter allows the system to bind your service as needed. <pre> <service android:name=".NodeListenerService"> <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service> </pre> </li> <li>Implement the <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"> <code>onPeerDisconnected()</code></a> method and handle cases of whether or not the device has built-in GPS. <pre> public class NodeListenerService extends WearableListenerService { private static final String TAG = "NodeListenerService"; @Override public void onPeerDisconnected(Node peer) { Log.d(TAG, "You have been disconnected."); if(!hasGPS()) { // Notify user to bring tethered handset // Fall back to functionality that does not use location } } ... } </pre> </li> </ol> For more information, read the <a href="{@docRoot}training/wearables/data-layer/events.html#Listen"> Listen for Data Layer Events</a> guide. <h2 id="Notify">Handle Location Not Found</h2> <p>When the GPS signal is lost, you can still retrieve the last known location using <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)"> <code>getLastLocation()</code></a>. This method can be helpful in situations where you are unable to get a GPS fix, or when your wearable doesn't have built-in GPS and loses its connection with the phone.</p> <p>The following code uses <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)"> <code>getLastLocation()</code></a> to retrieve the last known location if available: </p> <pre> Location location = LocationServices.FusedLocationApi .getLastLocation(mGoogleApiClient); </pre> <h2 id="Synchronize">Synchronize Data</h2> <p>If your wearable app records data using the built-in GPS, you may want to synchronize the location data with the handset. With the {@link android.location.LocationListener}, you implement the {@link android.location.LocationListener#onLocationChanged onLocationChanged()} method to detect and record the location as it changes. <p>The following code for wearable apps detects when the location changes and uses the data layer API to store the data for later retrieval by your phone app:</p> <pre> @Override public void onLocationChanged(Location location) { ... addLocationEntry(location.getLatitude(), location.getLongitude()); } private void addLocationEntry(double latitude, double longitude) { if (!mSaveGpsLocation || !mGoogleApiClient.isConnected()) { return; } mCalendar.setTimeInMillis(System.currentTimeMillis()); // Set the path of the data map String path = Constants.PATH + "/" + mCalendar.getTimeInMillis(); PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path); // Set the location values in the data map putDataMapRequest.getDataMap() .putDouble(Constants.KEY_LATITUDE, latitude); putDataMapRequest.getDataMap() .putDouble(Constants.KEY_LONGITUDE, longitude); putDataMapRequest.getDataMap() .putLong(Constants.KEY_TIME, mCalendar.getTimeInMillis()); // Prepare the data map for the request PutDataRequest request = putDataMapRequest.asPutDataRequest(); // Request the system to create the data item Wearable.DataApi.putDataItem(mGoogleApiClient, request) .setResultCallback(new ResultCallback<DataApi.DataItemResult>() { @Override public void onResult(DataApi.DataItemResult dataItemResult) { if (!dataItemResult.getStatus().isSuccess()) { Log.e(TAG, "Failed to set the data, " + "status: " + dataItemResult.getStatus() .getStatusCode()); } } }); } </pre> <p>For more information on how to use the Data Layer API, see the <a href="{@docRoot}training/ wearables/data-layer/index.html">Sending and Syncing Data</a> guide.</p>