diff options
Diffstat (limited to 'docs/html/training')
-rw-r--r-- | docs/html/training/enterprise/app-restrictions.jd | 14 | ||||
-rw-r--r-- | docs/html/training/swipe/add-swipe-interface.jd | 135 | ||||
-rw-r--r-- | docs/html/training/swipe/images/swipe.mp4 | bin | 0 -> 411879 bytes | |||
-rw-r--r-- | docs/html/training/swipe/images/swipe_original.mp4 | bin | 0 -> 743388 bytes | |||
-rw-r--r-- | docs/html/training/swipe/index.jd | 91 | ||||
-rw-r--r-- | docs/html/training/swipe/respond-refresh-request.jd | 158 | ||||
-rw-r--r-- | docs/html/training/testing/ui-testing/espresso-testing.jd | 5 | ||||
-rw-r--r-- | docs/html/training/testing/ui-testing/uiautomator-testing.jd | 5 | ||||
-rw-r--r-- | docs/html/training/training_toc.cs | 19 | ||||
-rw-r--r-- | docs/html/training/tv/start/start.jd | 23 | ||||
-rw-r--r-- | docs/html/training/wearables/data-layer/index.jd | 35 | ||||
-rw-r--r-- | docs/html/training/wearables/data-layer/messages.jd | 213 |
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><android.support.v4.widget.SwipeRefreshLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/swiperefresh" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ListView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + +</android.support.v4.widget.SwipeRefreshLayout></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><menu xmlns:android="http://schemas.android.com/apk/res/android" > + <item + android:id="@+id/menu_refresh" + android:showAsAction="never" + android:title="@string/menu_refresh"/> +</menu></pre> diff --git a/docs/html/training/swipe/images/swipe.mp4 b/docs/html/training/swipe/images/swipe.mp4 Binary files differnew file mode 100644 index 0000000..cd5c511 --- /dev/null +++ b/docs/html/training/swipe/images/swipe.mp4 diff --git a/docs/html/training/swipe/images/swipe_original.mp4 b/docs/html/training/swipe/images/swipe_original.mp4 Binary files differnew file mode 100644 index 0000000..abdf08f --- /dev/null +++ b/docs/html/training/swipe/images/swipe_original.mp4 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() { + @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. + */ +@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"; +<resources> + <string-array name="android_wear_capabilities"> + <item>voice_transcription</item> + </string-array> +</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<SendMessageResult>() { - @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() { + @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<Node> connectedNodes = capabilityInfo.getNodes(); + + transcriptionNodeId = pickBestNodeId(connectedNodes); +} + +private String pickBestNodeId(Set<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>() { + @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<String> getNodes() { @@ -78,22 +227,24 @@ private Collection<String> 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> @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); } } |