Home | History | Annotate | Download | only in articles
      1 page.title=Detecting Location on Android Wear
      2 page.tags="gps"
      3 
      4 page.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
     33 understanding of their geographic position, movement and what's around them. With the small form
     34 factor and glanceable nature of a wearable device, you can build low-friction apps that record and
     35 respond to location data.</p>
     36 
     37 <p>Some wearable devices include a GPS sensor that can retrieve location data without another
     38 tethered device. However, when you request location data in a wearable app, you don't have to worry
     39 about where the location data originates; the system retrieves the location updates using the most
     40 power-efficient method. Your app should be able to handle loss of location data, in case the wear
     41 device 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
     44 monitor 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
     47 API to retrieve location data. For more information, see <a href="{@docRoot}training/
     48 location/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
     53 use 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.
     55 To 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
     58 the 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>
     62 APIs in the Android framework. The best practice for retrieving location updates is through the
     63 Google 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
     71 href="{@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/
     73 GoogleApiClient.OnConnectionFailedListener.html">{@code OnConnectionFailedListener}</a>, and <a
     74 href="{@docRoot}reference/com/google/android/gms/location/LocationListener.html">{@code
     75 LocationListener}</a>.</li>
     76   <li>In your activity's {@link android.app.Activity#onCreate onCreate()} method, create an instance
     77 of <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>
     78 GoogleApiClient</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>
     92 public class WearableMainActivity extends Activity implements
     93     GoogleApiClient.ConnectionCallbacks,
     94     GoogleApiClient.OnConnectionFailedListener,
     95     LocationListener {
     96 
     97     private GoogleApiClient mGoogleApiClient;
     98     ...
     99 
    100     &#64;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     &#64;Override
    114     protected void onResume() {
    115         super.onResume();
    116         mGoogleApiClient.connect();
    117         ...
    118     }
    119 
    120     &#64;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
    135 location 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
    138 follows:</p>
    139 
    140 <ol>
    141   <li>Create a <a
    142 href="{@docRoot}reference/com/google/android/gms/location/LocationRequest.html"
    143 >{@code LocationRequest}</a> object and set any options using methods like <a
    144 href="{@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 &#64;Override
    160 public 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                 &#64;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 &#64;Override
    189 protected void onPause() {
    190     super.onPause();
    191     if (mGoogleApiClient.isConnected()) {
    192         LocationServices.FusedLocationApi
    193              .removeLocationUpdates(mGoogleApiClient, this);
    194     }
    195     mGoogleApiClient.disconnect();
    196 }
    197 
    198 &#64;Override
    199 public 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
    208 onLocationChanged()} method with the updated location at the interval specified in <a
    209 href="{@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
    216 home, your wearable app cannot receive location data through a tethered connection. If the
    217 wearable device does not have a sensor, you should detect this situation and warn the user that
    218 location 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()}
    222 method. The following code detects whether the device has built-in GPS when you start an activity:
    223 </p>
    224 
    225 <pre>
    226 
    227 protected 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 
    240 private 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
    248 abruptly. If your wearable app expects a constant stream of data, you must handle the
    249 disconnection based upon where that data is interrupted or unavailable. On a wearable device with no
    250 onboard 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
    254 device does not have a GPS sensor, you should detect the loss of that connection, warn the user, and
    255 gracefully 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 &lt;service android:name=".NodeListenerService"&gt;
    269     &lt;intent-filter&gt;
    270         &lt;action android:name="com.google.android.gms.wearable.BIND_LISTENER" /&gt;
    271     &lt;/intent-filter&gt;
    272 &lt;/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>
    280 public class NodeListenerService extends WearableListenerService {
    281 
    282     private static final String TAG = "NodeListenerService";
    283 
    284     &#64;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 
    298 For more information, read the <a href="{@docRoot}training/wearables/data-layer/events.html#Listen">
    299 Listen 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
    306 get a GPS fix, or when your wearable doesn't have built-in GPS and loses its connection with the
    307 phone.</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>
    314 Location 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
    321 the location data with the handset. With the {@link android.location.LocationListener}, you
    322 implement the {@link android.location.LocationListener#onLocationChanged onLocationChanged()}
    323 method 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
    326 API to store the data for later retrieval by your phone app:</p>
    327 
    328 <pre>
    329 &#64;Override
    330 public void onLocationChanged(Location location) {
    331     ...
    332     addLocationEntry(location.getLatitude(), location.getLongitude());
    333 
    334 }
    335 
    336 private 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                 &#64;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/
    374 wearables/data-layer/index.html">Sending and Syncing Data</a>
    375 guide.</p>
    376