1page.title=Detecting Location on Android Wear 2page.tags="gps" 3 4page.article=true 5@jd:body 6 7<div id="tb-wrapper"> 8<div id="tb"> 9<h2>In this document</h2> 10<ol class="nolist"> 11 <li><a href="#Connect">Connect to Google Play Services</a></li> 12 <li><a href="#Request">Request Location Updates</a></li> 13 <li><a href="#DetectGPS">Detect On-Board GPS</a></li> 14 <li><a href="#Disconnection">Handle Disconnection Events</a></li> 15 <li><a href="#Notify">Handle Location Not Found</a></li> 16 <li><a href="#Synchronize">Synchronize Data</a></li> 17</ol> 18<!-- Required platform, tools, add-ons, devices, knowledge, etc. --> 19<h2>Dependencies and prerequisites</h2> 20<ul> 21 <li>Android 4.3 (API Level 18) or higher on the handset device</li> 22 <li><a href="{@docRoot}google/play-services/index.html">Google Play services</a> 6.1 or higher</li> 23 <li>An Android Wear device</li> 24</ul> 25<h2>See also</h2> 26<ul> 27 <li><a href="{@docRoot}training/location/index.html">Making Your App Location-Aware 28 </a></li> 29</ul> 30</div></div> 31 32<p>Location awareness on wearable devices enables you to create apps that give users a better 33understanding of their geographic position, movement and what's around them. With the small form 34factor and glanceable nature of a wearable device, you can build low-friction apps that record and 35respond to location data.</p> 36 37<p>Some wearable devices include a GPS sensor that can retrieve location data without another 38tethered device. However, when you request location data in a wearable app, you don't have to worry 39about where the location data originates; the system retrieves the location updates using the most 40power-efficient method. Your app should be able to handle loss of location data, in case the wear 41device loses connection with its paired device and does not have a built-in GPS sensor.</p> 42 43<p>This document shows you how to check for on-device location sensors, receive location data, and 44monitor tethered data connections.</p> 45 46<p class="note"><b>Note:</b> The article assumes that you know how to use the Google Play services 47API to retrieve location data. For more information, see <a href="{@docRoot}training/ 48location/index.html">Making Your App Location-Aware</a>.</p> 49 50<h2 id="Connect">Connect to Google Play Services</h2> 51 52<p>Location data on wearable devices is obtained though the Google Play services location APIs. You 53use the <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html"> 54<code>FusedLocationProviderApi</code></a> and its accompanying classes to obtain this data. 55To access location services, create an instance of 56<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"> 57<code>GoogleApiClient</code></a>, which is 58the main entry point for any of the Google Play services APIs. 59</p> 60 61<p class="caution"><b>Caution:</b> Do not use the existing <a href="{@docRoot}reference/android/location/package-summary.html">Location</a> 62APIs in the Android framework. The best practice for retrieving location updates is through the 63Google Play services API as outlined in this article.</p> 64 65<p>To connect to Google Play services, configure your app to create an instance of 66<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"> 67<code>GoogleApiClient</code></a>:</p> 68 69<ol> 70 <li>Create an activity that specifies an implementation for the interfaces <a 71href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html" 72>{@code ConnectionCallbacks}</a>, <a href="{@docRoot}reference/com/google/android/gms/common/api/ 73GoogleApiClient.OnConnectionFailedListener.html">{@code OnConnectionFailedListener}</a>, and <a 74href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code 75LocationListener}</a>.</li> 76 <li>In your activity's {@link android.app.Activity#onCreate onCreate()} method, create an instance 77of <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code> 78GoogleApiClient</code></a> and add the Location service. 79 </li> 80 <li>To gracefully manage the lifecycle of the connection, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"> 81 {@code connect()}</a> in the {@link android.app.Activity#onResume onResume()} method and 82 <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#disconnect()"> 83 {@code disconnect()}</a> in the {@link android.app.Activity#onPause onPause()} method. 84 </li> 85</ol> 86 87<p>The following code example shows an implementation of an activity that implements the 88<a href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html"> 89{@code LocationListener}</a> interface:</p> 90 91<pre> 92public class WearableMainActivity extends Activity implements 93 GoogleApiClient.ConnectionCallbacks, 94 GoogleApiClient.OnConnectionFailedListener, 95 LocationListener { 96 97 private GoogleApiClient mGoogleApiClient; 98 ... 99 100 @Override 101 protected void onCreate(Bundle savedInstanceState) { 102 super.onCreate(savedInstanceState); 103 104 ... 105 mGoogleApiClient = new GoogleApiClient.Builder(this) 106 .addApi(LocationServices.API) 107 .addApi(Wearable.API) // used for data layer API 108 .addConnectionCallbacks(this) 109 .addOnConnectionFailedListener(this) 110 .build(); 111 } 112 113 @Override 114 protected void onResume() { 115 super.onResume(); 116 mGoogleApiClient.connect(); 117 ... 118 } 119 120 @Override 121 protected void onPause() { 122 super.onPause(); 123 ... 124 mGoogleApiClient.disconnect(); 125 } 126} 127</pre> 128 129<p>For more information on connecting to Google Play services, see <a href="{@docRoot}google/auth 130/api-client.html">Accessing Google APIs</a>.</p> 131 132<h2 id="Request">Request Location Updates</h2> 133 134<p>After your app has connected to the Google Play services API, it is ready to start receiving 135location updates. When the system invokes the 136<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"> 137<code>onConnected()</code></a> callback for your client, you build the location data request as 138follows:</p> 139 140<ol> 141 <li>Create a <a 142href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html" 143>{@code LocationRequest}</a> object and set any options using methods like <a 144href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setPriority(int)" 145>{@code setPriority()}</a>. 146 </li> 147 <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)"> 148 <code>requestLocationUpdates()</code></a>. 149 </li> 150 <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)"> 151 <code>removeLocationUpdates()</code></a> in the {@link android.app.Activity#onPause 152 onPause()} method. 153 </li> 154</ol> 155 156<p>The following example shows how to retrieve and remove location updates:</p> 157 158<pre> 159@Override 160public void onConnected(Bundle bundle) { 161 LocationRequest locationRequest = LocationRequest.create() 162 .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) 163 .setInterval(UPDATE_INTERVAL_MS) 164 .setFastestInterval(FASTEST_INTERVAL_MS); 165 166 LocationServices.FusedLocationApi 167 .requestLocationUpdates(mGoogleApiClient, locationRequest, this) 168 .setResultCallback(new ResultCallback<Status>() { 169 170 @Override 171 public void onResult(Status status) { 172 if (status.getStatus().isSuccess()) { 173 if (Log.isLoggable(TAG, Log.DEBUG)) { 174 Log.d(TAG, "Successfully requested location updates"); 175 } 176 } else { 177 Log.e(TAG, 178 "Failed in requesting location updates, " 179 + "status code: " 180 + status.getStatusCode() 181 + ", message: " 182 + status.getStatusMessage()); 183 } 184 } 185 }); 186} 187 188@Override 189protected void onPause() { 190 super.onPause(); 191 if (mGoogleApiClient.isConnected()) { 192 LocationServices.FusedLocationApi 193 .removeLocationUpdates(mGoogleApiClient, this); 194 } 195 mGoogleApiClient.disconnect(); 196} 197 198@Override 199public void onConnectionSuspended(int i) { 200 if (Log.isLoggable(TAG, Log.DEBUG)) { 201 Log.d(TAG, "connection to location client suspended"); 202 } 203} 204 205</pre> 206 207<p>Now that you have enabled location updates, the system calls the {@link android.location.LocationListener#onLocationChanged 208onLocationChanged()} method with the updated location at the interval specified in <a 209href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html#setInterval(long)"> 210{@code setInterval()}</a> 211</p> 212 213<h2 id="DetectGPS">Detect On-Board GPS</h2> 214 215<p>Not all wearables have a GPS sensor. If your user goes out for a run and leaves their phone at 216home, your wearable app cannot receive location data through a tethered connection. If the 217wearable device does not have a sensor, you should detect this situation and warn the user that 218location functionality is not available. 219 220<p>To determine whether your Android Wear device has a built-in GPS sensor, use the 221{@link android.content.pm.PackageManager#hasSystemFeature hasSystemFeature()} 222method. The following code detects whether the device has built-in GPS when you start an activity: 223</p> 224 225<pre> 226 227protected void onCreate(Bundle savedInstanceState) { 228 super.onCreate(savedInstanceState); 229 230 setContentView(R.layout.main_activity); 231 if (!hasGps()) { 232 Log.d(TAG, "This hardware doesn't have GPS."); 233 // Fall back to functionality that does not use location or 234 // warn the user that location function is not available. 235 } 236 237 ... 238} 239 240private boolean hasGps() { 241 return getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION_GPS); 242} 243</pre> 244 245<h2 id="Disconnection">Handle Disconnection Events</h2> 246 247<p>Wearable devices relying on a tethered connection for location data may lose their connections 248abruptly. If your wearable app expects a constant stream of data, you must handle the 249disconnection based upon where that data is interrupted or unavailable. On a wearable device with no 250onboard GPS sensor, loss of location data occurs when the device loses its tethered data connection. 251</p> 252 253<p>In cases where your app depends on a tethered data connection for location data and the wear 254device does not have a GPS sensor, you should detect the loss of that connection, warn the user, and 255gracefully degrade the functionality of your app.</p> 256 257<p>To detect the loss of a tethered data connection:</p> 258 259<ol> 260 <li>Extend a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"> 261 <code>WearableListenerService</code></a> that lets you listen for important data layer events. 262 </li> 263 <li>Declare an intent filter in your Android manifest to notify the system about your 264 <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code> 265 WearableListenerService</code></a>. 266 This filter allows the system to bind your service as needed. 267<pre> 268<service android:name=".NodeListenerService"> 269 <intent-filter> 270 <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> 271 </intent-filter> 272</service> 273</pre> 274 </li> 275 <li>Implement the <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"> 276 <code>onPeerDisconnected()</code></a> method and handle cases of whether or not the device has 277 built-in 278 GPS. 279<pre> 280public class NodeListenerService extends WearableListenerService { 281 282 private static final String TAG = "NodeListenerService"; 283 284 @Override 285 public void onPeerDisconnected(Node peer) { 286 Log.d(TAG, "You have been disconnected."); 287 if(!hasGPS()) { 288 // Notify user to bring tethered handset 289 // Fall back to functionality that does not use location 290 } 291 } 292 ... 293} 294</pre> 295 </li> 296</ol> 297 298For more information, read the <a href="{@docRoot}training/wearables/data-layer/events.html#Listen"> 299Listen for Data Layer Events</a> guide. 300 301<h2 id="Notify">Handle Location Not Found</h2> 302 303<p>When the GPS signal is lost, you can still retrieve the last known location using 304<a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)"> 305<code>getLastLocation()</code></a>. This method can be helpful in situations where you are unable to 306get a GPS fix, or when your wearable doesn't have built-in GPS and loses its connection with the 307phone.</p> 308 309<p>The following code uses <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)"> 310<code>getLastLocation()</code></a> to retrieve the last known location if available: 311</p> 312 313<pre> 314Location location = LocationServices.FusedLocationApi 315 .getLastLocation(mGoogleApiClient); 316</pre> 317 318<h2 id="Synchronize">Synchronize Data</h2> 319 320<p>If your wearable app records data using the built-in GPS, you may want to synchronize 321the location data with the handset. With the {@link android.location.LocationListener}, you 322implement the {@link android.location.LocationListener#onLocationChanged onLocationChanged()} 323method to detect and record the location as it changes. 324 325<p>The following code for wearable apps detects when the location changes and uses the data layer 326API to store the data for later retrieval by your phone app:</p> 327 328<pre> 329@Override 330public void onLocationChanged(Location location) { 331 ... 332 addLocationEntry(location.getLatitude(), location.getLongitude()); 333 334} 335 336private void addLocationEntry(double latitude, double longitude) { 337 if (!mSaveGpsLocation || !mGoogleApiClient.isConnected()) { 338 return; 339 } 340 341 mCalendar.setTimeInMillis(System.currentTimeMillis()); 342 343 // Set the path of the data map 344 String path = Constants.PATH + "/" + mCalendar.getTimeInMillis(); 345 PutDataMapRequest putDataMapRequest = PutDataMapRequest.create(path); 346 347 // Set the location values in the data map 348 putDataMapRequest.getDataMap() 349 .putDouble(Constants.KEY_LATITUDE, latitude); 350 putDataMapRequest.getDataMap() 351 .putDouble(Constants.KEY_LONGITUDE, longitude); 352 putDataMapRequest.getDataMap() 353 .putLong(Constants.KEY_TIME, mCalendar.getTimeInMillis()); 354 355 // Prepare the data map for the request 356 PutDataRequest request = putDataMapRequest.asPutDataRequest(); 357 358 // Request the system to create the data item 359 Wearable.DataApi.putDataItem(mGoogleApiClient, request) 360 .setResultCallback(new ResultCallback<DataApi.DataItemResult>() { 361 @Override 362 public void onResult(DataApi.DataItemResult dataItemResult) { 363 if (!dataItemResult.getStatus().isSuccess()) { 364 Log.e(TAG, "Failed to set the data, " 365 + "status: " + dataItemResult.getStatus() 366 .getStatusCode()); 367 } 368 } 369 }); 370} 371</pre> 372 373<p>For more information on how to use the Data Layer API, see the <a href="{@docRoot}training/ 374wearables/data-layer/index.html">Sending and Syncing Data</a> 375guide.</p> 376