1page.title=Handling Data Layer Events
2
3@jd:body
4
5<div id="tb-wrapper">
6<div id="tb">
7
8<h2>This lesson teaches you to</h2>
9<ol>
10  <li><a href="#Wait">Wait for the Status of Data Layer Calls</a></li>
11  <li><a href="#Listen">Listen for Data Layer Events</a></li>
12</ol>
13
14</div>
15</div>
16
17<p>When you make calls to the Data Layer API, you can receive the status
18of the call when it completes as well as listen for any changes that
19the call ends up making with listeners.
20</p>
21
22<h2 id="Wait">Wait for the Status of Data Layer Calls</h2>
23
24<p>You'll notice that calls to the Data Layer API sometimes return a
25<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>,
26such as
27<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#putDataItem(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.PutDataRequest)"><code>putDataItem()</code></a>.
28As soon as the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> is created,
29the operation is queued in the background. If you do nothing else after this, the operation
30eventually completes silently. However, you'll usually want to do something with the result
31after the operation completes, so the
32<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
33lets you wait for the result status, either synchronously or asynchronously.
34</p>
35
36<h3 id="async-waiting">Asynchronous calls</h3>
37<p>If your code is running on the main UI thread, do not make blocking calls
38to the Data Layer API. You can run the calls asynchronously by adding a callback method
39to the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a> object,
40which fires when the operation is completed:</p>
41<pre>
42pendingResult.setResultCallback(new ResultCallback&lt;DataItemResult&gt;() {
43    &#64;Override
44    public void onResult(final DataItemResult result) {
45        if(result.getStatus().isSuccess()) {
46            Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
47        }
48    }
49});
50</pre>
51
52<h3 id="sync-waiting">Synchronous calls</h3>
53<p>If your code is running on a separate handler thread in a background service (which is the case
54in a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>),
55it's fine for the calls to block. In this case, you can call
56<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"><code>await()</code></a>
57on the <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html"><code>PendingResult</code></a>
58object, which blocks until the request completes and returns a
59<a href="{@docRoot}reference/com/google/android/gms/common/api/Result.html"><code>Result</code></a>
60object:
61</p>
62
63<pre>
64DataItemResult result = pendingResult.await();
65if(result.getStatus().isSuccess()) {
66    Log.d(TAG, "Data item set: " + result.getDataItem().getUri());
67}
68</pre>
69
70
71<h2 id="Listen">Listen for Data Layer Events </h2>
72<p>Because the data layer synchronizes and sends data across the handheld and
73wearable, you normally want to listen for important events, such as when data items
74are created, messages are received, or when the wearable and handset are connected.
75</p>
76<p>To listen for data layer events, you have two options:</p>
77
78<ul>
79  <li>Create a service that extends
80  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
81  </li>
82  <li>Create an activity that implements
83  <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>.
84  </li>
85</ul>
86
87<p>With both these options, you override the data event callback methods for the events you
88are interested in handling.</p>
89
90<h3 id="listener-service">With a WearableListenerService</h3>
91
92<p>
93You typically create instances of this service in both your wearable and handheld apps. If you
94are not interested in data events in one of these apps, then you don't need to implement this
95service in that particular app.</p>
96
97<p>For example, you can have a handheld app that sets and gets data item objects and a wearable app
98that listens for these updates to update it's UI. The wearable never updates any of the data items,
99so the handheld app doesn't listen for any data events from the wearable app.</p>
100
101<p>You can listen for the following events with
102<a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
103
104<ul>
105  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>
106- Called when data item objects are created, changed, or deleted. An event on one side of a connection
107triggers this callback on both sides.</li>
108  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onMessageReceived()</code></a>
109-  A message sent from one side of a connection triggers this callback on the other side of the connection.</li>
110  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onMessageReceived(com.google.android.gms.wearable.MessageEvent)"><code>onPeerConnected()</code></a>
111  and <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a> -
112  Called when the connection with the handheld or wearable is connected or disconnected.
113  Changes in connection state on one side of the connection trigger these callbacks on both sides
114  of the connection.
115  </li>
116</ul>
117
118<p>To create a <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:</p>
119
120<ol>
121  <li>Create a class that extends
122  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
123  </li>
124  <li>Listen for the events that you're interested in, such as
125  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code></a>.
126  </li>
127  <li>Declare an intent filter in your Android manifest to notify the system about your
128  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>.
129  This allows the system to bind your service as needed.
130  </li>
131</ol>
132
133  <p>The following example shows how to implement a simple
134  <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>:
135  </p>
136
137<pre>
138public class DataLayerListenerService extends WearableListenerService {
139
140    private static final String TAG = "DataLayerSample";
141    private static final String START_ACTIVITY_PATH = "/start-activity";
142    private static final String DATA_ITEM_RECEIVED_PATH = "/data-item-received";
143
144    &#64;Override
145    public void onDataChanged(DataEventBuffer dataEvents) {
146        if (Log.isLoggable(TAG, Log.DEBUG)) {
147            Log.d(TAG, "onDataChanged: " + dataEvents);
148        }
149        final List<DataEvent> events = FreezableUtils
150                .freezeIterable(dataEvents);
151
152        GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
153                .addApi(Wearable.API)
154                .build();
155
156        ConnectionResult connectionResult =
157                googleApiClient.blockingConnect(30, TimeUnit.SECONDS);
158
159        if (!connectionResult.isSuccess()) {
160            Log.e(TAG, "Failed to connect to GoogleApiClient.");
161            return;
162        }
163
164        // Loop through the events and send a message
165        // to the node that created the data item.
166        for (DataEvent event : events) {
167            Uri uri = event.getDataItem().getUri();
168
169            // Get the node id from the host value of the URI
170            String nodeId = uri.getHost();
171            // Set the data of the message to be the bytes of the URI
172            byte[] payload = uri.toString().getBytes();
173
174            // Send the RPC
175            Wearable.MessageApi.sendMessage(googleApiClient, nodeId,
176                    DATA_ITEM_RECEIVED_PATH, payload);
177        }
178    }
179}
180</pre>
181
182<p>Here's the corresponding intent filter in the Android manifest file:</p>
183
184<pre>
185&lt;service android:name=".DataLayerListenerService"&gt;
186  &lt;intent-filter&gt;
187      &lt;action android:name="com.google.android.gms.wearable.BIND_LISTENER" /&gt;
188  &lt;/intent-filter&gt;
189&lt;/service&gt;
190</pre>
191
192
193<h4>Permissions within Data Layer Callbacks</h4>
194
195<p>
196To deliver callbacks to your application for data layer events, Google Play services
197binds to your <a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>,
198and calls your callbacks via IPC. This has the consequence
199that your callbacks inherit the permissions of the calling process.</p>
200
201<p>If you try to perform a privileged operation within a callback, the security check fails because your callback is
202running with the identity of the calling process, instead of the identity of your app's
203process.</p>
204
205<p>To fix this, call {@link android.os.Binder#clearCallingIdentity} </a>,
206to reset identity after crossing the IPC boundary, and then restore identity with
207{@link android.os.Binder#restoreCallingIdentity restoreCallingIdentity()} when
208you've completed the privileged operation:
209</p>
210
211<pre>
212long token = Binder.clearCallingIdentity();
213try {
214    performOperationRequiringPermissions();
215} finally {
216    Binder.restoreCallingIdentity(token);
217}
218</pre>
219
220<h3 id="Listen">With a Listener Activity</h3>
221
222<p>
223If your app only cares about data layer events when the user is interacting
224with the app and does not need a long-running service to handle every data
225change, you can listen for events in an activity by implementing one or more
226of the following interfaces:
227
228<ul>
229  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a></li>
230  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html"><code>MessageApi.MessageListener</code></a></li>
231  <li><a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html"><code>NodeApi.NodeListener</code></a></li>
232</ul>
233</p>
234
235<p>To create an activity that listens for data events:</p>
236<ol>
237<li>Implement the desired interfaces.</li>
238<li>In {@link android.app.Activity#onCreate}, create an instance of
239<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>
240to work with the Data Layer API.
241<li>
242In {@link android.app.Activity#onStart onStart()}, call <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"><code>connect()</code></a> to connect the client to Google Play services.
243</li>
244<li>When the connection to Google Play services is established, the system calls
245<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)"><code>onConnected()</code></a>. This is where you call
246<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.addListener()</code></a>,
247  <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.addListener()</code></a>,
248  or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#addListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.addListener()</code></a>
249  to notify Google Play services that your activity is interested in listening for data layer events.
250</li>
251<li>In {@link android.app.Activity#onStop onStop()}, unregister any listeners with
252<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.DataApi.DataListener)"><code>DataApi.removeListener()</code></a>,
253<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.MessageApi.MessageListener)"><code>MessageApi.removeListener()</code></a>,
254or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#removeListener(com.google.android.gms.common.api.GoogleApiClient, com.google.android.gms.wearable.NodeApi.NodeListener)"><code>NodeApi.removeListener()</code></a>.
255</li>
256<li>Implement <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html#onDataChanged(com.google.android.gms.wearable.DataEventBuffer)"><code>onDataChanged()</code>,
257  <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onMessageReceived()</code></a>,
258    <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerConnected(com.google.android.gms.wearable.Node)"><code>onPeerConnected()</code></a>, and
259  <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.NodeListener.html#onPeerDisconnected(com.google.android.gms.wearable.Node)"><code>onPeerDisconnected()</code></a>, depending on the interfaces that you implemented.
260</li>
261</ol>
262
263<p>Here's an example that implements
264<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html"><code>DataApi.DataListener</code></a>:</p>
265
266<pre>
267public class MainActivity extends Activity implements
268        DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
269
270    private GoogleApiClient mGoogleApiClient;
271
272    &#64;Override
273    protected void onCreate(Bundle savedInstanceState) {
274        super.onCreate(savedInstanceState);
275
276        setContentView(R.layout.main);
277        mGoogleApiClient = new GoogleApiClient.Builder(this)
278                .addApi(Wearable.API)
279                .addConnectionCallbacks(this)
280                .addOnConnectionFailedListener(this)
281                .build();
282    }
283
284    &#64;Override
285    protected void onStart() {
286        super.onStart();
287        if (!mResolvingError) {
288            mGoogleApiClient.connect();
289        }
290    }
291
292    &#64;Override
293    public void onConnected(Bundle connectionHint) {
294        if (Log.isLoggable(TAG, Log.DEBUG)) {
295            Log.d(TAG, "Connected to Google Api Service");
296        }
297        Wearable.DataApi.addListener(mGoogleApiClient, this);
298    }
299
300    &#64;Override
301    protected void onStop() {
302        if (null != mGoogleApiClient && mGoogleApiClient.isConnected()) {
303            Wearable.DataApi.removeListener(mGoogleApiClient, this);
304            mGoogleApiClient.disconnect();
305        }
306        super.onStop();
307    }
308
309    &#64;Override
310    public void onDataChanged(DataEventBuffer dataEvents) {
311        for (DataEvent event : dataEvents) {
312            if (event.getType() == DataEvent.TYPE_DELETED) {
313                Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
314            } else if (event.getType() == DataEvent.TYPE_CHANGED) {
315                Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
316            }
317        }
318    }
319}
320</pre>
321