summaryrefslogtreecommitdiffstats
path: root/docs/html/training
diff options
context:
space:
mode:
Diffstat (limited to 'docs/html/training')
-rw-r--r--docs/html/training/enterprise/app-restrictions.jd14
-rw-r--r--docs/html/training/swipe/add-swipe-interface.jd135
-rw-r--r--docs/html/training/swipe/images/swipe.mp4bin0 -> 411879 bytes
-rw-r--r--docs/html/training/swipe/images/swipe_original.mp4bin0 -> 743388 bytes
-rw-r--r--docs/html/training/swipe/index.jd91
-rw-r--r--docs/html/training/swipe/respond-refresh-request.jd158
-rw-r--r--docs/html/training/testing/ui-testing/espresso-testing.jd5
-rw-r--r--docs/html/training/testing/ui-testing/uiautomator-testing.jd5
-rw-r--r--docs/html/training/training_toc.cs19
-rw-r--r--docs/html/training/tv/start/start.jd23
-rw-r--r--docs/html/training/wearables/data-layer/index.jd35
-rw-r--r--docs/html/training/wearables/data-layer/messages.jd213
12 files changed, 658 insertions, 40 deletions
diff --git a/docs/html/training/enterprise/app-restrictions.jd b/docs/html/training/enterprise/app-restrictions.jd
index fc5dfcc..dd2c2c0 100644
--- a/docs/html/training/enterprise/app-restrictions.jd
+++ b/docs/html/training/enterprise/app-restrictions.jd
@@ -316,8 +316,18 @@ if (!appCanUseCellular) {
{@link android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent. Your app has to listen for
this intent so you can change the app's behavior when the restriction settings
- change. The following code shows how to dynamically register a broadcast
- receiver for this intent:
+ change.</p>
+
+<p class="note">
+ <strong>Note:</strong> The {@link
+ android.content.Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED
+ ACTION_APPLICATION_RESTRICTIONS_CHANGED} intent is sent only to listeners
+ that are dynamically registered, <em>not</em> to listeners that are declared
+ in the app manifest.
+</p>
+<p>
+ The following code shows how to dynamically register a broadcast receiver for
+ this intent:
</p>
<pre>IntentFilter restrictionsFilter =
diff --git a/docs/html/training/swipe/add-swipe-interface.jd b/docs/html/training/swipe/add-swipe-interface.jd
new file mode 100644
index 0000000..ac15caf
--- /dev/null
+++ b/docs/html/training/swipe/add-swipe-interface.jd
@@ -0,0 +1,135 @@
+page.title=Adding Swipe-to-Refresh To Your App
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#AddSwipeWidget">Add the SwipeRefreshLayout Widget</a>
+ <li><a href="#AddRefreshAction">Add a Refresh Action to the Action Bar</a>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+ <li>
+ <a href=
+ "{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
+ </li>
+
+ <li>
+ <a href="{@docRoot}training/basics/actionbar/index.html">Adding the Action
+ Bar</a>
+ </li>
+</ul>
+
+<h2>Sample Apps</h2>
+
+<ul>
+ <li><a href="{@docRoot}samples/SwipeRefreshLayoutBasic/index.html">
+ SwipeRefreshLayoutBasic</a></li>
+ <li><a href="{@docRoot}samples/SwipeRefreshListFragment/index.html">
+ SwipeRefreshListFragment</a></li>
+</ul>
+
+</div>
+</div>
+
+
+<p>
+ The swipe-to-refresh user interface pattern is implemented entirely within
+ the {@link android.support.v4.widget.SwipeRefreshLayout} widget, which
+ detects the vertical swipe, displays a distinctive progress bar, and triggers
+ callback methods in your app. You enable this behavior
+ by adding the widget to your layout file as the parent of a {@link
+ android.widget.ListView} or {@link android.widget.GridView}, and implementing
+ the refresh behavior that gets invoked when the user swipes.
+</p>
+
+<p>
+ This lesson shows you how to add the widget to an existing layout. It also
+ shows you how to add a refresh action to the action bar overflow area, so
+ that users who may be unable to use the swipe gesture can trigger a manual
+ update with an external device.
+</p>
+
+<h2 id="AddSwipeWidget">Add the SwipeRefreshLayout Widget</h2>
+<p>
+ To add the swipe to refresh widget to an existing app, add {@link
+ android.support.v4.widget.SwipeRefreshLayout} as the parent
+ of a single {@link android.widget.ListView} or {@link
+ android.widget.GridView}. Remember that {@link
+ android.support.v4.widget.SwipeRefreshLayout} only supports a single {@link
+ android.widget.ListView} or {@link android.widget.GridView} child.
+</p>
+
+<p>
+ The following example demonstrates how to add the {@link
+ android.support.v4.widget.SwipeRefreshLayout} widget to an existing layout
+ file containing a {@link android.widget.ListView}:
+</p>
+
+<pre>&lt;android.support.v4.widget.SwipeRefreshLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="&#64;+id/swiperefresh"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"&gt;
+
+ &lt;ListView
+ android:id="&#64;android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" /&gt;
+
+&lt;/android.support.v4.widget.SwipeRefreshLayout&gt;</pre>
+
+<p>
+ You can also use the {@link android.support.v4.widget.SwipeRefreshLayout}
+ widget with a {@link android.support.v4.app.ListFragment}. If the layout
+ contains a {@link android.widget.ListView} with the ID
+ <code>"@android:id/list"</code>, the swipe-to-refresh functionality is
+ automatically supported. However, explicitly declaring the {@link
+ android.widget.ListView} in this way supersedes the default {@link
+ android.support.v4.app.ListFragment} view structure. If you want to use the
+ default view structure, you will have to override parts of the {@link
+ android.support.v4.widget.SwipeRefreshLayout} and {@link
+ android.support.v4.app.ListFragment} behavior. For an example of how to do
+ this, see the <a href=
+ "{@docRoot}samples/SwipeRefreshListFragment/index.html">SwipeRefreshListFragment</a>
+ sample app.
+</p>
+
+<h2 id="AddRefreshAction">Add a Refresh Action to the Action Bar</h2>
+
+<p>
+ You should add a refresh action to your app's action bar to ensure that
+ users who may not be able to perform a swipe gesture can still trigger a
+ manual update. For example, users with accessibility issues can trigger
+ action bar actions using external devices, such as keyboards and D-pads.
+</p>
+
+<p>
+ You should add the refresh action as a menu item,
+ rather than as a button, by setting the attribute
+ <code>android:showAsAction=never</code>. If you display the action as a
+ button, users may assume that the refresh button action is different from the
+ swipe-to-refresh action. By making the refresh action less conspicuous in the
+ action bar, you can encourage users to perform manual updates with the swipe
+ gesture while still maintaining the accessible option in a place where D-pad
+ users would look for it.
+</p>
+
+<p>
+ The following code demonstrates how to add the swipe-to-refresh action to the
+ overflow area:
+</p>
+
+<pre>&lt;menu xmlns:android="http://schemas.android.com/apk/res/android" &gt;
+ &lt;item
+ android:id="&#64;+id/menu_refresh"
+ android:showAsAction="never"
+ android:title="&#64;string/menu_refresh"/&gt;
+&lt;/menu&gt;</pre>
diff --git a/docs/html/training/swipe/images/swipe.mp4 b/docs/html/training/swipe/images/swipe.mp4
new file mode 100644
index 0000000..cd5c511
--- /dev/null
+++ b/docs/html/training/swipe/images/swipe.mp4
Binary files differ
diff --git a/docs/html/training/swipe/images/swipe_original.mp4 b/docs/html/training/swipe/images/swipe_original.mp4
new file mode 100644
index 0000000..abdf08f
--- /dev/null
+++ b/docs/html/training/swipe/images/swipe_original.mp4
Binary files differ
diff --git a/docs/html/training/swipe/index.jd b/docs/html/training/swipe/index.jd
new file mode 100644
index 0000000..17b4cf7
--- /dev/null
+++ b/docs/html/training/swipe/index.jd
@@ -0,0 +1,91 @@
+page.title=Supporting Swipe-to-Refresh
+trainingnavtop=true
+startpage=true
+
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>Dependencies and prerequisites</h2>
+<ul>
+ <li>Android 1.6 (API level 4) or later
+ </li>
+
+ <li>Latest version of the Android v4 <a href=
+ "{@docRoot}tools/support-library/index.html">Support Library</a>
+ </li>
+</ul>
+
+<h2>Sample Apps</h2>
+
+<ul>
+ <li><a href="{@docRoot}samples/SwipeRefreshLayoutBasic/index.html">
+ SwipeRefreshLayoutBasic</a></li>
+ <li><a href="{@docRoot}samples/SwipeRefreshListFragment/index.html">
+ SwipeRefreshListFragment</a></li>
+ <li><a href="{@docRoot}samples/SwipeRefreshMultipleViews/index.html">
+ SwipeRefreshMultipleViews</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+ Even if your app automatically updates its content on a regular basis, you
+ can allow users to request manual updates as well. For example, a weather
+ forecasting app can allow users get the latest forecasts on demand. To
+ provide a standard user experience for requesting updates, the Android
+ platform includes the swipe-to-refresh design pattern, which allows users
+ to trigger an update with a vertical swipe.
+</p>
+
+<p class="note">
+ <strong>Note:</strong> This class requires the latest version of the Android
+ v4 Support Library APIs. If you have not used the Support Library before,
+ follow the instructions in the <a href=
+ "{@docRoot}tools/support-library/setup.html">Support Library Setup</a>
+ document.
+</p>
+
+<h2>Lessons</h2>
+
+<dl>
+ <dt>
+ <b><a href="add-swipe-interface.html">Adding Swipe-to-Refresh To Your
+ App</a></b>
+ </dt>
+
+ <dd>
+ <div style="width:290px;margin-right:35px;float:right">
+ <div class="framed-nexus5-port-span-5">
+ <video class="play-on-hover" autoplay alt=
+ "When the user performs a swipe gesture, the SwipeRefreshLayout widget displays a progress indicator until your app finishes updating its data.">
+ <!-- Preferred video size 216x384 (portrait) -->
+ <source src="images/swipe.mp4">
+ </video>
+ </div>
+
+ <div style="font-size:10pt;margin-left:20px;margin-bottom:30px">
+ <em>To replay the movie, click on the device screen</em>
+ </div>
+ </div>
+ </dd>
+
+ <dd>
+ Learn how to provide swipe-to-refresh support in a {@link
+ android.widget.ListView} or {@link android.widget.GridView}, and how to
+ provide an accessible refresh option using the action bar.
+ </dd>
+
+ <dt>
+ <b><a href="respond-refresh-request.html">Responding to a Refresh
+ Request</a></b>
+ </dt>
+
+ <dd>
+ Learn how to respond to the swipe-to-refresh gesture, and how to perform the
+ same update from an action bar action.
+ </dd>
+</dl>
diff --git a/docs/html/training/swipe/respond-refresh-request.jd b/docs/html/training/swipe/respond-refresh-request.jd
new file mode 100644
index 0000000..243b4a3
--- /dev/null
+++ b/docs/html/training/swipe/respond-refresh-request.jd
@@ -0,0 +1,158 @@
+page.title=Responding to a Refresh Request
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#RespondRefresh">Respond to the Refresh Gesture</a></li>
+ <li><a href="#RespondAction">Respond to the Refresh Action</a>
+</ol>
+
+<h2>Sample App</h2>
+
+<ul>
+ <li><a href="{@docRoot}samples/SwipeRefreshLayoutBasic/index.html">
+ SwipeRefreshLayoutBasic</a></li>
+</ul>
+
+
+</div>
+</div>
+
+<p>
+ This lesson shows you how to update your app when the user requests a manual
+ refresh, whether the user triggers the refresh with a swipe gesture or by
+ using the action bar refresh action.
+</p>
+
+<h2 id="RespondRefresh">Respond to the Refresh Gesture</h2>
+
+<p>
+ When the user makes a swipe gesture, the system displays the progress
+ indicator and calls your app's callback method. Your callback method is
+ responsible for actually updating the app's data.
+</p>
+
+<p>
+ To respond to the refresh gesture in your app, implement the {@link
+ android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener} interface and
+ its {@link
+ android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener#onRefresh
+ onRefresh()} method. The {@link
+ android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener#onRefresh
+ onRefresh()} method is invoked when the user performs a swipe gesture.
+</p>
+
+<p>
+ You should put the code for the actual update
+ operation in a separate method, and call that update method from your {@link
+ android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener#onRefresh
+ onRefresh()} implementation. That way, you can use the same update method to
+ perform the update when the user triggers a refresh from the action bar.
+</p>
+
+<p>
+ Your update method calls {@link
+ android.support.v4.widget.SwipeRefreshLayout#setRefreshing
+ setRefreshing(false)} when it has finished updating the data. Calling this
+ method instructs the {@link android.support.v4.widget.SwipeRefreshLayout} to
+ remove the progress indicator and update the view contents.
+</p>
+
+<p>
+ For example, the following code implements {@link
+ android.support.v4.widget.SwipeRefreshLayout.OnRefreshListener#onRefresh
+ onRefresh()} and invokes the method {@code myUpdateOperation()} to update the
+ data displayed by the {@link android.widget.ListView}:
+</p>
+
+<pre>/*
+ * Sets up a SwipeRefreshLayout.OnRefreshListener that is invoked when the user
+ * performs a swipe-to-refresh gesture.
+ */
+mySwipeRefreshLayout.setOnRefreshListener(
+ new SwipeRefreshLayout.OnRefreshListener() {
+ &#64;Override
+ public void onRefresh() {
+ Log.i(LOG_TAG, "onRefresh called from SwipeRefreshLayout");
+
+ // This method performs the actual data-refresh operation.
+ // The method calls setRefreshing(false) when it's finished.
+ myUpdateOperation();
+ }
+ }
+);</pre>
+
+<h2 id="RespondAction">Respond to the Refresh Action</h2>
+
+<p>
+ If the user requests a refresh by using the action bar, the system calls the
+ {@link android.support.v4.app.Fragment#onOptionsItemSelected
+ onOptionsItemSelected()} method. Your app should respond to this call by
+ displaying the progress indicator and refreshing the app's data.
+</p>
+
+<p>
+ To respond to the refresh action, override {@link
+ android.support.v4.app.Fragment#onOptionsItemSelected
+ onOptionsItemSelected()}. In your override method, trigger the {@link
+ android.support.v4.widget.SwipeRefreshLayout} progress indicator by calling
+ {@link android.support.v4.widget.SwipeRefreshLayout#setRefreshing
+ setRefreshing()} with the value {@code true}, then perform the update
+ operation. Once again, you should be doing the actual update in a separate
+ method, so the same method can be called whether the user triggers the update
+ with a swipe or by using the action bar. When the update has finished, call
+ {@link android.support.v4.widget.SwipeRefreshLayout#setRefreshing
+ setRefreshing(false)} to remove the refresh progress indicator.
+</p>
+
+<p>The following code shows how to respond to the request action:
+</p>
+
+<pre>/*
+ * Listen for option item selections so that we receive a notification
+ * when the user requests a refresh by selecting the refresh action bar item.
+ */
+&#64;Override
+public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+
+ // Check if user triggered a refresh:
+ case R.id.menu_refresh:
+ Log.i(LOG_TAG, "Refresh menu item selected");
+
+ // Signal SwipeRefreshLayout to start the progress indicator
+ mySwipeRefreshLayout.setRefreshing(true);
+
+ // Start the refresh background task.
+ // This method calls setRefreshing(false) when it's finished.
+ myUpdateOperation();
+
+ return true;
+ }
+
+ // User didn't trigger a refresh, let the superclass handle this action
+ return super.onOptionsItemSelected(item);
+
+}</pre>
+
+<p class="note">
+ <strong>Note:</strong> When the user triggers a refresh with a swipe action as
+ described in <a href="#RespondRefresh">Respond to the Refresh Gesture</a>,
+ you do not need to call {@link
+ android.support.v4.widget.SwipeRefreshLayout#setRefreshing setRefreshing()}.
+ The {@link
+ android.support.v4.widget.SwipeRefreshLayout} widget takes care of displaying
+ the progress indicator and removing it when the update has finished. However,
+ if the update is triggered by any means <em>other than</em> a swipe gesture,
+ you need to explicitly turn the progress indicator on with {@link
+ android.support.v4.widget.SwipeRefreshLayout#setRefreshing setRefreshing()}.
+ The method which actually refreshes the data calls {@link
+ android.support.v4.widget.SwipeRefreshLayout#setRefreshing
+ setRefreshing(false)} to signal that the update is finished.
+</p>
diff --git a/docs/html/training/testing/ui-testing/espresso-testing.jd b/docs/html/training/testing/ui-testing/espresso-testing.jd
index 85f4ba4..29e955f 100644
--- a/docs/html/training/testing/ui-testing/espresso-testing.jd
+++ b/docs/html/training/testing/ui-testing/espresso-testing.jd
@@ -120,8 +120,9 @@ trainingnavtop=true
<pre>
dependencies {
- androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
- androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
+ androidTestCompile 'com.android.support.test:runner:0.2'
+ androidTestCompile 'com.android.support.test:rules:0.2'
+ androidTestCompile 'com.android.support.test.espresso:espresso-core:2.1'
}
</pre>
</li>
diff --git a/docs/html/training/testing/ui-testing/uiautomator-testing.jd b/docs/html/training/testing/ui-testing/uiautomator-testing.jd
index e314b70..be68ce9 100644
--- a/docs/html/training/testing/ui-testing/uiautomator-testing.jd
+++ b/docs/html/training/testing/ui-testing/uiautomator-testing.jd
@@ -89,8 +89,9 @@ test runner.
<pre>
dependencies {
- androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
- androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.0.0'
+ androidTestCompile 'com.android.support.test:runner:0.2'
+ androidTestCompile 'com.android.support.test:rules:0.2'
+ androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.0'
}
</pre>
</li>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 3ee7ab7..862663e 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -1195,7 +1195,24 @@ include the action bar on devices running Android 2.1 or higher."
</li>
</ul>
</li>
-
+ <li class="nav-section">
+ <div class="nav-section-header">
+ <a href="<?cs var:toroot ?>training/swipe/index.html"
+ description=
+ "How to modify your app's layout to support manual content updates triggered by the
+ swipe-to-refresh gesture."
+ >Supporting Swipe-to-Refresh</a>
+ </div>
+ <ul>
+ <li>
+ <a href="<?cs var:toroot ?>training/swipe/add-swipe-interface.html"
+ >Adding Swipe-to-Refresh To Your App</a></li>
+ <li>
+ <a href="<?cs var:toroot ?>training/swipe/respond-refresh-request.html"
+ >Responding to a Refresh Gesture</a>
+ </li>
+ </ul>
+ </li>
<li class="nav-section">
<div class="nav-section-header">
<a href="<?cs var:toroot ?>training/search/index.html"
diff --git a/docs/html/training/tv/start/start.jd b/docs/html/training/tv/start/start.jd
index 2766e90..0f5871f 100644
--- a/docs/html/training/tv/start/start.jd
+++ b/docs/html/training/tv/start/start.jd
@@ -1,4 +1,4 @@
-page.title=Get Started with TV Apps
+page.title=Getting Started with TV Apps
page.tags="leanback","recyclerview","launcher"
trainingnavtop=true
@@ -10,6 +10,7 @@ startpage=true
<div id="tb">
<h2>This lesson teaches you how to</h2>
<ol>
+ <li><a href="#media">Determine Media Format Support</a></li>
<li><a href="#dev-project">Setup a TV Project</a></li>
<li><a href="#tv-libraries">Add TV Support Libraries</a></li>
<li><a href="#build-it">Build TV Apps</a></li>
@@ -42,6 +43,18 @@ startpage=true
minimum required changes to enable an app to run on TV devices.
</p>
+<h2 id="media">Determine Media Format Support</h2>
+
+<p>See the following documentation for information about the codecs, protocols, and formats
+supported by Android TV.</p>
+
+<ul>
+ <li><a href="{@docRoot}guide/appendix/media-formats.html">Supported Media Formats</a></li>
+ <li><a class="external-link" href="https://source.android.com/devices/drm.html">DRM</a></li>
+ <li><code><a href="{@docRoot}reference/android/drm/package-summary.html">android.drm</a></code></li>
+ <li><a href="{@docRoot}guide/topics/media/exoplayer.html">ExoPlayer</a></li>
+ <li>{@link android.media.MediaPlayer android.media.MediaPlayer}</li>
+</ul>
<h2 id="dev-project">Set up a TV Project</h2>
@@ -284,9 +297,15 @@ startpage=true
TV devices.
</li>
<li>
- <a href="{@docRoot}training/tv/games/index.html">Games for TV</a> - TV devices are a great
+ <a href="{@docRoot}training/tv/games/index.html">Building TV Games</a> - TV devices are a great
platform for games. See this topic for information on building great game experiences for TV.
</li>
+ <li>
+ <a href="{@docRoot}training/tv/tif/index.html">Building Live TV Apps</a> - Present your video
+ content in a linear, "broadcast TV" style with channels and programs that your users can access
+ through a program guide as well as the channel up/down buttons.
+ </li>
+
</ul>
diff --git a/docs/html/training/wearables/data-layer/index.jd b/docs/html/training/wearables/data-layer/index.jd
index 85b2c33..df7c216 100644
--- a/docs/html/training/wearables/data-layer/index.jd
+++ b/docs/html/training/wearables/data-layer/index.jd
@@ -58,6 +58,30 @@ the data layer:</p>
<a href="{@docRoot}reference/com/google/android/gms/wearable/WearableListenerService.html"><code>WearableListenerService</code></a>
lets you listen for changes only when the user is actively using your app.
</dd>
+
+ <dt><b>Channel</b></dt>
+ <dd>
+ You can use the
+ <a href="{@docRoot}reference/com/google/android/gms/wearable/ChannelApi.html"><code>ChannelApi</code></a>
+ class to transfer large data items, such as music and movie files, from a handheld to a wearable
+ device. The Channel API for data transfer has the following benefits:
+ <ul>
+ <li>Transfer large data files between two or more connected devices, without
+ the automatic synchronization provided when using
+ <a href="{@docRoot}reference/com/google/android/gms/wearable/Asset.html"><code>Asset</code></a>
+ objects attached to
+ <a href="{@docRoot}reference/com/google/android/gms/wearable/DataItem.html"><code>DataItem</code></a>
+ objects. The Channel API saves disk space unlike the
+ <a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.html"><code>DataApi</code></a>
+ class, which creates a copy of the assets on the local device before synchronizing with
+ connected devices.</li>
+ <li>Reliably send a file that is too large in size to send using the
+ <a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a>
+ class.</li>
+ <li>Transfer streamed data, such as music pulled from a network server or voice
+ data from the microphone.</li>
+ </ul>
+ </dd>
</dl>
<p class="warning"><b>Warning:</b>
@@ -67,6 +91,17 @@ devices. For instance, don't try to open low-level sockets to create a communica
channel.
</p>
+<p>Android Wear supports multiple wearables connected to a handheld device. For example, when the
+user saves a note on a handheld, it automatically appears on both of the user's Wear devices. To
+synchronize data between devices, Google’s servers host a cloud node in the network of devices. The
+system synchronizes data to directly connected devices, the cloud node, and to wearable devices
+connected to the cloud node via Wi-Fi.</p>
+
+<img src="{@docRoot}wear/images/wear_cloud_node.png" alt="" width="330" height="375"/>
+<p class="img-caption"><strong>Figure 1.</strong> A sample network of nodes with
+handheld and wearable devices.</p>
+
+
<h2>Lessons</h2>
<dl>
<dt><a href="{@docRoot}training/wearables/data-layer/accessing.html">Accessing the Wearable Data Layer</a></dt>
diff --git a/docs/html/training/wearables/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
index 0ca55ba..0826e6b 100644
--- a/docs/html/training/wearables/data-layer/messages.jd
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -26,42 +26,191 @@ Unlike with data items, there is no syncing between the handheld and wearable ap
Messages are a one-way communication mechanism that's good for remote procedure calls (RPC),
such as sending a message to the wearable to start an activity.</p>
+<p>Multiple wearable devices can be connected to a user’s handheld device. Each connected device in
+the network is considered a <em>node</em>. With multiple connected devices, you must consider which
+nodes receive the messages. For example, In a voice transcription app that receives voice data on
+the wearable device, you should send the message to a node with the processing power and battery
+capacity to handle the request, such as a handheld device.</p>
+
+<p class="note"><strong>Note:</strong>
+With versions of Google Play services prior to 7.3.0, only one wearable device could be connected to
+a handheld device at a time. You may need to update your existing code to take the multiple
+connected nodes feature into consideration. If you don’t implement the changes, your messages may
+not get delivered to intended devices.
+</p>
+
<h2 id="SendMessage">Send a Message</h2>
-<p>The following example shows how to send a message that indicates to the other
-side of the connection to start an activity.
-This call is synchronous and blocks processing until the message is received or until the request
-times out:</p>
+<p>A wearable app can provide functionality for users such as voice
+transcription. Users can speak into their wearable device's microphone, and have a transcription
+saved to a note. Since a wearable device typically does not have the processing power and battery
+capacity required to handle the voice transcription activity, the app should offload this work to a
+more capable, connected device.</p>
+
+<p>The following sections show you how to advertise device nodes that can process activity
+requests, discover the nodes capable of fulfilling a requested need, and send messages to those
+nodes.
+</p>
+
+<h3 id="AdvertiseCapabilities">Advertise capabilities</h3>
+
+<p>To launch an activity on a handheld device from a wearable device, use the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a>
+class to send the request. Since multiple wearables can be connected to the handheld device, the
+wearable app needs to determine that a connected node is capable of launching the activity. In your
+handheld app, advertise that the node it runs on provides specific capabilities.</p>
+
+<p>To advertise the capabilities of your handheld app:</p>
-<p class="note"><b>Note:</b> Read more about asynchronous and synchronous calls
-to Google Play services and when to use each in
-<a href="{@docRoot}google/auth/api-client.html#Communicating">Communicate with Google Play Services</a>.
+<ol>
+ <li>Create an XML configuration file in the <code>res/values/</code> directory of your project and
+ name it <code>wear.xml</code>.
+ </li>
+ <li>Add a resource named <code>android_wear_capabilities</code> to <code>wear.xml</code>.
+ </li>
+ <li>Define capabilities that the device provides.
+ </li>
+</ol>
+
+<p class="note"><strong>Note:</strong>
+Capabilities are custom strings that you define and must be unique within your app.
</p>
+<p>The following example shows how to add a capability named <code>voice_transcription</code> to
+<code>wear.xml</code>:</p>
+
<pre>
-GoogleApiClient mGoogleApiClient;
-public static final String START_ACTIVITY_PATH = "/start/MainActivity";
+&lt;resources>
+ &lt;string-array name="android_wear_capabilities">
+ &lt;item>voice_transcription&lt;/item>
+ &lt;/string-array>
+&lt;/resources>
+</pre>
+
+<h3 id="RetrieveCapabilities">Retrieve the nodes with the required capabilities</h3>
+
+<p>Initially, you can detect the capable nodes by calling the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/CapabilityApi.html#getCapability(com.google.android.gms.common.api.GoogleApiClient, java.lang.String, int)"><code>CapabilityApi.getCapability()</code></a>
+method.
+The following example shows how to manually retrieve the results of reachable nodes with the
+<code>voice_transcription</code> capability:</p>
+
+<pre>
+private static final String
+ VOICE_TRANSCRIPTION_CAPABILITY_NAME = "voice_transcription";
+
+private GoogleApiClient mGoogleApiClient;
+
...
-private void sendStartActivityMessage(String nodeId) {
- Wearable.MessageApi.sendMessage(
- mGoogleApiClient, nodeId, START_ACTIVITY_PATH, new byte[0]).setResultCallback(
- new ResultCallback&lt;SendMessageResult>() {
- &#64;Override
- public void onResult(SendMessageResult sendMessageResult) {
- if (!sendMessageResult.getStatus().isSuccess()) {
- Log.e(TAG, "Failed to send message with status code: "
- + sendMessageResult.getStatus().getStatusCode());
+private void setupVoiceTranscription() {
+ CapabilityApi.GetCapabilityResult result =
+ Wearable.CapabilityApi.getCapability(
+ mGoogleApiClient, VOICE_TRANSCRIPTION_CAPABILITY_NAME,
+ CapabilityApi.FILTER_REACHABLE).await();
+
+ updateTranscriptionCapability(result.getCapability());
+}
+</pre>
+
+<p>To detect capable nodes as they connect to the wearable device, register a <a
+href="{@docRoot}reference/com/google/android/gms/wearable/CapabilityApi.CapabilityListener.html"><code>CapabilityApi.CapabilityListener()</code></a>
+instance to your <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html"><code>GoogleApiClient</code></a>.
+The following example shows how to register the listener and retrieve the results of reachable nodes
+with the <code>voice_transcription</code> capability:</p>
+
+<pre>
+private void setupVoiceTranscription() {
+ ...
+
+ CapabilityApi.CapabilityListener capabilityListener =
+ new CapabilityApi.CapabilityListener() {
+ &#64;Override
+ public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
+ updateTranscriptionCapability(capabilityInfo);
+ }
+ };
+
+ Wearable.CapabilityApi.addCapabilityListener(
+ mGoogleApiClient,
+ capabilityListener,
+ VOICE_TRANSCRIPTION_CAPABILITY_NAME);
+}
+</pre>
+
+<p>After detecting the capable nodes, determine where to send the message. You should pick a node
+that is in close proximity to your wearable device to
+minimize message routing through multiple nodes. A nearby node is defined as one that is directly
+connected to the device. To determine if a node is nearby, call the <a
+href="{@docRoot}reference/com/google/android/gms/wearable/Node.html#isNearby()"><code>Node.isNearby()</code></a>
+method.</p>
+
+<p>The following example shows how you might determine the best node to use:</p>
+
+<pre>
+private String transcriptionNodeId = null;
+
+private void updateTranscriptionCapability(CapabilityInfo capabilityInfo) {
+ Set&lt;Node> connectedNodes = capabilityInfo.getNodes();
+
+ transcriptionNodeId = pickBestNodeId(connectedNodes);
+}
+
+private String pickBestNodeId(Set&lt;Node> nodes) {
+ String bestNodeId = null;
+ // Find a nearby node or pick one arbitrarily
+ for (Node node : nodes) {
+ if (node.isNearby()) {
+ return node.getId();
+ }
+ bestNodeId = node.getId();
+ }
+ return bestNodeId;
+}
+</pre>
+
+<h3 id="DeliverMessage">Deliver the message</h3>
+
+<p>Once you’ve identified the best node to use, send the message using the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.html"><code>MessageApi</code></a>
+class.</p>
+
+<p>The following example shows how to send a message to the transcription-capable node from a
+wearable device. Verify that the node is available before you attempt to send the message. This call
+is synchronous and blocks processing until the message is received or until the request times out.
+</p>
+
+<pre>
+
+public static final String VOICE_TRANSCRIPTION_MESSAGE_PATH = "/voice_transcription";
+
+private void requestTranscription(byte[] voiceData) {
+ if (transcriptionNodeId != null) {
+ Wearable.MessageApi.sendMessage(googleApiClient, transcriptionNodeId,
+ VOICE_TRANSCRIPTION_MESSAGE_PATH, voiceData).setResultCallback(
+ new ResultCallback<SendMessageResult>() {
+ &#64;Override
+ public void onResult(SendMessageResult sendMessageResult) {
+ if (!sendMessageResult.getStatus().isSuccess()) {
+ // Failed to send message
+ }
+ }
}
- }
- }
- );
+ );
+ } else {
+ // Unable to retrieve node with transcription capability
+ }
}
</pre>
-<p>
-Here's a simple way to get a list of connected nodes that you can potentially
-send messages to:</p>
+<p class="note"><strong>Note:</strong> To learn more about asynchronous and synchronous calls
+to Google Play services and when to use each, see
+<a href="{@docRoot}google/auth/api-client.html#Communicating">Communicate with Google Play
+Services</a>.
+</p>
+
+<p>You can also broadcast messages to all connected nodes. To retrieve all of the
+connected nodes that you can send messages to, implement the following code:</p>
<pre>
private Collection&lt;String&gt; getNodes() {
@@ -78,22 +227,24 @@ private Collection&lt;String&gt; getNodes() {
<h2 id="ReceiveMessage">Receive a Message</h2>
<p>
-To be notified of received messages, you implement the
+To be notified of received messages, implement the
<a href="{@docRoot}reference/com/google/android/gms/wearable/MessageApi.MessageListener.html">
-<code>MessageListener</code></a> interface to provide a listener for message events. Then you register your
-listener with the
+<code>MessageListener</code></a> interface to provide a listener for message events. Then,
+register the listener with the
<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> method. This example shows how you might implement the listener
-to check the <code>START_ACTIVITY_PATH</code> that the previous example used to send the message.
-If this condition is <code>true</code>, a specific activity is started.
+<code>MessageApi.addListener()</code></a> method. This example shows how you might implement the
+listener to check the <code>VOICE_TRANSCRIPTION_MESSAGE_PATH</code>. If this condition is
+<code>true</code>, start an activity to process the voice
+data.
</p>
<pre>
&#64;Override
public void onMessageReceived(MessageEvent messageEvent) {
- if (messageEvent.getPath().equals(START_ACTIVITY_PATH)) {
+ if (messageEvent.getPath().equals(VOICE_TRANSCRIPTION_MESSAGE_PATH)) {
Intent startIntent = new Intent(this, MainActivity.class);
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startIntent.putExtra("VOICE_DATA", messageEvent.getData());
startActivity(startIntent);
}
}