summaryrefslogtreecommitdiffstats
path: root/docs/html/training
diff options
context:
space:
mode:
Diffstat (limited to 'docs/html/training')
-rw-r--r--docs/html/training/articles/security-tips.jd12
-rw-r--r--docs/html/training/basics/fragments/support-lib.jd83
-rw-r--r--docs/html/training/basics/intents/sending.jd3
-rw-r--r--docs/html/training/graphics/opengl/draw.jd96
-rw-r--r--docs/html/training/graphics/opengl/environment.jd26
-rw-r--r--docs/html/training/graphics/opengl/motion.jd7
-rw-r--r--docs/html/training/graphics/opengl/projection.jd48
-rw-r--r--docs/html/training/graphics/opengl/touch.jd18
-rw-r--r--docs/html/training/location/display-address.jd680
-rw-r--r--docs/html/training/material/drawables.jd2
-rw-r--r--docs/html/training/multiscreen/screendensities.jd23
-rw-r--r--docs/html/training/training_toc.cs35
-rw-r--r--docs/html/training/transitions/custom-transitions.jd189
-rw-r--r--docs/html/training/transitions/index.jd80
-rw-r--r--docs/html/training/transitions/overview.jd165
-rw-r--r--docs/html/training/transitions/scenes.jd211
-rw-r--r--docs/html/training/transitions/transitions.jd315
-rw-r--r--docs/html/training/tv/discovery/index.jd5
-rw-r--r--docs/html/training/tv/discovery/recommendations.jd43
-rw-r--r--docs/html/training/tv/discovery/searchable.jd20
-rw-r--r--docs/html/training/tv/playback/card.jd156
-rw-r--r--docs/html/training/tv/playback/index.jd3
-rw-r--r--docs/html/training/tv/start/layouts.jd4
-rw-r--r--docs/html/training/wearables/data-layer/data-items.jd128
-rw-r--r--docs/html/training/wearables/data-layer/events.jd3
-rw-r--r--docs/html/training/wearables/data-layer/messages.jd22
-rw-r--r--docs/html/training/wearables/watch-faces/configuration.jd19
-rw-r--r--docs/html/training/wearables/watch-faces/drawing.jd51
-rw-r--r--docs/html/training/wearables/watch-faces/index.jd8
-rw-r--r--docs/html/training/wearables/watch-faces/service.jd33
30 files changed, 1964 insertions, 524 deletions
diff --git a/docs/html/training/articles/security-tips.jd b/docs/html/training/articles/security-tips.jd
index e05b44c..3215a0e 100644
--- a/docs/html/training/articles/security-tips.jd
+++ b/docs/html/training/articles/security-tips.jd
@@ -445,7 +445,17 @@ locally. Server-side
headers like <code>no-cache</code> can also be used to indicate that an application should
not cache particular content.</p>
-
+<p>Devices running platforms older than Android 4.4 (API level 19)
+use a version of {@link android.webkit webkit} that has a number of security issues.
+As a workaround, if your app is running on these devices, it
+should confirm that {@link android.webkit.WebView} objects display only trusted
+content. You should also use the updatable security {@link
+java.security.Provider Provider} object to make sure your app isn’t exposed to
+potential vulnerabilities in SSL, as described in <a
+href="{@docRoot}training/articles/security-gms-provider.html">Updating Your
+Security Provider to Protect Against SSL Exploits</a>. If your application must
+render content from the open web, consider providing your own renderer so
+you can keep it up to date with the latest security patches.</p>
<h3 id="Credentials">Handling Credentials</h3>
diff --git a/docs/html/training/basics/fragments/support-lib.jd b/docs/html/training/basics/fragments/support-lib.jd
deleted file mode 100644
index a1d781b..0000000
--- a/docs/html/training/basics/fragments/support-lib.jd
+++ /dev/null
@@ -1,83 +0,0 @@
-page.title=Using the Support Library
-page.tags=support library
-helpoutsWidget=true
-
-trainingnavtop=true
-
-@jd:body
-
-<div id="tb-wrapper">
- <div id="tb">
- <h2>This lesson teaches you to</h2>
- <ol>
- <li><a href="#Setup">Set Up Your Project with the Support Library</a></li>
- <li><a href="#Apis">Import the Support Library APIs</a></li>
- </ol>
- <h2>You should also read</h2>
- <ul>
- <li><a href="{@docRoot}tools/support-library/index.html">Support Library</a></li>
- </ul>
- </div>
-</div>
-
-<p>The Android <a href="{@docRoot}tools/support-library/index.html">Support Library</a> provides a JAR
-file with an API library that allows you to use some of the more recent Android APIs in your app
-while running on earlier versions of Android. For instance, the Support Library provides a version
-of the {@link android.app.Fragment} APIs that you can use on Android 1.6 (API level 4) and
-higher.</p>
-
-<p>This lesson shows how to set up your app to use the Support Library in order to use fragments
-to build a dynamic app UI.</p>
-
-
-<h2 id="Setup">Set Up Your Project with the Support Library</h2>
-
-<div class="figure" style="width:527px">
-<img src="{@docRoot}images/training/basics/sdk-manager.png" alt="" />
-<p class="img-caption"><strong>Figure 1.</strong> The Android SDK Manager with the
-Android Support package selected.</p>
-</div>
-
-<p>To set up your project:</p>
-
-<ol>
- <li>Download the Android Support package using the SDK Manager.</li>
-
- <li>Create a <code>libs</code> directory at the top level of your Android project.</li>
- <li>Locate the JAR file for the library you want to use and copy it into the <code>libs/</code>
-directory.
-<p>For example, the library that supports API level 4 and up is located at
-<code>&lt;sdk>/extras/android/support/v4/android-support-v4.jar</code>.</p></li>
- <li>Update your manifest file to set the minimum API level to <code>4</code> and the target
-API level to the latest release:
- <pre>&lt;uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" /></pre>
- </li>
-</ol>
-
-
-<h2 id="Apis">Import the Support Library APIs</h2>
-
-<p>The Support Library includes a variety of APIs that were either added in recent versions of
-Android or don't exist in the platform at all and merely provide additional support to you when
-developing specific application features.</p>
-
-<p>You can find all the API reference documentation for the Support Library in the
-platform docs at {@link android.support.v4.app android.support.v4.*}.</p>
-
-<div class="warning"><p><strong>Warning:</strong> To be sure that you don't accidentally use new
-APIs on an older system version, be certain that you import the {@link
-android.support.v4.app.Fragment} class and related APIs from the {@link android.support.v4.app}
-package:</p>
-<pre>
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-...
-</pre>
-</div>
-
-
-<p>When creating an activity that hosts fragments while using the Support Library, you must also
-extend the {@link android.support.v4.app.FragmentActivity} class instead of the traditional {@link
-android.app.Activity} class. You'll see sample code for the fragment and activity in the next
-lesson.</p>
-
diff --git a/docs/html/training/basics/intents/sending.jd b/docs/html/training/basics/intents/sending.jd
index 4698ba1..b9463e4 100644
--- a/docs/html/training/basics/intents/sending.jd
+++ b/docs/html/training/basics/intents/sending.jd
@@ -153,7 +153,8 @@ java.util.List} is not empty, you can safely use the intent. For example:</p>
<pre>
PackageManager packageManager = {@link android.content.Context#getPackageManager()};
-List&lt;ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
+List<ResolveInfo> activities = packageManager.queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;
</pre>
diff --git a/docs/html/training/graphics/opengl/draw.jd b/docs/html/training/graphics/opengl/draw.jd
index ba00627..a588066 100644
--- a/docs/html/training/graphics/opengl/draw.jd
+++ b/docs/html/training/graphics/opengl/draw.jd
@@ -50,13 +50,21 @@ android.opengl.GLSurfaceView.Renderer#onSurfaceCreated onSurfaceCreated()} metho
for memory and processing efficiency.</p>
<pre>
-public void onSurfaceCreated(GL10 unused, EGLConfig config) {
+public class MyGLRenderer implements GLSurfaceView.Renderer {
+
...
+ private Triangle mTriangle;
+ private Square mSquare;
+
+ public void onSurfaceCreated(GL10 unused, EGLConfig config) {
+ ...
- // initialize a triangle
- mTriangle = new Triangle();
- // initialize a square
- mSquare = new Square();
+ // initialize a triangle
+ mTriangle = new Triangle();
+ // initialize a square
+ mSquare = new Square();
+ }
+ ...
}
</pre>
@@ -77,21 +85,27 @@ one or more shapes.</li>
<p>You need at least one vertex shader to draw a shape and one fragment shader to color that shape.
These shaders must be complied and then added to an OpenGL ES program, which is then used to draw
-the shape. Here is an example of how to define basic shaders you can use to draw a shape:</p>
+the shape. Here is an example of how to define basic shaders you can use to draw a shape in the
+<code>Triangle</code> class:</p>
<pre>
-private final String vertexShaderCode =
- "attribute vec4 vPosition;" +
- "void main() {" +
- " gl_Position = vPosition;" +
- "}";
-
-private final String fragmentShaderCode =
- "precision mediump float;" +
- "uniform vec4 vColor;" +
- "void main() {" +
- " gl_FragColor = vColor;" +
- "}";
+public class Triangle {
+
+ private final String vertexShaderCode =
+ "attribute vec4 vPosition;" +
+ "void main() {" +
+ " gl_Position = vPosition;" +
+ "}";
+
+ private final String fragmentShaderCode =
+ "precision mediump float;" +
+ "uniform vec4 vColor;" +
+ "void main() {" +
+ " gl_FragColor = vColor;" +
+ "}";
+
+ ...
+}
</pre>
<p>Shaders contain OpenGL Shading Language (GLSL) code that must be compiled prior to using it in
@@ -125,13 +139,28 @@ get created once and then cached for later use.</p>
public class Triangle() {
...
- int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
- int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
+ private final int mProgram;
+
+ public Triangle() {
+ ...
+
+ int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
+ vertexShaderCode);
+ int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
+ fragmentShaderCode);
+
+ // create empty OpenGL ES Program
+ mProgram = GLES20.glCreateProgram();
- mProgram = GLES20.glCreateProgram(); // create empty OpenGL ES Program
- GLES20.glAttachShader(mProgram, vertexShader); // add the vertex shader to program
- GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
- GLES20.glLinkProgram(mProgram); // creates OpenGL ES program executables
+ // add the vertex shader to program
+ GLES20.glAttachShader(mProgram, vertexShader);
+
+ // add the fragment shader to program
+ GLES20.glAttachShader(mProgram, fragmentShader);
+
+ // creates OpenGL ES program executables
+ GLES20.glLinkProgram(mProgram);
+ }
}
</pre>
@@ -145,6 +174,12 @@ color values to the shape’s vertex shader and fragment shader, and then execut
function.</p>
<pre>
+private int mPositionHandle;
+private int mColorHandle;
+
+private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
+private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
+
public void draw() {
// Add program to OpenGL ES environment
GLES20.glUseProgram(mProgram);
@@ -176,8 +211,17 @@ public void draw() {
<p>Once you have all this code in place, drawing this object just requires a call to the
{@code draw()} method from within your renderer’s {@link
-android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method. When you run the
-application, it should look something like this:</p>
+android.opengl.GLSurfaceView.Renderer#onDrawFrame onDrawFrame()} method:
+
+<pre>
+public void onDrawFrame(GL10 unused) {
+ ...
+
+ mTriangle.draw();
+}
+</pre>
+
+<p>When you run the application, it should look something like this:</p>
<img src="{@docRoot}images/opengl/ogl-triangle.png">
<p class="img-caption">
diff --git a/docs/html/training/graphics/opengl/environment.jd b/docs/html/training/graphics/opengl/environment.jd
index 6b00c76..cf2b64a 100644
--- a/docs/html/training/graphics/opengl/environment.jd
+++ b/docs/html/training/graphics/opengl/environment.jd
@@ -129,28 +129,22 @@ just create an inner class in the activity that uses it:</p>
<pre>
class MyGLSurfaceView extends GLSurfaceView {
+ private final MyGLRenderer mRenderer;
+
public MyGLSurfaceView(Context context){
super(context);
+ // Create an OpenGL ES 2.0 context
+ setEGLContextClientVersion(2);
+
+ mRenderer = new MyGLRenderer();
+
// Set the Renderer for drawing on the GLSurfaceView
- setRenderer(new MyRenderer());
+ setRenderer(mRenderer);
}
}
</pre>
-<p>When using OpenGL ES 2.0, you must add another call to your {@link android.opengl.GLSurfaceView}
-constructor, specifying that you want to use the 2.0 API:</p>
-
-<pre>
-// Create an OpenGL ES 2.0 context
-setEGLContextClientVersion(2);
-</pre>
-
-<p class="note"><strong>Note:</strong> If you are using the OpenGL ES 2.0 API, make sure you declare
-this in your application manifest. For more information, see <a href="#manifest">Declare OpenGL ES
-Use
-in the Manifest</a>.</p>
-
<p>One other optional addition to your {@link android.opengl.GLSurfaceView} implementation is to set
the render mode to only draw the view when there is a change to your drawing data using the
{@link android.opengl.GLSurfaceView#RENDERMODE_WHEN_DIRTY GLSurfaceView.RENDERMODE_WHEN_DIRTY}
@@ -186,7 +180,7 @@ the geometry of the view changes, for example when the device's screen orientati
</ul>
<p>Here is a very basic implementation of an OpenGL ES renderer, that does nothing more than draw a
-gray background in the {@link android.opengl.GLSurfaceView}:</p>
+black background in the {@link android.opengl.GLSurfaceView}:</p>
<pre>
public class MyGLRenderer implements GLSurfaceView.Renderer {
@@ -208,7 +202,7 @@ public class MyGLRenderer implements GLSurfaceView.Renderer {
</pre>
<p>That’s all there is to it! The code examples above create a simple Android application that
-displays a gray screen using OpenGL. While this code does not do anything very interesting, by
+displays a black screen using OpenGL. While this code does not do anything very interesting, by
creating these classes, you have laid the foundation you need to start drawing graphic elements with
OpenGL.</p>
diff --git a/docs/html/training/graphics/opengl/motion.jd b/docs/html/training/graphics/opengl/motion.jd
index fbcdd7f..b026a4a 100644
--- a/docs/html/training/graphics/opengl/motion.jd
+++ b/docs/html/training/graphics/opengl/motion.jd
@@ -45,16 +45,17 @@ to a shape with rotation.</p>
<h2 id="rotate">Rotate a Shape</h2>
-<p>Rotating a drawing object with OpenGL ES 2.0 is relatively simple. You create another
-transformation matrix (a rotation matrix) and then combine it with your projection and
+<p>Rotating a drawing object with OpenGL ES 2.0 is relatively simple. In your renderer, create
+another transformation matrix (a rotation matrix) and then combine it with your projection and
camera view transformation matrices:</p>
<pre>
private float[] mRotationMatrix = new float[16];
public void onDrawFrame(GL10 gl) {
- ...
float[] scratch = new float[16];
+ ...
+
// Create a rotation transformation for the triangle
long time = SystemClock.uptimeMillis() % 4000L;
float angle = 0.090f * ((int) time);
diff --git a/docs/html/training/graphics/opengl/projection.jd b/docs/html/training/graphics/opengl/projection.jd
index b09e74c..356d5d4 100644
--- a/docs/html/training/graphics/opengl/projection.jd
+++ b/docs/html/training/graphics/opengl/projection.jd
@@ -71,6 +71,11 @@ projection transformation {@link android.opengl.Matrix} using the {@link
android.opengl.Matrix#frustumM Matrix.frustumM()} method:</p>
<pre>
+// mMVPMatrix is an abbreviation for "Model View Projection Matrix"
+private final float[] mMVPMatrix = new float[16];
+private final float[] mProjectionMatrix = new float[16];
+private final float[] mViewMatrix = new float[16];
+
&#64;Override
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
@@ -95,10 +100,10 @@ view transformation in order for anything to show up on screen.</p>
<h2 id="camera-view">Define a Camera View</h2>
<p>Complete the process of transforming your drawn objects by adding a camera view transformation as
-part of the drawing process. In the following example code, the camera view transformation is
-calculated using the {@link android.opengl.Matrix#setLookAtM Matrix.setLookAtM()} method and then
-combined with the previously calculated projection matrix. The combined transformation matrices
-are then passed to the drawn shape.</p>
+part of the drawing process in your renderer. In the following example code, the camera view
+transformation is calculated using the {@link android.opengl.Matrix#setLookAtM Matrix.setLookAtM()}
+method and then combined with the previously calculated projection matrix. The combined
+transformation matrices are then passed to the drawn shape.</p>
<pre>
&#64;Override
@@ -119,7 +124,32 @@ public void onDrawFrame(GL10 unused) {
<h2 id="#transform">Apply Projection and Camera Transformations</h2>
<p>In order to use the combined projection and camera view transformation matrix shown in the
-previews sections, modify the {@code draw()} method of your graphic objects to accept the combined
+previews sections, first add a matrix variable to the <em>vertex shader</em> previously defined
+in the <code>Triangle</code> class:</p>
+
+<pre>
+public class Triangle {
+
+ private final String vertexShaderCode =
+ // This matrix member variable provides a hook to manipulate
+ // the coordinates of the objects that use this vertex shader
+ <strong>"uniform mat4 uMVPMatrix;" +</strong>
+ "attribute vec4 vPosition;" +
+ "void main() {" +
+ // the matrix must be included as a modifier of gl_Position
+ // Note that the uMVPMatrix factor *must be first* in order
+ // for the matrix multiplication product to be correct.
+ " gl_Position = <strong>uMVPMatrix</strong> * vPosition;" +
+ "}";
+
+ // Use to access and set the view transformation
+ private int mMVPMatrixHandle;
+
+ ...
+}
+</pre>
+
+<p>Next, modify the {@code draw()} method of your graphic objects to accept the combined
transformation matrix and apply it to the shape:</p>
<pre>
@@ -127,14 +157,16 @@ public void draw(float[] mvpMatrix) { // pass in the calculated transformation m
...
// get handle to shape's transformation matrix
- mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
+ <strong>mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");</strong>
// Pass the projection and view transformation to the shader
- GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
+ <strong>GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);</strong>
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
- ...
+
+ // Disable vertex array
+ GLES20.glDisableVertexAttribArray(mPositionHandle);
}
</pre>
diff --git a/docs/html/training/graphics/opengl/touch.jd b/docs/html/training/graphics/opengl/touch.jd
index 4c9f0c7..089ede7 100644
--- a/docs/html/training/graphics/opengl/touch.jd
+++ b/docs/html/training/graphics/opengl/touch.jd
@@ -50,6 +50,10 @@ android.opengl.GLSurfaceView#onTouchEvent onTouchEvent()} to listen for touch ev
an angle of rotation for a shape.</p>
<pre>
+private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
+private float mPreviousX;
+private float mPreviousY;
+
&#64;Override
public boolean onTouchEvent(MotionEvent e) {
// MotionEvent reports input details from the touch screen
@@ -77,7 +81,7 @@ public boolean onTouchEvent(MotionEvent e) {
mRenderer.setAngle(
mRenderer.getAngle() +
- ((dx + dy) * TOUCH_SCALE_FACTOR); // = 180.0f / 320
+ ((dx + dy) * TOUCH_SCALE_FACTOR));
requestRender();
}
@@ -108,12 +112,22 @@ public MyGLSurfaceView(Context context) {
<p>The example code above requires that you expose the rotation angle through your renderer by
adding a public member. Since the renderer code is running on a separate thread from the main user
interface thread of your application, you must declare this public variable as {@code volatile}.
-Here is the code to do that:</p>
+Here is the code to declare the variable and expose the getter and setter pair:</p>
<pre>
public class MyGLRenderer implements GLSurfaceView.Renderer {
...
+
public volatile float mAngle;
+
+ public float getAngle() {
+ return mAngle;
+ }
+
+ public void setAngle(float angle) {
+ mAngle = angle;
+ }
+}
</pre>
diff --git a/docs/html/training/location/display-address.jd b/docs/html/training/location/display-address.jd
index 621b082..516f14f 100644
--- a/docs/html/training/location/display-address.jd
+++ b/docs/html/training/location/display-address.jd
@@ -1,280 +1,468 @@
page.title=Displaying a Location Address
-
trainingnavtop=true
-
@jd:body
-
-
<div id="tb-wrapper">
-<div id="tb">
+ <div id="tb">
-<h2>This lesson teaches you to</h2>
-<ol>
- <li><a href="#DefineTask">Define the Address Lookup Task</a></li>
- <li><a href="#DisplayResults">Define a Method to Display the Results</a></li>
- <li><a href="#RunTask">Run the Lookup Task</a></li>
-</ol>
+ <h2>This lesson teaches you how to</h2>
+ <ol>
+ <li><a href="#connect">Get a Geographic Location</a></li>
+ <li><a href="#fetch-address">Define an Intent Service to Fetch the
+ Address</a></li>
+ <li><a href="#start-intent">Start the Intent Service</a></li>
+ <li><a href="#result-receiver">Receive the Geocoding Results</a></li>
+ </ol>
-<h2>You should also read</h2>
-<ul>
- <li>
- <a href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>
- </li>
- <li>
- <a href="retrieve-current.html">Retrieving the Current Location</a>
- </li>
- <li>
- <a href="receive-location-updates.html">Receiving Location Updates</a>
- </li>
-</ul>
-<h2>Try it out</h2>
+ <h2>You should also read</h2>
+ <ul>
+ <li>
+ <a href="{@docRoot}google/play-services/setup.html">Setting up Google
+ Play Services</a>
+ </li>
+ <li>
+ <a href="retrieve-current.html">Getting the Last Known Location</a>
+ </li>
+ <li>
+ <a href="receive-location-updates.html">Receiving Location Updates</a>
+ </li>
+ </ul>
+ <h2>Try it out</h2>
-<div class="download-box">
-<a href="http://developer.android.com/shareables/training/LocationUpdates.zip" class="button">Download
- the sample app</a>
-<p class="filename">LocationUpdates.zip</p>
+ <ul>
+ <li>
+ <a href="https://github.com/googlesamples/android-play-location/tree/master/LocationAddress" class="external-link">LocationAddress</a>
+ </li>
+ </ul>
+ </div>
</div>
-</div>
-</div>
+<p>The lessons <a href="retrieve-current.html">Getting the Last Known
+ Location</a> and <a href="receive-location-updates.html">Receiving Location
+ Updates</a> describe how to get the user's location in the form of a
+ {@link android.location.Location} object that contains latitude and longitude
+ coordinates. Although latitude and longitude are useful for calculating
+ distance or displaying a map position, in many cases the address of the
+ location is more useful. For example, if you want to let your users know where
+ they are or what is close by, a street address is more meaningful than the
+ geographic coordinates (latitude/longitude) of the location.</p>
+
+<p>Using the {@link android.location.Geocoder} class in the Android framework
+ location APIs, you can convert an address to the corresponding geographic
+ coordinates. This process is called <em>geocoding</em>. Alternatively, you can
+ convert a geographic location to an address. The address lookup feature is
+ also known as <em>reverse geocoding</em>.</p>
+
+<p>This lesson shows you how to use the
+ {@link android.location.Geocoder#getFromLocation getFromLocation()} method to
+ convert a geographic location to an address. The method returns an estimated
+ street address corresponding to a given latitude and longitude.</p>
+
+<h2 id="connect">Get a Geographic Location</h2>
+
+<p>The last known location of the device is a useful starting point for the
+ address lookup feature. The lesson on
+ <a href="retrieve-current.html">Getting the Last Known Location</a> shows you
+ how to use the
+ <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html#getLastLocation(com.google.android.gms.common.api.GoogleApiClient)">{@code getLastLocation()}</a>
+ method provided by the
+ <a href="{@docRoot}reference/com/google/android/gms/location/FusedLocationProviderApi.html">fused
+ location provider</a> to find the latest location of the device.</p>
+
+<p>To access the fused location provider, you need to create an instance of the
+ Google Play services API client. To learn how to connect your client, see
+ <a href="{@docRoot}training/location/retrieve-current.html#play-services">Connect
+ to Google Play Services</a>.</p>
+
+<p>In order for the fused location provider to retrieve a precise street
+ address, set the location permission in your app manifest to
+ {@code ACCESS_FINE_LOCATION}, as shown in the following example:</p>
-<p>
- The lessons <a href="retrieve-current.html">Retrieving the Current Location</a> and
- <a href="receive-location-updates.html">Receiving Location Updates</a> describe how to get the
- user's current location in the form of a {@link android.location.Location} object that
- contains latitude and longitude coordinates. Although latitude and longitude are useful for
- calculating distance or displaying a map position, in many cases the address of the location is
- more useful.
-</p>
-<p>
- The Android platform API provides a feature that returns an estimated street addresses for
- latitude and longitude values. This lesson shows you how to use this address lookup feature.
-</p>
-<p class="note">
- <strong>Note:</strong> Address lookup requires a backend service that is not included in the
- core Android framework. If this backend service is not available,
- {@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()} returns an empty
- list. The helper method {@link android.location.Geocoder#isPresent isPresent()}, available
- in API level 9 and later, checks to see if the backend service is available.
-</p>
-<p>
- The snippets in the following sections assume that your app has already retrieved the
- current location and stored it as a {@link android.location.Location} object in the global
- variable {@code mLocation}.
-</p>
-<!--
- Define the address lookup task
--->
-<h2 id="DefineTask">Define the Address Lookup Task</h2>
-<p>
-To get an address for a given latitude and longitude, call
-{@link android.location.Geocoder#getFromLocation Geocoder.getFromLocation()}, which returns a
-list of addresses. The method is synchronous, and may take a long time to do its work, so you
-should call the method from the {@link android.os.AsyncTask#doInBackground
-doInBackground()} method of an {@link android.os.AsyncTask}.
-</p>
-<p>
-While your app is getting the address, display an indeterminate activity
-indicator to show that your app is working in the background. Set the indicator's initial state
-to {@code android:visibility="gone"}, to make it invisible and remove it from the layout
-hierarchy. When you start the address lookup, you set its visibility to "visible".
-</p>
-<p>
-The following snippet shows how to add an indeterminate {@link android.widget.ProgressBar} to
-your layout file:
-</p>
<pre>
-&lt;ProgressBar
-android:id="&#64;+id/address_progress"
-android:layout_width="wrap_content"
-android:layout_height="wrap_content"
-android:layout_centerHorizontal="true"
-android:indeterminate="true"
-android:visibility="gone" /&gt;
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.gms.location.sample.locationupdates" &gt;
+
+ &lt;uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/&gt;
+&lt;/manifest&gt;
</pre>
-<p>
-To create the background task, define a subclass of {@link android.os.AsyncTask} that calls
-{@link android.location.Geocoder#getFromLocation getFromLocation()} and returns an address.
-Define a {@link android.widget.TextView} object {@code mAddress} to contain the returned
-address, and a {@link android.widget.ProgressBar} object that allows you to control the
-indeterminate activity indicator. For example:
-</p>
+
+<h2 id="fetch-address">Define an Intent Service to Fetch the Address</h2>
+
+<p>The {@link android.location.Geocoder#getFromLocation getFromLocation()}
+ method provided by the {@link android.location.Geocoder} class accepts a
+ latitude and longitude, and returns a list of addresses. The method is
+ synchronous, and may take a long time to do its work, so you should not call
+ it from the main, user interface (UI) thread of your app.</p>
+
+<p>The {@link android.app.IntentService IntentService} class provides a
+ structure for running a task on a background thread. Using this class, you can
+ handle a long-running operation without affecting your UI's responsiveness.
+ Note that the {@link android.os.AsyncTask AsyncTask} class also allows you to
+ perform background operations, but it's designed for short operations. An
+ {@link android.os.AsyncTask AsyncTask} shouldn't keep a reference to the UI if
+ the activity is recreated, for example when the device is rotated. In
+ contrast, an {@link android.app.IntentService IntentService} doesn't need to
+ be cancelled when the activity is rebuilt.</p>
+
+<p>Define a {@code FetchAddressIntentService} class that extends
+ {@link android.app.IntentService}. This class is your address lookup service.
+ The intent service handles an intent asynchronously on a worker thread, and
+ stops itself when it runs out of work. The intent extras provide the data
+ needed by the service, including a {@link android.location.Location} object
+ for conversion to an address, and a {@link android.os.ResultReceiver} object
+ to handle the results of the address lookup. The service uses a {@link
+ android.location.Geocoder} to fetch the address for the location, and sends
+ the results to the {@link android.os.ResultReceiver}.</p>
+
+<h3>Define the Intent Service in your App Manifest</h3>
+
+<p>Add an entry to your app manifest defining the intent service:</p>
+
<pre>
-public class MainActivity extends FragmentActivity {
+&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.android.gms.location.sample.locationaddress" &gt;
+ &lt;application
+ ...
+ &lt;service
+ android:name=".FetchAddressIntentService"
+ android:exported="false"/&gt;
+ &lt;/application&gt;
...
- private TextView mAddress;
- private ProgressBar mActivityIndicator;
+&lt;/manifest&gt;
+</pre>
+
+<p class="note"><strong>Note:</strong> The {@code &lt;service&gt;} element in
+ the manifest doesn't need to include an intent filter, because your main
+ activity creates an explicit intent by specifying the name of the class to use
+ for the intent.</p>
+
+<h3>Create a Geocoder</h3>
+
+<p>The process of converting a geographic location to an address is called
+ <em>reverse geocoding</em>. To perform the main work of the intent service,
+ that is, your reverse geocoding request, implement
+ {@link android.app.IntentService#onHandleIntent onHandleIntent()} within the
+ {@code FetchAddressIntentService} class. Create a
+ {@link android.location.Geocoder} object to handle the reverse geocoding.</p>
+
+<p>A locale represents a specific geographical or linguistic region. Locale
+ objects are used to adjust the presentation of information, such as numbers or
+ dates, to suit the conventions in the region represented by the locale. Pass a
+ <a href="{@docRoot}reference/java/util/Locale.html">{@code Locale}</a> object
+ to the {@link android.location.Geocoder} object, to ensure that the resulting
+ address is localized to the user's geographic region.</p>
+
+<pre>
+&#64;Override
+protected void onHandleIntent(Intent intent) {
+ Geocoder geocoder = new Geocoder(this, Locale.getDefault());
...
- &#64;Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+}
+</pre>
+
+<h3 id="retrieve-street-address">Retrieve the street address data</h3>
+
+<p>The next step is to retrieve the street address from the geocoder, handle
+ any errors that may occur, and send the results back to the activity that
+ requested the address. To report the results of the geocoding
+ process, you need two numeric constants that indicate success or failure.
+ Define a {@code Constants} class to contain the values, as shown in this code
+ snippet:</p>
+
+<pre>
+public final class Constants {
+ public static final int SUCCESS_RESULT = 0;
+ public static final int FAILURE_RESULT = 1;
+ public static final String PACKAGE_NAME =
+ "com.google.android.gms.location.sample.locationaddress";
+ public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER";
+ public static final String RESULT_DATA_KEY = PACKAGE_NAME +
+ ".RESULT_DATA_KEY";
+ public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
+ ".LOCATION_DATA_EXTRA";
+}
+</pre>
+
+<p>To get a street address corresponding to a geographical location, call
+ {@link android.location.Geocoder#getFromLocation getFromLocation()},
+ passing it the latitude and longitude from the location object, and the
+ maximum number of addresses you want returned. In this case, you want just one
+ address. The geocoder returns an array of addresses. If no addresses were
+ found to match the given location, it returns an empty list. If there is no
+ backend geocoding service available, the geocoder returns null.</p>
+
+<p>Check for the following errors as shown in the code sample below. If an error
+ occurs, place the corresponding error message in the {@code errorMessage}
+ variable, so you can send it back to the requesting activity:</p>
+
+<ul>
+ <li><strong>No location data provided</strong> - The intent extras do not
+ include the {@link android.location.Location} object required for reverse
+ geocoding.</li>
+ <li><strong>Invalid latitude or longitude used</strong> - The latitude
+ and/or longitude values provided in the {@link android.location.Location}
+ object are invalid.</li>
+ <li><strong>No geocoder available</strong> - The background geocoding service
+ is not available, due to a network error or IO exception.</li>
+ <li><strong>Sorry, no address found</strong> - The geocoder could not find an
+ address for the given latitude/longitude.</li>
+</ul>
+
+<p>To get the individual lines of an address object, use the
+ {@link android.location.Address#getAddressLine getAddressLine()}
+ method provided by the {@link android.location.Address} class. Then join the
+ lines into a list of address fragments ready to return to the activity that
+ requested the address.</p>
+
+<p>To send the results back to the requesting activity, call the
+ {@code deliverResultToReceiver()} method (defined in
+ <a href="#return-address">Return the address to the requestor</a>). The
+ results consist of the previously-mentioned numeric success/failure code and
+ a string. In the case of a successful reverse geocoding, the string contains
+ the address. In the case of a failure, the string contains the error message,
+ as shown in the code sample below:</p>
+
+<pre>
+&#64;Override
+protected void onHandleIntent(Intent intent) {
+ String errorMessage = "";
+
+ // Get the location passed to this service through an extra.
+ Location location = intent.getParcelableExtra(
+ Constants.LOCATION_DATA_EXTRA);
+
...
- mAddress = (TextView) findViewById(R.id.address);
- mActivityIndicator =
- (ProgressBar) findViewById(R.id.address_progress);
+
+ List&lt;Address&gt; addresses = null;
+
+ try {
+ addresses = geocoder.getFromLocation(
+ location.getLatitude(),
+ location.getLongitude(),
+ // In this sample, get just a single address.
+ 1);
+ } catch (IOException ioException) {
+ // Catch network or other I/O problems.
+ errorMessage = getString(R.string.service_not_available);
+ Log.e(TAG, errorMessage, ioException);
+ } catch (IllegalArgumentException illegalArgumentException) {
+ // Catch invalid latitude or longitude values.
+ errorMessage = getString(R.string.invalid_lat_long_used);
+ Log.e(TAG, errorMessage + ". " +
+ "Latitude = " + location.getLatitude() +
+ ", Longitude = " +
+ location.getLongitude(), illegalArgumentException);
}
- ...
- /**
- * A subclass of AsyncTask that calls getFromLocation() in the
- * background. The class definition has these generic types:
- * Location - A {@link android.location.Location} object containing
- * the current location.
- * Void - indicates that progress units are not used
- * String - An address passed to onPostExecute()
- */
- private class GetAddressTask extends
- AsyncTask&lt;Location, Void, String&gt; {
- Context mContext;
- public GetAddressTask(Context context) {
- super();
- mContext = context;
+
+ // Handle case where no address was found.
+ if (addresses == null || addresses.size() == 0) {
+ if (errorMessage.isEmpty()) {
+ errorMessage = getString(R.string.no_address_found);
+ Log.e(TAG, errorMessage);
}
- ...
- /**
- * Get a Geocoder instance, get the latitude and longitude
- * look up the address, and return it
- *
- * &#64;params params One or more Location objects
- * &#64;return A string containing the address of the current
- * location, or an empty string if no address can be found,
- * or an error message
- */
- &#64;Override
- protected String doInBackground(Location... params) {
- Geocoder geocoder =
- new Geocoder(mContext, Locale.getDefault());
- // Get the current location from the input parameter list
- Location loc = params[0];
- // Create a list to contain the result address
- List&lt;Address&gt; addresses = null;
- try {
- /*
- * Return 1 address.
- */
- addresses = geocoder.getFromLocation(loc.getLatitude(),
- loc.getLongitude(), 1);
- } catch (IOException e1) {
- Log.e("LocationSampleActivity",
- "IO Exception in getFromLocation()");
- e1.printStackTrace();
- return ("IO Exception trying to get address");
- } catch (IllegalArgumentException e2) {
- // Error message to post in the log
- String errorString = "Illegal arguments " +
- Double.toString(loc.getLatitude()) +
- " , " +
- Double.toString(loc.getLongitude()) +
- " passed to address service";
- Log.e("LocationSampleActivity", errorString);
- e2.printStackTrace();
- return errorString;
- }
- // If the reverse geocode returned an address
- if (addresses != null &amp;&amp; addresses.size() &gt; 0) {
- // Get the first address
- Address address = addresses.get(0);
- /*
- * Format the first line of address (if available),
- * city, and country name.
- */
- String addressText = String.format(
- "&#037;s, &#037;s, &#037;s",
- // If there's a street address, add it
- address.getMaxAddressLineIndex() &gt; 0 ?
- address.getAddressLine(0) : "",
- // Locality is usually a city
- address.getLocality(),
- // The country of the address
- address.getCountryName());
- // Return the text
- return addressText;
- } else {
- return "No address found";
- }
+ deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);
+ } else {
+ Address address = addresses.get(0);
+ ArrayList&lt;String&gt; addressFragments = new ArrayList&lt;String&gt;();
+
+ // Fetch the address lines using {@code getAddressLine},
+ // join them, and send them to the thread.
+ for(int i = 0; i < address.getMaxAddressLineIndex(); i++) {
+ addressFragments.add(address.getAddressLine(i));
}
- ...
+ Log.i(TAG, getString(R.string.address_found));
+ deliverResultToReceiver(Constants.SUCCESS_RESULT,
+ TextUtils.join(System.getProperty("line.separator"),
+ addressFragments));
}
- ...
}
</pre>
-<p>
-The next section shows you how to display the address in the user interface.
-</p>
-<!-- Define a method to display the address -->
-<h2 id="DisplayResults">Define a Method to Display the Results</h2>
-<p>
- {@link android.os.AsyncTask#doInBackground doInBackground()} returns the result of the address
- lookup as a {@link java.lang.String}. This value is passed to
- {@link android.os.AsyncTask#onPostExecute onPostExecute()}, where you do further processing
- on the results. Since {@link android.os.AsyncTask#onPostExecute onPostExecute()}
- runs on the UI thread, it can update the user interface; for example, it can turn off the
- activity indicator and display the results to the user:
+
+<h3 id="return-address">Return the address to the requestor</h3>
+
+<p>The final thing the intent service must do is send the address back to a
+ {@link android.os.ResultReceiver} in the activity that started the service.
+ The {@link android.os.ResultReceiver} class allows you to send a
+ numeric result code as well as a message containing the result data. The
+ numeric code is useful for reporting the success or failure of the geocoding
+ request. In the case of a successful reverse geocoding, the message contains
+ the address. In the case of a failure, the message contains some text
+ describing the reason for failure.</p>
+
+<p>You have already retrieved the address from the geocoder, trapped any errors
+ that may occur, and called the {@code deliverResultToReceiver()} method. Now
+ you need to define the {@code deliverResultToReceiver()} method that sends
+ a result code and message bundle to the result receiver.</p>
+
+<p>For the result code, use the value that you've passed to the
+ {@code deliverResultToReceiver()} method in the {@code resultCode} parameter.
+ To construct the message bundle, concatenate the {@code RESULT_DATA_KEY}
+ constant from your {@code Constants} class (defined in
+ <a href="#retrieve-street-address">Retrieve the street address data</a>) and
+ the value in the {@code message} parameter passed to the
+ {@code deliverResultToReceiver()} method, as shown in the following sample:
</p>
+
<pre>
- private class GetAddressTask extends
- AsyncTask&lt;Location, Void, String&gt; {
- ...
- /**
- * A method that's called once doInBackground() completes. Turn
- * off the indeterminate activity indicator and set
- * the text of the UI element that shows the address. If the
- * lookup failed, display the error message.
- */
- &#64;Override
- protected void onPostExecute(String address) {
- // Set activity indicator visibility to "gone"
- mActivityIndicator.setVisibility(View.GONE);
- // Display the results of the lookup.
- mAddress.setText(address);
- }
- ...
+public class FetchAddressIntentService extends IntentService {
+ protected ResultReceiver mReceiver;
+ ...
+ private void deliverResultToReceiver(int resultCode, String message) {
+ Bundle bundle = new Bundle();
+ bundle.putString(Constants.RESULT_DATA_KEY, message);
+ mReceiver.send(resultCode, bundle);
}
+}
</pre>
-<p>
- The final step is to run the address lookup.
-</p>
-<!-- Get and display the address -->
-<h2 id="RunTask">Run the Lookup Task</h2>
-<p>
- To get the address, call {@link android.os.AsyncTask#execute execute()}. For example, the
- following snippet starts the address lookup when the user clicks the "Get Address" button:
-</p>
+
+<h2 id="start-intent">Start the Intent Service</h2>
+
+<p>The intent service, as defined in the previous section, runs in the
+ background and is responsible for fetching the address corresponding to a
+ given geographic location. When you start the service, the Android framework
+ instantiates and starts the service if it isn't already running, and creates a
+ process if needed. If the service is already running then it remains running.
+ Because the service extends {@link android.app.IntentService IntentService},
+ it shuts down automatically when all intents have been processed.</p>
+
+<p>Start the service from your app's main activity,
+ and create an {@link android.content.Intent} to pass data to the service. You
+ need an <em>explicit</em> intent, because you want only your service
+ to respond to the intent. For more information, see
+ <a href="{@docRoot}guide/components/intents-filters.html#Types">Intent
+ Types</a>.</p>
+
+<p>To create an explicit intent, specify the name of the
+ class to use for the service: {@code FetchAddressIntentService.class}.
+ Pass two pieces of information in the intent extras:</p>
+
+<ul>
+ <li>A {@link android.os.ResultReceiver} to handle the results of the address
+ lookup.</li>
+ <li>A {@link android.location.Location} object containing the latitude and
+ longitude that you want to convert to an address.</li>
+</ul>
+
+<p>The following code sample shows you how to start the intent service:</p>
+
<pre>
-public class MainActivity extends FragmentActivity {
+public class MainActivity extends ActionBarActivity implements
+ ConnectionCallbacks, OnConnectionFailedListener {
+
+ protected Location mLastLocation;
+ private AddressResultReceiver mResultReceiver;
...
- /**
- * The "Get Address" button in the UI is defined with
- * android:onClick="getAddress". The method is invoked whenever the
- * user clicks the button.
- *
- * &#64;param v The view object associated with this method,
- * in this case a Button.
- */
- public void getAddress(View v) {
- // Ensure that a Geocoder services is available
- if (Build.VERSION.SDK_INT &gt;=
- Build.VERSION_CODES.GINGERBREAD
- &amp;&amp;
- Geocoder.isPresent()) {
- // Show the activity indicator
- mActivityIndicator.setVisibility(View.VISIBLE);
- /*
- * Reverse geocoding is long-running and synchronous.
- * Run it on a background thread.
- * Pass the current location to the background task.
- * When the task finishes,
- * onPostExecute() displays the address.
- */
- (new GetAddressTask(this)).execute(mLocation);
+
+ protected void startIntentService() {
+ Intent intent = new Intent(this, FetchAddressIntentService.class);
+ intent.putExtra(Constants.RECEIVER, mResultReceiver);
+ intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);
+ startService(intent);
+ }
+}
+</pre>
+
+<p>Call the above {@code startIntentService()} method when the
+ user takes an action that requires a geocoding address lookup. For example,
+ the user may press a <em>Fetch address</em> button on your app's UI. Before
+ starting the intent service, you need to check that the connection to Google
+ Play services is present. The following code snippet shows the call to the
+ {@code startIntentService()} method in the button handler:</p>
+
+<pre>
+public void fetchAddressButtonHandler(View view) {
+ // Only start the service to fetch the address if GoogleApiClient is
+ // connected.
+ if (mGoogleApiClient.isConnected() && mLastLocation != null) {
+ startIntentService();
+ }
+ // If GoogleApiClient isn't connected, process the user's request by
+ // setting mAddressRequested to true. Later, when GoogleApiClient connects,
+ // launch the service to fetch the address. As far as the user is
+ // concerned, pressing the Fetch Address button
+ // immediately kicks off the process of getting the address.
+ mAddressRequested = true;
+ updateUIWidgets();
+}
+</pre>
+
+<p>You must also start the intent service when the connection to Google Play
+ services is established, if the user has already clicked the button on your
+ app's UI. The following code snippet shows the call to the
+ {@code startIntentService()} method in the
+ <a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">{@code onConnected()}</a>
+ callback provided by the Google API Client:</p>
+
+<pre>
+public class MainActivity extends ActionBarActivity implements
+ ConnectionCallbacks, OnConnectionFailedListener {
+ ...
+ &#64;Override
+ public void onConnected(Bundle connectionHint) {
+ // Gets the best and most recent location currently available,
+ // which may be null in rare cases when a location is not available.
+ mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
+ mGoogleApiClient);
+
+ if (mLastLocation != null) {
+ // Determine whether a Geocoder is available.
+ if (!Geocoder.isPresent()) {
+ Toast.makeText(this, R.string.no_geocoder_available,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ if (mAddressRequested) {
+ startIntentService();
+ }
}
- ...
}
+}
+</pre>
+
+<h2 id="result-receiver">Receive the Geocoding Results</h2>
+
+<p>The intent service has handled the geocoding request, and uses a
+ {@link android.os.ResultReceiver} to return the results to the activity that
+ made the request. In the activity that makes the request, define an
+ {@code AddressResultReceiver} that extends {@link android.os.ResultReceiver}
+ to handle the response from {@code FetchAddressIntentService}.</p>
+
+<p>The result includes a numeric result code (<code>resultCode</code>) as well
+ as a message containing the result data (<code>resultData</code>). If the
+ reverse geocoding process was successful, the <code>resultData</code> contains
+ the address. In the case of a failure, the <code>resultData</code> contains
+ text describing the reason for failure. For details of the possible errors,
+ see <a href="#return-address">Return the address to the requestor</a>.</p>
+
+<p>Override the
+ {@link android.os.ResultReceiver#onReceiveResult onReceiveResult()} method
+ to handle the results delivered to the result receiver, as shown in the
+ following code sample:</p>
+
+<pre>
+public class MainActivity extends ActionBarActivity implements
+ ConnectionCallbacks, OnConnectionFailedListener {
...
+ class AddressResultReceiver extends ResultReceiver {
+ public AddressResultReceiver(Handler handler) {
+ super(handler);
+ }
+
+ &#64;Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+
+ // Display the address string
+ // or an error message sent from the intent service.
+ mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);
+ displayAddressOutput();
+
+ // Show a toast message if an address was found.
+ if (resultCode == Constants.SUCCESS_RESULT) {
+ showToast(getString(R.string.address_found));
+ }
+
+ }
+ }
}
</pre>
-<p>
- The next lesson, <a href="geofencing.html">Creating and Monitoring Geofences</a>, demonstrates
- how to define locations of interest called <b>geofences</b> and how to use geofence monitoring
- to detect the user's proximity to a location of interest.
-</p>
diff --git a/docs/html/training/material/drawables.jd b/docs/html/training/material/drawables.jd
index 820a004..a2de8e9 100644
--- a/docs/html/training/material/drawables.jd
+++ b/docs/html/training/material/drawables.jd
@@ -73,7 +73,7 @@ app's module:</p>
<pre>
dependencies {
...
- compile 'com.android.support:palette-v7:21.0.+'
+ compile 'com.android.support:palette-v7:21.0.0'
}
</pre>
diff --git a/docs/html/training/multiscreen/screendensities.jd b/docs/html/training/multiscreen/screendensities.jd
index fcb65cc..1fc5904 100644
--- a/docs/html/training/multiscreen/screendensities.jd
+++ b/docs/html/training/multiscreen/screendensities.jd
@@ -28,6 +28,7 @@ next.link=adaptui.html
<ul>
<li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+ <li><a href="{@docRoot}design/style/iconography.html">Iconography</a></li>
<li><a href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design
Guidelines</a></li>
</ul>
@@ -133,6 +134,28 @@ MyProject/
<p>Then, any time you reference <code>&#64;drawable/awesomeimage</code>, the system selects the
appropriate bitmap based on the screen's dpi.</p>
+<p>Place your launcher icons in the <code>mipmap/</code> folders. </p>
+
+<pre>res/...
+ mipmap-ldpi/...
+ <em>finished_launcher_asset</em>.png
+ mipmap-mdpi/...
+ <em>finished_launcher_asset</em>.png
+ mipmap-hdpi/...
+ <em>finished_launcher_asset</em>.png
+ mipmap-xhdpi/...
+ <em>finished_launcher_asset</em>.png
+ mipmap-xxhdpi/...
+ <em>finished_launcher_asset</em>.png
+ mipmap-xxxhdpi/...
+ <em>finished_launcher_asset</em>.png
+</pre>
+
+<p class="note"><strong>Note:</strong> You should place all launcher icons in the
+<code>res/mipmap-[density]/</code> folders, rather than <code>drawable/</code> folders to ensure
+launcher apps use the best resolution icon. For more information about using the mipmap folders, see
+<a href="{@docRoot}tools/project/index.html#mipmap">Managing Projects Overview</a>.</p>
+
<p>For more tips and guidelines for creating icon assets for your application, see the <a
href="{@docRoot}guide/practices/ui_guidelines/icon_design.html">Icon Design
Guidelines</a>.</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 0fcfb9c..c59d8ff 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -437,6 +437,35 @@ 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/transitions/index.html"
+ description=
+ "How to animate state changes in a view hierarchy using transitions."
+ >Animating Views Using Scenes and Transitions</a>
+ </div>
+ <ul>
+ <li><a href="<?cs var:toroot ?>training/transitions/overview.html">
+ The Transitions Framework
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/scenes.html">
+ Creating a Scene
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/transitions.html">
+ Applying a Transition
+ </a>
+ </li>
+ <li><a href="<?cs var:toroot ?>training/transitions/custom-transitions.html">
+ Creating Custom Transitions
+ </a>
+ </li>
+
+ </ul>
+ </li>
+
<li class="nav-section">
<div class="nav-section-header"><a href="<?cs var:toroot ?>training/animation/index.html"
description=
@@ -922,6 +951,10 @@ include the action bar on devices running Android 2.1 or higher."
Creating a Catalog Browser</a>
</li>
<li>
+ <a href="<?cs var:toroot ?>training/tv/playback/card.html">
+ Providing a Card View</a>
+ </li>
+ <li>
<a href="<?cs var:toroot ?>training/tv/playback/details.html"
ja-lang="詳細ビューをビルドする">
Building a Details View</a>
@@ -1854,4 +1887,4 @@ results."
buildToggleLists();
changeNavLang(getLangPref());
//-->
-</script>
+</script> \ No newline at end of file
diff --git a/docs/html/training/transitions/custom-transitions.jd b/docs/html/training/transitions/custom-transitions.jd
new file mode 100644
index 0000000..b64daae
--- /dev/null
+++ b/docs/html/training/transitions/custom-transitions.jd
@@ -0,0 +1,189 @@
+page.title=Creating Custom Transitions
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#Extend">Extend the Transition Class</a></li>
+ <li><a href="#CaptureProperties">Capture View Property Values</a></li>
+ <li><a href="#CreateAnimator">Create a Custom Animator</a></li>
+ <li><a href="#Apply">Apply a Custom Transition</a></li>
+</ol>
+</div>
+</div>
+
+<p>A custom transition enables you to create an animation that is not available from any of
+the built-in transition classes. For example, you can define a custom transition that turns
+the foreground color of text and input fields to gray to indicate that the fields are disabled
+in the new screen. This type of change helps users see the fields you disabled.</p>
+
+<p>A custom transition, like one of the built-in transition types, applies animations to
+child views of both the starting and ending scenes. Unlike built-in transition types,
+however, you have to provide the code that captures property values and generates animations.
+You may also want to define a subset of target views for your animation.</p>
+
+<p>This lesson teaches you to capture property values and generate animations to create
+custom transitions.</p>
+
+
+
+<h2 id="Extend">Extend the Transition Class</h2>
+
+<p>To create a custom transition, add a class to your project that extends the {@link
+android.transition.Transition} class and override the methods shown in the following snippet:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+ &#64;Override
+ public void captureStartValues(TransitionValues values) {}
+
+ &#64;Override
+ public void captureEndValues(TransitionValues values) {}
+
+ &#64;Override
+ public Animator createAnimator(ViewGroup sceneRoot,
+ TransitionValues startValues,
+ TransitionValues endValues) {}
+}
+</pre>
+
+<p>The following sections explain how to override these methods.</p>
+
+
+
+<h2 id="CaptureProperties">Capture View Property Values</h2>
+
+<p>Transition animations use the property animation system described in
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>. Property
+animations change a view property between a starting and ending value over a specified
+period of time, so the framework needs to have both the starting and ending value of
+the property to construct the animation.</p>
+
+<p>However, a property animation usually needs only a small subset of all the view's property
+values. For example, a color animation needs color property values, while a movement
+animation needs position property values. Since the property values needed for an animation
+are specific to a transition, the transitions framework does not provide every property value
+to a transition. Instead, the framework invokes callback methods that allow a transition to
+capture only the property values it needs and store them in the framework.</p>
+
+
+<h3 id="StartingValues">Capturing Starting Values</h3>
+
+<p>To pass the starting view values to the framework, implement the
+{@link android.transition.Transition#captureStartValues captureStartValues(transitionValues)}
+method. The framework calls this method for every view in the starting scene. The method
+argument is a {@link android.transition.TransitionValues} object that contains a reference
+to the view and a {@link java.util.Map} instance in which you can store the view values you
+want. In your implementation, retrieve these property values and pass them back to the
+framework by storing them in the map.</p>
+
+<p>To ensure that the key for a property value does not conflict with other {@link
+android.transition.TransitionValues} keys, use the following naming scheme:</p>
+
+<pre>
+package_name:transition_name:property_name
+</pre>
+
+<p>The following snippet shows an implementation of the {@link
+android.transition.Transition#captureStartValues captureStartValues()} method:</p>
+
+<pre>
+public class CustomTransition extends Transition {
+
+ // Define a key for storing a property value in
+ // TransitionValues.values with the syntax
+ // package_name:transition_class:property_name to avoid collisions
+ private static final String PROPNAME_BACKGROUND =
+ "com.example.android.customtransition:CustomTransition:background";
+
+ &#64;Override
+ public void captureStartValues(TransitionValues transitionValues) {
+ // Call the convenience method captureValues
+ captureValues(transitionValues);
+ }
+
+
+ // For the view in transitionValues.view, get the values you
+ // want and put them in transitionValues.values
+ private void captureValues(TransitionValues transitionValues) {
+ // Get a reference to the view
+ View view = transitionValues.view;
+ // Store its background property in the values map
+ transitionValues.values.put(PROPNAME_BACKGROUND, view.getBackground());
+ }
+ ...
+}
+</pre>
+
+
+<h3 id="EndingValues">Capture Ending Values</h3>
+
+<p>The framework calls the {@link android.transition.Transition#captureEndValues} method
+once for every target view in the ending scene. In all other respects, {@link
+android.transition.Transition#captureEndValues captureEndValues()} works the same as {@link
+android.transition.Transition#captureStartValues captureStartValues()}.</p>
+
+<p>The following code snippet shows an implementation of the {@link
+android.transition.Transition#captureEndValues captureEndValues()} method:</p>
+
+<pre>
+&#64;Override
+public void captureEndValues(TransitionValues transitionValues) {
+ captureValues(transitionValues);
+}
+</pre>
+
+<p>In this example, both the {@link android.transition.Transition#captureStartValues
+captureStartValues()} and {@link android.transition.Transition#captureEndValues captureEndValues()}
+methods invoke <code>captureValues()</code> to retrieve and store values. The view property
+that <code>captureValues()</code> retrieves is the same, but it has different values in the
+starting and ending scenes. The framework maintains separate maps for the starting and ending
+states of a view.</p>
+
+
+
+<h2 id="CreateAnimator">Create a Custom Animator</h2>
+
+<p>To animate the changes to a view between its state in the starting scene and its state in
+the ending scene, you provide an animator by overriding the {@link
+android.transition.Transition#createAnimator createAnimator()} method. When the
+framework calls this method, it passes in the scene root view and the {@link
+android.transition.TransitionValues} objects that contain the starting and ending values
+you captured.</p>
+
+<p>The number of times the framework calls the {@link
+android.transition.Transition#createAnimator createAnimator()} method depends on the changes that
+occur between the starting and ending scenes. For example, consider a fade out/fade in animation
+implemented as a custom transition. If the starting scene has five targets of which two are
+removed from the ending scene, and the ending scene has the three targets from the starting
+scene plus a new target, then the framework calls {@link
+android.transition.Transition#createAnimator createAnimator()} six times: three of the calls
+animate the fading out and fading in of the targets that stay in both scene objects; two more calls
+animate the fading out of the targets removed from the ending scene; and one call
+animates the fading in of the new target in the ending scene.</p>
+
+<p>For target views that exist on both the starting and ending scenes, the framework provides
+a {@link android.transition.TransitionValues} object for both the <code>startValues</code> and
+<code>endValues</code> arguments. For target views that only exist in the starting or the
+ending scene, the framework provides a {@link android.transition.TransitionValues} object
+for the corresponding argument and <code>null</code> for the other.</p>
+
+<p>To implement the {@link android.transition.Transition#createAnimator} method when you create
+a custom transition, use the view property values you captured to create an {@link
+android.animation.Animator} object and return it to the framework. For an example implementation,
+see the <a
+href="{@docRoot}samples/CustomTransition/src/com.example.android.customtransition/ChangeColor.html">
+<code>ChangeColor</code></a> class in the <a href="{@docRoot}samples/CustomTransition/index.html">
+CustomTransition</a> sample. For more information about property animators, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a>.</p>
+
+
+
+<h2 id="Apply">Apply a Custom Transition</h2>
+
+<p>Custom transitions work the same as built-in transitions. You can apply a custom transition
+using a transition manager, as described in <a
+href="{@docRoot}training/transitions/transitions.html#Apply">Applying a Transition</a>.</p>
diff --git a/docs/html/training/transitions/index.jd b/docs/html/training/transitions/index.jd
new file mode 100644
index 0000000..53faa01
--- /dev/null
+++ b/docs/html/training/transitions/index.jd
@@ -0,0 +1,80 @@
+page.title=Animating Views Using Scenes and Transitions
+
+@jd:body
+
+<!-- Sidebox -->
+<div id="tb-wrapper">
+<div id="tb">
+ <h2>Dependencies and Prerequisites</h2>
+ <ul>
+ <li>Android 4.4.2 (API level 19) or higher</li>
+ </ul>
+ <h2>You should also read</h2>
+ <ul>
+ <li><a href="{@docRoot}guide/topics/ui/how-android-draws.html">
+ How Android Draws Views</a></li>
+ </ul>
+ <h2>Try it out</h2>
+ <ul>
+ <li><a href="{@docRoot}samples/BasicTransition/index.html">BasicTransition</a> sample</li>
+ <li><a href="{@docRoot}samples/CustomTransition/index.html">CustomTransition</a> sample</li>
+ </ul>
+</div>
+</div>
+
+<!-- Video box -->
+<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=S3H7nJ4QaD8">
+<div>
+ <h3>Video</h3>
+ <p>DevBytes: Android 4.4 Transitions</p>
+</div>
+</a>
+
+<p>The user interface of an activity often changes in response to user input and other events.
+For example, an activity that contains a form where users can type search queries can hide
+the form when the user submits it and show a list of search results in its place.</p>
+
+<p>To provide visual continuity in these situations, you can animate changes between
+different view hierarchies in your user interface. These animations give users feedback on
+their actions and help them learn how your app works.</p>
+
+<p>Android includes the <em>transitions framework</em>, which enables you to easily
+animate changes between two view hierarchies. The framework animates the views at runtime by
+changing some of their property values over time. The framework includes built-in animations
+for common effects and lets you create custom animations and transition lifecycle callbacks.</p>
+
+<p>This class teaches you to use the built-in animations in the transitions framework to
+animate changes between view hierarchies. This class also covers how to create custom
+animations.</p>
+
+<p class="note"><strong>Note:</strong> For Android versions earlier than 4.4.2 (API level 19)
+but greater than or equal to Android 4.0 (API level 14), use the <code>animateLayoutChanges</code>
+attribute to animate layouts. To learn more, see
+<a href="{@docRoot}guide/topics/graphics/prop-animation.html">Property Animation</a> and
+<a href="{@docRoot}training/animation/layout.html">Animating Layout Changes</a>.</p>
+
+
+<h2>Lessons</h2>
+
+<dl>
+<dt><a href="{@docRoot}training/transitions/overview.html">
+The Transitions Framework</a></dt>
+<dd>
+ Learn the main features and components of the transitions framework.
+</dd>
+<dt><a href="{@docRoot}training/transitions/scenes.html">
+Creating a Scene</a></dt>
+<dd>
+ Learn how to create a scene to store the state of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/transitions.html">
+Applying a Transition</a></dt>
+<dd>
+ Learn how to apply a transition between two scenes of a view hierarchy.
+</dd>
+<dt><a href="{@docRoot}training/transitions/custom-transitions.html">
+Creating Custom Transitions</a></dt>
+<dd>
+ Learn how to create other animation effects not included in the transitions framework.
+</dd>
+</dl>
diff --git a/docs/html/training/transitions/overview.jd b/docs/html/training/transitions/overview.jd
new file mode 100644
index 0000000..044cf16
--- /dev/null
+++ b/docs/html/training/transitions/overview.jd
@@ -0,0 +1,165 @@
+page.title=The Transitions Framework
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson covers</h2>
+<ol>
+ <li><a href="#Overview">Overview</a></li>
+ <li><a href="#Scenes">Scenes</a></li>
+ <li><a href="#Transitions">Transitions</a></li>
+ <li><a href="#Limitations">Limitations</a></li>
+</ol>
+</div>
+</div>
+
+<p>Animating your app's user interface provides more than just visual appeal. Animations
+highlight changes and provide visual cues that help users learn how your app works.</p>
+
+<p>To help you animate a change between one view hierarchy and another, Android provides the
+transitions framework. This framework applies one or more animations to all the views in the
+hierarchies as it changes between them.</p>
+
+<p>The framework has the following features:</p>
+
+<dl>
+<dt><em>Group-level animations</em></dt>
+<dd>Applies one or more animation effects to all of the views in a view hierarchy.</dd>
+<dt><em>Transition-based animation</em></dt>
+<dd>Runs animations based on the changes between starting and ending view property values.</dd>
+<dt><em>Built-in animations</em></dt>
+<dd>Includes predefined animations for common effects such as fade out or movement.</dd>
+
+<!-- Figure 1 - Transitions video -->
+<div style="float:right;margin-left:30px;margin-top:10px">
+<div class="framed-nexus5-port-span-5" style="clear:left;">
+<video class="play-on-hover" height="442" autoplay="" poster="">
+<source src="{@docRoot}images/transitions/transition_sample_video.mp4" type="video/mp4">
+<source src="{@docRoot}images/transitions/transition_sample_video.ogv" type="video/ogg">
+<source src="{@docRoot}images/transitions/transition_sample_video.webm" type="video/webm">
+</video>
+</div>
+<p class="img-caption" style="margin-top:7px;margin-bottom:0px">
+<strong>Figure 1.</strong> Visual cues using user interface animation.</p>
+<div style="margin-top:5px;margin-bottom:20px;font-size:10pt" class="video-instructions">&nbsp;</div>
+</div>
+
+<dt><em>Resource file support</em></dt>
+<dd>Loads view hierarchies and built-in animations from layout resource files.</dd>
+<dt><em>Lifecycle callbacks</em></dt>
+<dd>Defines callbacks that provide finer control over the animation and hierarchy change
+process.</dd>
+</dl>
+
+
+
+<h2 id="Overview">Overview</h2>
+
+<p>The example in Figure 1 shows how an animation provides visual cues to help the user. As the
+app changes from its search entry screen to its search results screen, it fades out views that
+are no longer in use and fades in new views.</p>
+
+<p>This animation is an example of using the transitions framework. The framework
+animates changes to all the views in two view hierarchies. A view hierarchy can be as simple
+as a single view or as complex as a {@link android.view.ViewGroup} containing an elaborate
+tree of views. The framework animates each view by changing one or more of its property values
+over time between the initial or <em>starting</em> view hierarchy and the final or <em>ending</em>
+view hierarchy.</p>
+
+<p>The transitions framework works in parallel with view hierarchies and animations. The
+purpose of the framework is to store the state of view hierarchies, change between these
+hierarchies in order to modify the appearance of the device screen, and animate the change by
+storing and applying animation definitions.</p>
+
+<p>The diagram in Figure 2 illustrates the relationship between view hierarchies, framework
+objects, and animations:</p>
+
+<!-- Figure 2 - diagram -->
+<img src="{@docRoot}images/transitions/transitions_diagram.png"
+ width="506" height="234" alt="" style="margin-top:7px" />
+<p class="img-caption"><strong>Figure 2.</strong> Relationships in the transitions framework.</p>
+
+<p>The transitions framework provides abstractions for scenes, transitions, and transition
+managers. These are described in detail in the following sections. To use the framework, you
+create scenes for the view hierarchies in your app that you plan to change between. Next, you
+create a transition for each animation you want to use. To start the animation between two
+view hierarchies, you use a transition manager specifying the transition to use and the ending
+scene. This procedure is described in detail in the remaining lessons in this class.</p>
+
+
+
+<h2 id="Scenes">Scenes</h2>
+
+<p>A scene stores the state of a view hierarchy, including all its views and their property
+values. A view hierarchy can be a simple view or a complex tree of views and child layouts.
+Storing the view hierarchy state in a scene enables you to transition into that state from
+another scene. The framework provides the {@link android.transition.Scene} class to represent
+a scene.</p>
+
+<p>The transitions framework lets you create scenes from layout resource files or from
+{@link android.view.ViewGroup} objects in your code. Creating a scene in your code is useful
+if you generated a view hierarchy dynamically or if you are modifying it at runtime.</p>
+
+<p>In most cases, you do not create a starting scene explicitly. If you have applied a
+transition, the framework uses the previous ending scene as the starting scene for any
+subsequent transitions. If you have not applied a transition, the framework collects information
+about the views from the current state of the screen.</p>
+
+<p>A scene can also define its own actions that run when you make a scene change. For example,
+this feature is useful for cleaning up view settings after you transition to a scene.</p>
+
+<p>In addition to the view hierarchy and its property values, a scene also stores a reference
+to the parent of the view hierarchy. This root view is called a <strong>scene root</strong>.
+Changes to the scene and animations that affect the scene occur within the scene root.</p>
+
+<p>To learn how to create scenes, see
+<a href="{@docRoot}training/transitions/scenes.html">Creating a Scene</a>.</p>
+
+
+
+<h2 id="Transitions">Transitions</h2>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. Information about the animation
+is stored in a {@link android.transition.Transition} object. To run the animation, you apply the
+transition using a {@link android.transition.TransitionManager} instance. The framework can
+transition between two different scenes or transition to a different state for the current
+scene.</p>
+
+<p>The framework includes a set of built-in transitions for commonly-used animation effects,
+such as fading and resizing views. You can also define your own custom transitions to create
+an animation effect using the APIs in the animations framework. The transitions framework also
+enables you to combine different animation effects in a transition set that contains a group
+of individual built-in or custom transitions.</p>
+
+<p>The transition lifecycle is similar to the activity lifecycle, and it represents the
+transition states that the framework monitors between the start and the completion of an
+animation. At important lifecycle states, the framework invokes callback methods that you can
+implement to make adjustments to your user interface at different phases of the transition.</p>
+
+<p>To learn more about transitions, see
+<a href="{@docRoot}training/transitions/transitions.html">Applying a Transition</a> and
+<a href="{@docRoot}training/transitions/custom-transitions.html">Creating Custom
+Transitions</a>.</p>
+
+
+
+<h2 id="Limitations">Limitations</h2>
+
+<p>This section lists some known limitations of the transitions framework:</p>
+
+<ul>
+<li>Animations applied to a {@link android.view.SurfaceView} may not appear correctly.
+{@link android.view.SurfaceView} instances are updated from a non-UI thread, so the updates
+may be out of sync with the animations of other views.</li>
+<li>Some specific transition types may not produce the desired animation effect when applied
+to a {@link android.view.TextureView}.</li>
+<li>Classes that extend {@link android.widget.AdapterView}, such as
+{@link android.widget.ListView}, manage their child views in ways that are incompatible with
+the transitions framework. If you try to animate a view based on
+{@link android.widget.AdapterView}, the device display may hang.</li>
+<li>If you try to resize a {@link android.widget.TextView} with an animation, the text will
+pop to a new location before the object has completely resized. To avoid this problem, do not
+animate the resizing of views that contain text.</li>
+</ul>
diff --git a/docs/html/training/transitions/scenes.jd b/docs/html/training/transitions/scenes.jd
new file mode 100644
index 0000000..4bf7d0e
--- /dev/null
+++ b/docs/html/training/transitions/scenes.jd
@@ -0,0 +1,211 @@
+page.title=Creating a Scene
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#FromLayout">Create a Scene From a Layout Resource</a></li>
+ <li><a href="#FromCode">Create a Scene in Your Code</a></li>
+ <li><a href="#Actions">Create Scene Actions</a></li>
+</ol>
+</div>
+</div>
+
+<p>Scenes store the state of a view hierarchy, including all its views and their property
+values. The transitions framework can run animations between a starting and an ending scene.
+The starting scene is often determined automatically from the current state of the user
+interface. For the ending scene, the framework enables you to create a scene from a layout
+resource file or from a group of views in your code.</p>
+
+<p>This lesson shows you how to create scenes in your app and how to define scene actions.
+The next lesson shows you how to transition between two scenes.</p>
+
+<p class="note"><strong>Note:</strong> The framework can animate changes in a single view
+hierarchy without using scenes, as described in
+<a href="{@docRoot}training/transitions/transitions.html#NoScenes">Apply a Transition Without
+Scenes</a>. However, understanding this lesson is essential to work with transitions.</p>
+
+
+
+<h2 id="FromLayout">Create a Scene From a Layout Resource</h2>
+
+<p>You can create a {@link android.transition.Scene} instance directly from a layout resource
+file. Use this technique when the view hierarchy in the file is mostly static. The resulting
+scene represents the state of the view hierarchy at the time you created the
+{@link android.transition.Scene} instance. If you change the view hierarchy, you have to
+recreate the scene. The framework creates the scene from the entire view hierarchy in the
+file; you can not create a scene from part of a layout file.</p>
+
+<p>To create a {@link android.transition.Scene} instance from a layout resource file, retrieve
+the scene root from your layout as a {@link android.view.ViewGroup} instance and then call the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method with the
+scene root and the resource ID of the layout file that contains the view hierarchy for the
+scene.</p>
+
+<h3>Define Layouts for Scenes</h3>
+
+<p>The code snippets in the rest of this section show you how to create two different scenes
+with the same scene root element. The snippets also demonstrate that you can load multiple
+unrelated {@link android.transition.Scene} objects without implying that they are related to
+each other.</p>
+
+<p>The example consists of the following layout definitions:</p>
+
+<ul>
+<li>The main layout of an activity with a text label and a child layout.</li>
+<li>A relative layout for the first scene with two text fields.</li>
+<li>A relative layout for the second scene with the same two text fields in different order.</li>
+</ul>
+
+<p>The example is designed so that all of the animation occurs within the child layout of the
+main layout for the activity. The text label in the main layout remains static.</p>
+
+<p>The main layout for the activity is defined as follows:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/master_layout">
+ &lt;TextView
+ android:id="@+id/title"
+ ...
+ android:text="Title"/>
+ &lt;FrameLayout
+ android:id="@+id/scene_root">
+ &lt;include layout="@layout/a_scene" />
+ &lt;/FrameLayout>
+&lt;/LinearLayout>
+</pre>
+
+<p>This layout definition contains a text field and a child layout for the scene root. The
+layout for the first scene is included in the main layout file. This allows the app to display
+it as part of the initial user interface and also to load it into a scene, since the framework
+can load only a whole layout file into a scene.</p>
+
+<p>The layout for the first scene is defined as follows:</p>
+
+<p class="code-caption">res/layout/a_scene.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scene_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ &lt;TextView
+ android:id="@+id/text_view1
+ android:text="Text Line 1" />
+ &lt;TextView
+ android:id="@+id/text_view2
+ android:text="Text Line 2" />
+&lt;/RelativeLayout>
+</pre>
+
+<p>The layout for the second scene contains the same two text fields (with the same IDs)
+placed in a different order and is defined as follows:</p>
+
+<p class="code-caption">res/layout/another_scene.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scene_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ &lt;TextView
+ android:id="@+id/text_view2
+ android:text="Text Line 2" />
+ &lt;TextView
+ android:id="@+id/text_view1
+ android:text="Text Line 1" />
+&lt;/RelativeLayout>
+</pre>
+
+<h3>Generate Scenes from Layouts</h3>
+
+<p>After you create definitions for the two relative layouts, you can obtain an scene for
+each of them. This enables you to later transition between the two UI configurations.
+To obtain a scene, you need a reference to the scene root and the layout resource ID.</p>
+
+<p>The following code snippet shows you how to get a reference to the scene root and create
+two {@link android.transition.Scene} objects from the layout files:</p>
+
+<pre>
+Scene mAScene;
+Scene mAnotherScene;
+
+// Create the scene root for the scenes in this app
+mSceneRoot = (ViewGroup) findViewById(R.id.scene_root);
+
+// Create the scenes
+mAScene = Scene.getSceneForLayout(mSceneRoot, R.layout.a_scene, this);
+mAnotherScene =
+ Scene.getSceneForLayout(mSceneRoot, R.layout.another_scene, this);
+</pre>
+
+<p>In the app, there are now two {@link android.transition.Scene} objects based on view
+hierarchies. Both scenes use the scene root defined by the
+{@link android.widget.FrameLayout} element in <code>res/layout/activity_main.xml</code>.</p>
+
+
+
+<h2 id="FromCode">Create a Scene in Your Code</h2>
+
+<p>You can also create a {@link android.transition.Scene} instance in your code from a
+{@link android.view.ViewGroup} object. Use this technique when you modify the view hierarchies
+directly in your code or when you generate them dynamically.</p>
+
+<p>To create a scene from a view hierarchy in your code, use the
+{@link android.transition.Scene#Scene(android.view.ViewGroup, android.view.View) Scene(sceneRoot, viewHierarchy)}
+constructor. Calling this constructor is equivalent to calling the
+{@link android.transition.Scene#getSceneForLayout Scene.getSceneForLayout()} method when you
+have already inflated a layout file.</p>
+
+<p>The following code snippet demonstrates how to create a {@link android.transition.Scene}
+instance from the scene root element and the view hierarchy for the scene in your code:</p>
+
+<pre>
+Scene mScene;
+
+// Obtain the scene root element
+mSceneRoot = (ViewGroup) mSomeLayoutElement;
+
+// Obtain the view hierarchy to add as a child of
+// the scene root when this scene is entered
+mViewHierarchy = (ViewGroup) someOtherLayoutElement;
+
+// Create a scene
+mScene = new Scene(mSceneRoot, mViewHierarchy);
+</pre>
+
+
+
+<h2 id="Actions">Create Scene Actions</h2>
+
+<p>The framework enables you to define custom scene actions that the system runs when entering
+or exiting a scene. In many cases, defining custom scene actions is not necessary, since the
+framework animates the change between scenes automatically.</p>
+
+<p>Scene actions are useful for handling these cases:</p>
+
+<ul>
+<li>Animate views that are not in the same hierarchy. You can animate views for both the
+starting and ending scenes using exit and entry scene actions.</li>
+<li>Animate views that the transitions framework cannot animate automatically, such as
+{@link android.widget.ListView} objects. For more information, see
+<a href="{@docRoot}training/transitions/overview.html#Limitations">Limitations</a>.</li>
+</ul>
+
+<p>To provide custom scene actions, define your actions as {@link java.lang.Runnable} objects
+and pass them to the {@link android.transition.Scene#setExitAction Scene.setExitAction()} or
+{@link android.transition.Scene#setEnterAction Scene.setEnterAction()} methods. The framework
+calls the {@link android.transition.Scene#setExitAction setExitAction()} method on the starting
+scene before running the transition animation and the {@link
+android.transition.Scene#setEnterAction setEnterAction()} method on the ending scene after
+running the transition animation.</p>
+
+<p class="note"><strong>Note:</strong> Do not use scene actions to pass data between views in
+the starting and ending scenes. For more information, see
+<a href="{@docRoot}training/transitions/transitions.html#Callbacks">Defining Transition
+Lifecycle Callbacks</a>.</p>
diff --git a/docs/html/training/transitions/transitions.jd b/docs/html/training/transitions/transitions.jd
new file mode 100644
index 0000000..489e291
--- /dev/null
+++ b/docs/html/training/transitions/transitions.jd
@@ -0,0 +1,315 @@
+page.title=Applying a Transition
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+<h2>This lesson teaches you to</h2>
+<ol>
+ <li><a href="#Create">Create a Transition</a></li>
+ <li><a href="#Apply">Apply a Transition</a></li>
+ <li><a href="#Targets">Choose Specific Target Views</a></li>
+ <li><a href="#Multiple">Specify Multiple Transitions</a></li>
+ <li><a href="#NoScenes">Apply a Transition Without Scenes</a></li>
+ <li><a href="#Callbacks">Define Transition Lifecycle Callbacks</a></li>
+</ol>
+</div>
+</div>
+
+<p>In the transitions framework, animations create a series of frames that depict a change
+between the view hierarchies in the starting and ending scenes. The framework represents
+these animations as transition objects, which contain information about an animation. To
+run an animation, you provide the transition to use and the ending scene to a transition
+manager.</p>
+
+<p>This lesson teaches you run an animation between two scenes using built-in transitions
+to move, resize, and fade views. The next lesson shows you how to define custom transitions.</p>
+
+
+
+<h2 id="Create">Create a Transition</h2>
+
+<p>In the previous lesson, you learned how to create scenes that represent the state of
+different view hierarchies. Once you have defined the starting scene and the ending scene you
+want to change between, you need to create a {@link android.transition.Transition} object
+that defines an animation. The framework enables you to specify a built-in transition in a
+resource file and inflate it in your code or to create an instance of a built-in transition
+directly in your code.</p>
+
+<!-- Built in transition table -->
+<p class="table-caption" id="table1"><strong>Table 1.</strong> Built-in transition types.</p>
+<table>
+<tr>
+ <th scope="col">Class</th>
+ <th scope="col">Tag</th>
+ <th scope="col">Attributes</th>
+ <th scope="col">Effect</th>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/AutoTransition.html">AutoTransition</a></code></td>
+ <td>&lt;autoTransition/&gt;</td>
+ <td style="text-align=center;"> - </td>
+ <td>Default transition. Fade out, move and resize, and fade in views, in that order.</td>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/Fade.html">Fade</a></code></td>
+ <td>&lt;fade/&gt;</td>
+ <td><code>android:fadingMode="[fade_in |<br> fade_out |<br> fade_in_out]"</code></td>
+ <td>
+ <code>fade_in</code> fades in views<br>
+ <code>fade_out</code> fades out views<br>
+ <code>fade_in_out</code> (default) does a <code>fade_out</code> followed by a <code>fade_in</code>.
+ </td>
+</tr>
+<tr>
+ <td><code><a href="/reference/android/transition/ChangeBounds.html">ChangeBounds</a></code></td>
+ <td>&lt;changeBounds/&gt;</td>
+ <td style="text-align=center;"> - </td>
+ <td>Moves and resizes views.</td>
+</tr>
+</table>
+
+
+<h3 id="FromFile">Create a transition instance from a resource file</h3>
+
+<p>This technique enables you to modify your transition definition without having to change
+the code of your activity. This technique is also useful to separate complex transition
+definitions from your application code, as shown in <a href="#Multiple">Specify Multiple
+Transitions</a>.</p>
+
+<p>To specify a built-in transition in a resource file, follow these steps:</p>
+
+<ol>
+<li>Add the <code>res/transition/</code> directory to your project.</li>
+<li>Create a new XML resource file inside this directory.</li>
+<li>Add an XML node for one of the built-in transitions.</li>
+</ol>
+
+<p>For example, the following resource file specifies the {@link android.transition.Fade}
+transition:</p>
+
+<p class="code-caption">res/transition/fade_transition.xml</p>
+
+<pre>
+&lt;fade xmlns:android="http://schemas.android.com/apk/res/android" />
+</pre>
+
+<p>The following code snippet shows how to inflate a {@link android.transition.Transition}
+instance inside your activity from a resource file:</p>
+
+<pre>
+Transition mFadeTransition =
+ TransitionInflater.from(this).
+ inflateTransition(R.transition.fade_transition);
+</pre>
+
+
+<h3 id="FromCode">Create a transition instance in your code</h3>
+
+<p>This technique is useful for creating transition objects dynamically if you modify the user
+interface in your code, and to create simple built-in transition instances with few or
+no parameters.</p>
+
+<p>To create an instance of a built-in transition, invoke one of the public constructors in
+the subclasses of the {@link android.transition.Transition} class. For example, the following
+code snippet creates an instance of the {@link android.transition.Fade} transition:</p>
+
+<pre>
+Transition mFadeTransition = new Fade();
+</pre>
+
+
+
+<h2 id="Apply">Apply a Transition</h2>
+
+<p>You typically apply a transition to change between different view hierarchies in response
+to an event, such as a user action. For example, consider a search app: when the user enters
+a search term and clicks the search button, the app changes to the scene that represents the
+results layout while applying a transition that fades out the search button and fades in the
+search results.</p>
+
+<p>To make a scene change while applying a transition in response to some event in your
+activity, call the {@link android.transition.TransitionManager#go TransitionManager.go()}
+static method with the ending scene and the transition instance to use for the animation,
+as shown in the following snippet:</p>
+
+<pre>
+TransitionManager.go(mEndingScene, mFadeTransition);
+</pre>
+
+<p>The framework changes the view hierarchy inside the scene root with the view hierarchy
+from the ending scene while running the animation specified by the transition instance. The
+starting scene is the ending scene from the last transition. If there was no previous
+transition, the starting scene is determined automatically from the current state of the
+user interface.</p>
+
+<p>If you do not specify a transition instance, the transition manager can apply an automatic
+transition that does something reasonable for most situations. For more information, see the
+API reference for the {@link android.transition.TransitionManager} class.</p>
+
+
+
+<h2 id="Targets">Choose Specific Target Views</h2>
+
+<p>The framework applies transitions to all views in the starting and ending scenes by
+default. In some cases, you may only want to apply an animation to a subset of views in a
+scene. For example, the framework does not support animating changes to
+{@link android.widget.ListView} objects, so you should not try to animate them during a
+transition. The framework enables you to select specific views you want to animate.</p>
+
+<p>Each view that the transition animates is called a <em>target</em>. You can only
+select targets that are part of the view hierarchy associated with a scene.</p>
+
+<p>To remove one or more views from the list of targets, call the {@link
+android.transition.Transition#removeTarget removeTarget()} method before starting
+the transition. To add only the views you specify to the list of targets, call the
+{@link android.transition.Transition#addTarget addTarget()} method. For more
+information, see the API reference for the {@link android.transition.Transition} class.</p>
+
+
+
+<h2 id="Multiple">Specify Multiple Transitions</h2>
+
+<p>To get the most impact from an animation, you should match it to the type of changes
+that occur between the scenes. For example, if you are removing some views and adding others
+between scenes, a fade out/fade in animation provides a noticeable indication that some views
+are no longer available. If you are moving views to different points on the screen, a better
+choice would be to animate the movement so that users notice the new location of the views.</p>
+
+<p>You do not have to choose only one animation, since the transitions framework enables you
+to combine animation effects in a transition set that contains a group of individual built-in
+or custom transitions.</p>
+
+<p>To define a transition set from a collection of transitions in XML, create a resource file
+in the <code>res/transitions/</code> directory and list the transitions under the
+<code>transitionSet</code> element. For example, the following snippet shows how to specify a
+transition set that has the same behaviour as the {@link android.transition.AutoTransition}
+class:</p>
+
+<pre>
+&lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android"
+ android:transitionOrdering="sequential">
+ &lt;fade android:fadingMode="fade_out" />
+ &lt;changeBounds />
+ &lt;fade android:fadingMode="fade_in" />
+&lt;/transitionSet>
+</pre>
+
+<p>To inflate the transition set into a {@link android.transition.TransitionSet} object in
+your code, call the {@link android.transition.TransitionInflater#from TransitionInflater.from()}
+method in your activity. The {@link android.transition.TransitionSet} class extends from the
+{@link android.transition.Transition} class, so you can use it with a transition manager just
+like any other {@link android.transition.Transition} instance.</p>
+
+
+
+<h2 id="NoScenes">Apply a Transition Without Scenes</h2>
+
+<p>Changing view hierarchies is not the only way to modify your user interface. You can also
+make changes by adding, modifying, and removing child views within the current hierarchy. For
+example, you can implement a search interaction with just a single layout. Start with the
+layout showing a search entry field and a search icon. To change the user interface to show
+the results, remove the search button when the user clicks it by calling the {@link
+android.view.ViewGroup#removeView ViewGroup.removeView()} method, and add the search results by
+calling {@link android.view.ViewGroup#addView ViewGroup.addView()} method.</p>
+
+<p>You may want to use this approach if the alternative is to have two hierarchies that are
+nearly identical. Rather than having to create and maintain two separate layout files for a
+minor difference in the user interface, you can have one layout file containing a view
+hierarchy that you modify in code.</p>
+
+<p>If you make changes within the current view hierarchy in this fashion, you do not need to
+create a scene. Instead, you can create and apply a transition between two states of a view
+hierarchy using a <em>delayed transition</em>. This feature of the transitions framework
+starts with the current view hierarchy state, records changes you make to its views, and applies
+a transition that animates the changes when the system redraws the user interface.</p>
+
+<p>To create a delayed transition within a single view hierarchy, follow these steps:</p>
+
+<ol>
+<li>When the event that triggers the transition occurs, call the {@link
+android.transition.TransitionManager#beginDelayedTransition
+TransitionManager.beginDelayedTransition()} method providing the parent view of all the views
+you want to change and the transition to use. The framework stores the current state of the
+child views and their property values.</li>
+<li>Make changes to the child views as required by your use case. The framework records
+the changes you make to the child views and their properties.</li>
+<li>When the system redraws the user interface according to your changes, the framework
+animates the changes between the original state and the new state.</li>
+</ol>
+
+<p>The following example shows how to animate the addition of a text view to a view hierarchy
+using a delayed transition. The first snippet shows the layout definition file:</p>
+
+<p class="code-caption">res/layout/activity_main.xml</p>
+
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/mainLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ &lt;EditText
+ android:id="@+id/inputText"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+ ...
+&lt;/RelativeLayout>
+</pre>
+
+<p>The next snippet shows the code that animates the addition of the text view:</p>
+
+<p class="code-caption">MainActivity.java</p>
+
+<pre>
+private TextView mLabelText;
+private Fade mFade;
+private ViewGroup mRootView;
+...
+
+// Load the layout
+this.setContentView(R.layout.activity_main);
+...
+
+// Create a new TextView and set some View properties
+mLabelText = new TextView();
+mLabelText.setText("Label").setId("1");
+
+// Get the root view and create a transition
+mRootView = (ViewGroup) findViewById(R.id.mainLayout);
+mFade = new Fade(IN);
+
+// Start recording changes to the view hierarchy
+TransitionManager.beginDelayedTransition(mRootView, mFade);
+
+// Add the new TextView to the view hierarchy
+mRootView.addView(mLabelText);
+
+// When the system redraws the screen to show this update,
+// the framework will animate the addition as a fade in
+</pre>
+
+
+
+<h2 id="Callbacks">Define Transition Lifecycle Callbacks</h2>
+
+<p>The transition lifecycle is similar to the activity lifecycle. It represents the transition
+states that the framework monitors during the time between a call to the {@link
+android.transition.TransitionManager#go TransitionManager.go()} method and the completion of
+the animation. At important lifecycle states, the framework invokes callbacks defined by
+the {@link android.transition.Transition.TransitionListener TransitionListener}
+interface.</p>
+
+<p>Transition lifecycle callbacks are useful, for example, for copying a view property value
+from the starting view hierarchy to the ending view hierarchy during a scene change. You
+cannot simply copy the value from its starting view to the view in the ending view hierarchy,
+because the ending view hierarchy is not inflated until the transition is completed.
+Instead, you need to store the value in a variable and then copy it into the ending view
+hierarchy when the framework has finished the transition. To get notified when the transition
+is completed, you can implement the {@link
+android.transition.Transition.TransitionListener#onTransitionEnd
+TransitionListener.onTransitionEnd()} method in your activity.</p>
+
+<p>For more information, see the API reference for the {@link
+android.transition.Transition.TransitionListener TransitionListener} class.</p>
diff --git a/docs/html/training/tv/discovery/index.jd b/docs/html/training/tv/discovery/index.jd
index f22ca67..066dfb3 100644
--- a/docs/html/training/tv/discovery/index.jd
+++ b/docs/html/training/tv/discovery/index.jd
@@ -23,7 +23,7 @@ startpage=true
<p>
TV devices offer many entertainment options for users. They have thousands of content options
from apps and related content services. At the same time, most users prefer to use TVs with the
- least amount of input possible. With the amount of choice available to users, it is important for
+ least amount of input possible. With the number of choices available to users, it is important for
app developers to provide quick and easy paths for users to discover and enjoy your content.
</p>
@@ -44,7 +44,8 @@ startpage=true
<dt><b><a href="recommendations.html">Recommending TV Content</a></b></dt>
<dd>Learn how to recommend content for users so that it appears in the recommendations row
on the home screen of a TV device.</dd>
-
+ <dt><b><a href="searchable.html">Making TV Apps Searchable</a></b></dt>
+ <dd>Learn how to make your content searchable from the Android TV home screen.</dd>
<dt><b><a href="in-app-search.html">Searching within TV Apps</a></b></dt>
<dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
</dl>
diff --git a/docs/html/training/tv/discovery/recommendations.jd b/docs/html/training/tv/discovery/recommendations.jd
index d348c14..a74ee56 100644
--- a/docs/html/training/tv/discovery/recommendations.jd
+++ b/docs/html/training/tv/discovery/recommendations.jd
@@ -61,11 +61,6 @@ trainingnavtop=true
create a recommendation service for your application:
</p>
-
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/UpdateRecommendationsService.java" target="_blank">
- UpdateRecommendationsService.java</a>
-</p>
<pre>
public class UpdateRecommendationsService extends IntentService {
private static final String TAG = "UpdateRecommendationsService";
@@ -136,10 +131,6 @@ public class UpdateRecommendationsService extends IntentService {
app manifest. The following code snippet illustrates how to declare this class as a service:
</p>
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
- AndroidManifest.xml</a>
-</p>
<pre>
&lt;manifest ... &gt;
&lt;application ... &gt;
@@ -160,6 +151,11 @@ the recommendations to appear at the end of the recommendations row. Once a cont
movie, has been played, <a href="{@docRoot}guide/topics/ui/notifiers/notifications.html#Removing">
remove it</a> from the recommendations.</p>
+<p>The order of an app's recommendations is preserved according to the order in which the app
+provides them. The framework interleaves app recommendations based on recommendation quality,
+as measured by user behavior. Better recommendations make an app's recommendations more likely
+to appear near the front of the list.</p>
+
<h2 id="build">Build Recommendations</h2>
<p>
@@ -175,10 +171,6 @@ remove it</a> from the recommendations.</p>
the builder pattern described as follows. First, you set the values of the recommendation card
elements.</p>
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/RecommendationBuilder.java" target="_blank">
- RecommendationBuilder.java</a>
-</p>
<pre>
public class RecommendationBuilder {
...
@@ -221,13 +213,9 @@ public class RecommendationBuilder {
</p>
<p>
- The following code example demonstrates how to build a recommendation, and post it to the manager.
+ The following code example demonstrates how to build a recommendation.
</p>
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/RecommendationBuilder.java" target="_blank">
- RecommendationBuilder.java</a>
-</p>
<pre>
public class RecommendationBuilder {
...
@@ -250,8 +238,6 @@ public class RecommendationBuilder {
.setExtras(extras))
.build();
- mNotificationManager.notify(mId, notification);
- mNotificationManager = null;
return notification;
}
}
@@ -267,10 +253,6 @@ public class RecommendationBuilder {
every half hour:
</p>
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/BootupActivity.java" target="_blank">
- BootupActivity.java</a>
-</p>
<pre>
public class BootupActivity extends BroadcastReceiver {
private static final String TAG = "BootupActivity";
@@ -307,10 +289,6 @@ public class BootupActivity extends BroadcastReceiver {
following sample code demonstrates how to add this configuration to the manifest:
</p>
-<p class="code-caption">
- <a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
- AndroidManifest.xml</a>
-</p>
<pre>
&lt;manifest ... &gt;
&lt;application ... &gt;
@@ -330,3 +308,12 @@ public class BootupActivity extends BroadcastReceiver {
requests the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
</p>
+
+<p>In your recommendation service class' {@link android.app.IntentService#onHandleIntent(android.content.Intent)
+onHandleIntent()}
+method, post the recommendation to the manager as follows:</p>
+
+<pre>
+Notification notification = notificationBuilder.build();
+mNotificationManager.notify(id, notification);
+</pre>
diff --git a/docs/html/training/tv/discovery/searchable.jd b/docs/html/training/tv/discovery/searchable.jd
index 5d3b9e3..27a1c33 100644
--- a/docs/html/training/tv/discovery/searchable.jd
+++ b/docs/html/training/tv/discovery/searchable.jd
@@ -109,8 +109,6 @@ view for the content, along with links to the apps of other providers. This is d
<p>Your application's database class might define the columns as follows:</p>
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoDatabase.java#L41" target="_blank">
-VideoDatabase.java</a></p>
<pre>
public class VideoDatabase {
//The columns we'll include in the video database table
@@ -136,8 +134,6 @@ public class VideoDatabase {
<p>When you build the map from the {@link android.app.SearchManager} columns to your data fields, you
must also specify the {@link android.provider.BaseColumns#_ID} to give each row a unique ID.</p>
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoDatabase.java#L83" target="_blank">
-VideoDatabase.java</a></p>
<pre>
...
private static HashMap<String, String> buildColumnMap() {
@@ -195,8 +191,6 @@ java.lang.String[], java.lang.String, java.lang.String[], java.lang.String) quer
provider searches your suggestion data and returns a {@link android.database.Cursor} that points to
the rows you have designated for suggestions.</p>
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/java/com/example/android/tvleanback/VideoContentProvider.java" target="_blank">
-VideoContentProvider.java</a></p>
<pre>
&#64;Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
@@ -248,8 +242,6 @@ provider includes the {@code android:searchSuggestAuthority} attribute to tell t
namespace of your content provider. Also, you must set its {@code android:exported} attribute to
{@code "true"} so that the Android global search can use the results returned from it.</p>
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
-AndroidManifest.xml</a></p>
<pre>
&lt;provider android:name="com.example.android.tvleanback.VideoContentProvider"
android:authorities="com.example.android.tvleanback"
@@ -294,8 +286,6 @@ question mark ({@code ?}) value is replaced with the query text.</p>
<a href="{@docRoot}guide/topics/search/searchable-config.html">{@code searchable.xml}</a>
file:</p>
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/res/xml/searchable.xml" target="_blank">
-Searchable.xml</a></p>
<pre>
&lt;searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
@@ -320,8 +310,6 @@ intent searches the repository for columns with the given word in their values,
of content items with those columns. In your {@code AndroidManifest.xml} file, you designate the
activity which handles the {@link android.content.Intent#ACTION_SEARCH} intent like this:
-<p class="code-caption"><a href="https://github.com/googlesamples/androidtv-Leanback/blob/master/app/src/main/AndroidManifest.xml" target="_blank">
-AndroidManifest.xml</a></p>
<pre>
...
&lt;activity
@@ -361,8 +349,12 @@ Suggestions</a> and mapped the {@link android.app.SearchManager#SUGGEST_COLUMN_T
{@link android.app.SearchManager#SUGGEST_COLUMN_CONTENT_TYPE}, and
{@link android.app.SearchManager#SUGGEST_COLUMN_PRODUCTION_YEAR} fields as described in
<a href="#columns">Identify Columns</a>, a <a href="{@docRoot}training/app-indexing/deep-linking.html">
-deep link</a> to your content appears in the details screen that launches when the user selects a
-search result.</p>
+deep link</a> to a watch action for your content appears in the details screen that launches when
+the user selects a search result, as shown in figure 1.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/deep-link.png" alt="Deep link in the details screen"/>
+<p class="img-caption"><b>Figure 1.</b> The details screen displays a deep link for the
+Videos by Google (Leanback) sample app. Sintel: &copy; copyright Blender Foundation, www.sintel.org.</p>
<p>When the user selects the link for your app, identified by the "Available On" button in the
details screen, the system launches the activity which handles the {@link android.content.Intent#ACTION_VIEW}
diff --git a/docs/html/training/tv/playback/card.jd b/docs/html/training/tv/playback/card.jd
new file mode 100644
index 0000000..8ac75fd
--- /dev/null
+++ b/docs/html/training/tv/playback/card.jd
@@ -0,0 +1,156 @@
+page.title=Providing a Card View
+page.tags="card"
+
+trainingnavtop=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+ <h2>This lesson teaches you to</h2>
+ <ol>
+ <li><a href="#presenter">Create a Card Presenter</a></li>
+ <li><a href="#card-view">Create a Card View</a></li>
+ </ol>
+ <h2>Try it out</h2>
+ <ul>
+ <li><a class="external-link" href="https://github.com/googlesamples/androidtv-Leanback">Android
+ Leanback sample app</a></li>
+ </ul>
+</div>
+</div>
+
+<p>In the previous lesson, you created a catalog browser, implemented in a browse fragment, that
+displays a list of media items. In this lesson, you create the card views for your media items and
+present them in the browse fragment.</p>
+
+<p>The {@link android.support.v17.leanback.widget.BaseCardView} class and subclasses display the meta
+data associated with a media item. The {@link android.support.v17.leanback.widget.ImageCardView}
+class used in this lesson displays an image for the content along with the media item's title.</p>
+
+<p>This lesson describes code from the <a href="https://github.com/googlesamples/androidtv-Leanback">
+Android Leanback sample app</a>, available on GitHub. Use this sample code to start your own
+app.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/app-browse.png" alt="App main screen"/>
+<p class="img-caption"><b>Figure 1.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> browse fragment with a card presenter displaying card view objects.</p>
+
+<h2 id="presenter">Create a Card Presenter</h2>
+
+<p>A {@link android.support.v17.leanback.widget.Presenter} generates views and binds objects to them
+on demand. In the browse fragment where your app presents its content to the user, you create a
+{@link android.support.v17.leanback.widget.Presenter} for the content cards and pass it to the adapter
+that adds the content to the screen. In the following code, the <code>CardPresenter</code> is created
+in the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished(android.support.v4.content.Loader, java.lang.Object) onLoadFinished()}
+callback of the {@link android.support.v4.app.LoaderManager}.</p>
+
+<pre>
+&#64;Override
+public void onLoadFinished(Loader&lt;HashMap&lt;String, List&lt;Movie&gt;&gt;&gt; arg0,
+ HashMap&lt;String, List&lt;Movie&gt;&gt; data) {
+
+ mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+ CardPresenter cardPresenter = new CardPresenter();
+
+ int i = 0;
+
+ for (Map.Entry&lt;String, List&lt;Movie&gt;&gt; entry : data.entrySet()) {
+ ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(cardPresenter);
+ List&lt;Movie&gt; list = entry.getValue();
+
+ for (int j = 0; j &lt; list.size(); j++) {
+ listRowAdapter.add(list.get(j));
+ }
+ HeaderItem header = new HeaderItem(i, entry.getKey(), null);
+ i++;
+ mRowsAdapter.add(new ListRow(header, listRowAdapter));
+ }
+
+ HeaderItem gridHeader = new HeaderItem(i, getString(R.string.more_samples),
+ null);
+
+ GridItemPresenter gridPresenter = new GridItemPresenter();
+ ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
+ gridRowAdapter.add(getString(R.string.grid_view));
+ gridRowAdapter.add(getString(R.string.error_fragment));
+ gridRowAdapter.add(getString(R.string.personal_settings));
+ mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
+
+ setAdapter(mRowsAdapter);
+
+ updateRecommendations();
+}
+</pre>
+
+<h2 id="card-view">Create a Card View</h2>
+
+<p>In this step, you build the card presenter with a view holder for the card view that describes
+your media content items. Note that each presenter must only create one view type. If you have two
+different card view types then you need two different card presenters.</p>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter}, implement an
+{@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup) onCreateViewHolder()}
+callback that creates a view holder that can be used to display a content item.</p>
+
+<pre>
+&#64;Override
+public class CardPresenter extends Presenter {
+
+ private Context mContext;
+ private static int CARD_WIDTH = 313;
+ private static int CARD_HEIGHT = 176;
+ private Drawable mDefaultCardImage;
+
+ &#64;Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent) {
+ mContext = parent.getContext();
+ mDefaultCardImage = mContext.getResources().getDrawable(R.drawable.movie);
+...
+</pre>
+
+<p>In the {@link android.support.v17.leanback.widget.Presenter#onCreateViewHolder(android.view.ViewGroup)
+onCreateViewHolder()} method, create a card view for content items. The sample below uses an
+{@link android.support.v17.leanback.widget.ImageCardView}.</p>
+
+<p>When a card is selected, the default behavior expands it to a larger size. If you want to designate
+a different color for the selected card, call {@link android.support.v17.leanback.widget.BaseCardView#setSelected(boolean)
+setSelected()}
+as shown here.</p>
+
+<pre>
+...
+ ImageCardView cardView = new ImageCardView(mContext) {
+ &#64;Override
+ public void setSelected(boolean selected) {
+ int selected_background = mContext.getResources().getColor(R.color.detail_background);
+ int default_background = mContext.getResources().getColor(R.color.default_background);
+ int color = selected ? selected_background : default_background;
+ findViewById(R.id.info_field).setBackgroundColor(color);
+ super.setSelected(selected);
+ }
+ };
+...
+</pre>
+
+<p>When the user opens your app, the {@link android.support.v17.leanback.widget.Presenter.ViewHolder}
+displays the <code>CardView</code> objects for your content items. You need to set these to receive
+focus from the D-pad controller by calling {@link android.view.View#setFocusable(boolean) setFocusable(true)}
+and {@link android.view.View#setFocusableInTouchMode(boolean) setFocusableInTouchMode(true)}.</p>
+
+<pre>
+...
+ cardView.setFocusable(true);
+ cardView.setFocusableInTouchMode(true);
+ return new ViewHolder(cardView);
+}
+</pre>
+
+<p>When the user selects the {@link android.support.v17.leanback.widget.ImageCardView}, it expands
+to reveal its text area with the background color you specify, as shown in figure 2.</p>
+
+<img itemprop="image" src="{@docRoot}images/tv/card-view.png" alt="App card view"/>
+<p class="img-caption"><b>Figure 2.</b> The <a href="https://github.com/googlesamples/androidtv-Leanback">
+Leanback sample app</a> image card view when selected.</p>
+
+
diff --git a/docs/html/training/tv/playback/index.jd b/docs/html/training/tv/playback/index.jd
index 5427d48..0e9c5ec 100644
--- a/docs/html/training/tv/playback/index.jd
+++ b/docs/html/training/tv/playback/index.jd
@@ -56,6 +56,9 @@ startpage=true
<dd>Learn how to use the Leanback support library to build a browsing interface for media
catalogs.</dd>
+ <dt><b><a href="details.html">Providing a Card View</a></b></dt>
+ <dd>Learn how to use the Leanback support library to build a card view for content items.</dd>
+
<dt><b><a href="details.html">Building a Details View</a></b></dt>
<dd>Learn how to use the Leanback support library to build a details page for media items.</dd>
diff --git a/docs/html/training/tv/start/layouts.jd b/docs/html/training/tv/start/layouts.jd
index 177ea7a..a378096 100644
--- a/docs/html/training/tv/start/layouts.jd
+++ b/docs/html/training/tv/start/layouts.jd
@@ -119,8 +119,8 @@ trainingnavtop=true
<p>
Avoid screen elements being clipped due to overscan and by incorporating a 10% margin
- on all sides of your layout. This translates into a 27dp margin on the left and right edges and
- a 48dp margin on the top and bottom of your base layouts for activities. The following
+ on all sides of your layout. This translates into a 48dp margin on the left and right edges and
+ a 27dp margin on the top and bottom of your base layouts for activities. The following
example layout demonstrates how to set these margins in the root layout for a TV app:
</p>
diff --git a/docs/html/training/wearables/data-layer/data-items.jd b/docs/html/training/wearables/data-layer/data-items.jd
index 12babbf..49a8d32 100644
--- a/docs/html/training/wearables/data-layer/data-items.jd
+++ b/docs/html/training/wearables/data-layer/data-items.jd
@@ -46,7 +46,7 @@ directly. Instead, you:
</ol>
<p>
-However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])">setData()</a>,
+However, instead of working with raw bytes using <a href="{@docRoot}reference/com/google/android/gms/wearable/PutDataRequest.html#setData(byte[])"><code>setData()</code></a>,
we recommend you <a href="#SyncData">use a data map</a>, which exposes
a data item in an easy-to-use {@link android.os.Bundle}-like interface.
</p>
@@ -88,39 +88,121 @@ app, you should create a path scheme that matches the structure of the data.
</li>
</ol>
-<p>The following example shows how to create a data map and put data on it:</p>
+<p>The <code>increaseCounter()</code> method in the following example shows how to create a
+data map and put data in it:</p>
<pre>
-PutDataMapRequest dataMap = PutDataMapRequest.create("/count");
-dataMap.getDataMap().putInt(COUNT_KEY, count++);
-PutDataRequest request = dataMap.asPutDataRequest();
-PendingResult&lt;DataApi.DataItemResult&gt; pendingResult = Wearable.DataApi
- .putDataItem(mGoogleApiClient, request);
+public class MainActivity extends Activity implements
+ DataApi.DataListener,
+ GoogleApiClient.ConnectionCallbacks,
+ GoogleApiClient.OnConnectionFailedListener {
+
+ private static final String COUNT_KEY = "com.example.key.count";
+
+ private GoogleApiClient mGoogleApiClient;
+ private int count = 0;
+
+ ...
+
+ // Create a data map and put data in it
+ private void <strong>increaseCounter</strong>() {
+ PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count");
+ putDataMapReq.getDataMap().putInt(COUNT_KEY, count++);
+ PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
+ PendingResult&lt;DataApi.DataItemResult> pendingResult =
+ Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
+ }
+
+ ...
+}
</pre>
+<p>For more information about handling the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">
+<code>PendingResult</code></a> object, see
+<a href="{@docRoot}training/wearables/data-layer/events.html#Wait">Wait for the Status of Data
+Layer Calls</a>.</p>
+
+
<h2 id="ListenEvents">Listen for Data Item Events</h2>
-If one side of the data layer connection changes a data item, you probably want
+
+<p>If one side of the data layer connection changes a data item, you probably want
to be notified of any changes on the other side of the connection.
-You can do this by implementing a listener for data item events.
+You can do this by implementing a listener for data item events.</p>
-<p>For example, here's what a typical callback looks like to carry out certain actions
-when data changes:</p>
+<p>The code snippet in the following example notifies your app when the value of the
+counter defined in the previous example changes:</p>
<pre>
-&#64;Override
-public void onDataChanged(DataEventBuffer dataEvents) {
- for (DataEvent event : dataEvents) {
- if (event.getType() == DataEvent.TYPE_DELETED) {
- Log.d(TAG, "DataItem deleted: " + event.getDataItem().getUri());
- } else if (event.getType() == DataEvent.TYPE_CHANGED) {
- Log.d(TAG, "DataItem changed: " + event.getDataItem().getUri());
+public class MainActivity extends Activity implements
+ DataApi.DataListener,
+ GoogleApiClient.ConnectionCallbacks,
+ GoogleApiClient.OnConnectionFailedListener {
+
+ private static final String COUNT_KEY = "com.example.key.count";
+
+ private GoogleApiClient mGoogleApiClient;
+ private int count = 0;
+
+ &#64;Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ mGoogleApiClient = new GoogleApiClient.Builder(this)
+ .addApi(Wearable.API)
+ .addConnectionCallbacks(this)
+ .addOnConnectionFailedListener(this)
+ .build();
+ }
+
+ &#64;Override
+ protected void onResume() {
+ super.onStart();
+ mGoogleApiClient.connect();
+ }
+
+ &#64;Override
+ public void onConnected(Bundle bundle) {
+ <strong>Wearable.DataApi.addListener</strong>(mGoogleApiClient, this);
+ }
+
+ &#64;Override
+ protected void onPause() {
+ super.onPause();
+ <strong>Wearable.DataApi.removeListener</strong>(mGoogleApiClient, this);
+ mGoogleApiClient.disconnect();
+ }
+
+ &#64;Override
+ public void <strong>onDataChanged</strong>(DataEventBuffer dataEvents) {
+ for (DataEvent event : dataEvents) {
+ if (event.getType() == DataEvent.TYPE_CHANGED) {
+ // DataItem changed
+ DataItem item = event.getDataItem();
+ if (item.getUri().getPath().compareTo("/count") == 0) {
+ DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
+ updateCount(dataMap.getInt(COUNT_KEY));
+ }
+ } else if (event.getType() == DataEvent.TYPE_DELETED) {
+ // DataItem deleted
+ }
}
}
+
+ // Our method to update the count
+ private void updateCount(int c) { ... }
+
+ ...
}
</pre>
-<p>
-This is just a snippet that requires more implementation details. Learn about
-how to implement a full listener service or activity in
+
+<p>This activity implements the
+<a href="{@docRoot}reference/com/google/android/gms/wearable/DataApi.DataListener.html">
+<code>DataItem.DataListener</code></a> interface. This activity adds itself as a listener
+for data item events inside the <code>onConnected()</code> method and removes the listener
+in the <code>onPause()</code> method.</p>
+
+<p>You can also implement the listener as a service. For more information, see
<a href="{@docRoot}training/wearables/data-layer/events.html#Listen">Listen for Data Layer
-Events</a>.
-</p> \ No newline at end of file
+Events</a>.</p>
diff --git a/docs/html/training/wearables/data-layer/events.jd b/docs/html/training/wearables/data-layer/events.jd
index 6a3949a..c797f68 100644
--- a/docs/html/training/wearables/data-layer/events.jd
+++ b/docs/html/training/wearables/data-layer/events.jd
@@ -267,6 +267,8 @@ or <a href="{@docRoot}reference/com/google/android/gms/wearable/NodeApi.html#rem
public class MainActivity extends Activity implements
DataApi.DataListener, ConnectionCallbacks, OnConnectionFailedListener {
+ private GoogleApiClient mGoogleApiClient;
+
&#64;Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -314,4 +316,5 @@ public class MainActivity extends Activity implements
}
}
}
+}
</pre>
diff --git a/docs/html/training/wearables/data-layer/messages.jd b/docs/html/training/wearables/data-layer/messages.jd
index 822e395..0ca55ba 100644
--- a/docs/html/training/wearables/data-layer/messages.jd
+++ b/docs/html/training/wearables/data-layer/messages.jd
@@ -39,16 +39,24 @@ to Google Play services and when to use each in
</p>
<pre>
-Node node; // the connected device to send the message to
GoogleApiClient mGoogleApiClient;
-public static final START_ACTIVITY_PATH = "/start/MainActivity";
+public static final String START_ACTIVITY_PATH = "/start/MainActivity";
...
- SendMessageResult result = Wearable.MessageApi.sendMessage(
- mGoogleApiClient, node, START_ACTIVITY_PATH, null).await();
- if (!result.getStatus().isSuccess()) {
- Log.e(TAG, "ERROR: failed to send Message: " + result.getStatus());
- }
+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());
+ }
+ }
+ }
+ );
+}
</pre>
<p>
diff --git a/docs/html/training/wearables/watch-faces/configuration.jd b/docs/html/training/wearables/watch-faces/configuration.jd
index edc7eac..5a4585d 100644
--- a/docs/html/training/wearables/watch-faces/configuration.jd
+++ b/docs/html/training/wearables/watch-faces/configuration.jd
@@ -92,7 +92,7 @@ and declare the following intent filter in the manifest file of the wearable app
&lt;action android:name=
"com.example.android.wearable.watchface.CONFIG_DIGITAL" />
&lt;category android:name=
- "com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION" />
+ <strong>"com.google.android.wearable.watchface.category.WEARABLE_CONFIGURATION"</strong> />
&lt;category android:name="android.intent.category.DEFAULT" />
&lt;/intent-filter>
&lt;/activity>
@@ -119,8 +119,21 @@ a handheld device. For example, a configuration activity on a handheld device en
present users with elaborate color pickers to select the background color of a watch face.</p>
<p>To create a companion configuration activity, add a new activity to your handheld app module and
-declare the same intent filter for this activity as the one in <a href="#WearableActivity">Create
-a Wearable Configuration Activity</a>.</p>
+declare the following intent filter in the manifest file of the handheld app:</p>
+
+<pre>
+&lt;activity
+ android:name=".DigitalWatchFaceCompanionConfigActivity"
+ android:label="@string/app_name">
+ &lt;intent-filter>
+ &lt;action android:name=
+ "com.example.android.wearable.watchface.CONFIG_DIGITAL" />
+ &lt;category android:name=
+ <strong>"com.google.android.wearable.watchface.category.COMPANION_CONFIGURATION"</strong> />
+ &lt;category android:name="android.intent.category.DEFAULT" />
+ &lt;/intent-filter>
+&lt;/activity>
+</pre>
<p>In your configuration activity, build a UI that provides options to customize all the
configurable elements of your watch face. When users make a selection, use the <a
diff --git a/docs/html/training/wearables/watch-faces/drawing.jd b/docs/html/training/wearables/watch-faces/drawing.jd
index 9afdd80..3c5da34 100644
--- a/docs/html/training/wearables/watch-faces/drawing.jd
+++ b/docs/html/training/wearables/watch-faces/drawing.jd
@@ -192,13 +192,14 @@ as described in <a href="#Drawing">Drawing Your Watch Face</a>.</p>
<h3 id="Timer">Initialize the custom timer</h3>
-<p>As a watch face developer, you can decide how often you want to update your watch face by
+<p>As a watch face developer, you decide how often you want to update your watch face by
providing a custom timer that ticks with the required frequency while the device is in
-interactive mode. This enables you to create custom animations and other visual effects. In
-ambient mode, you should disable the timer to let the CPU sleep and update the watch face
-only when the time changes. For more information, see
-<a href="{@docRoot}training/wearables/watch-faces/performance.html">Optimizing Performance and
-Battery Life</a>.</p>
+interactive mode. This enables you to create custom animations and other visual effects.
+</p>
+
+<p class="note"><strong>Note:</strong> In ambient mode, the system does not reliably call the
+custom timer. To update the watch face in ambient mode, see <a href="#TimeTick">Update the watch
+face in ambient mode</a>.</p>
<p>An example timer definition from the <code>AnalogWatchFaceService</code> class that ticks once
every second is shown in <a href="#Variables">Declare variables</a>. In the
@@ -210,9 +211,8 @@ conditions apply:</p>
<li>The device is in interactive mode.</li>
</ul>
-<p>The timer should not run under any other conditions, since this watch face does not
-draw the second hand in ambient mode to conserve power. The <code>AnalogWatchFaceService</code>
-class schedules the next timer tick if required as follows:</p>
+<p>The <code>AnalogWatchFaceService</code> class schedules the next timer tick if required as
+follows:</p>
<pre>
private void updateTimer() {
@@ -281,15 +281,15 @@ private void unregisterReceiver() {
-<h3 id="TimeTick">Invalidate the canvas when the time changes</h3>
+<h3 id="TimeTick">Update the watch face in ambient mode</h3>
-<p>The system calls the <code>Engine.onTimeTick()</code> method every minute. In ambient mode,
-it is usually sufficient to update your watch face once per minute. To update your watch face
-more often while in interactive mode, you provide a custom timer as described in
+<p>In ambient mode, the system calls the <code>Engine.onTimeTick()</code> method every minute.
+It is usually sufficient to update your watch face once per minute in this mode. To update your
+watch face while in interactive mode, you must provide a custom timer as described in
<a href="#Timer">Initialize the custom timer</a>.</p>
-<p>Most watch face implementations just invalidate the canvas to redraw the watch face when
-the time changes:</p>
+<p>In ambient mode, most watch face implementations simply invalidate the canvas to redraw the watch
+face in the <code>Engine.onTimeTick()</code> method:</p>
<pre>
&#64;Override
@@ -402,20 +402,17 @@ method for the system to redraw the watch face.</p>
&#64;Override
public void onAmbientModeChanged(boolean inAmbientMode) {
- boolean wasInAmbientMode = isInAmbientMode();
super.onAmbientModeChanged(inAmbientMode);
- if (inAmbientMode != wasInAmbientMode) {
- if (mLowBitAmbient) {
- boolean antiAlias = !inAmbientMode;
- mHourPaint.setAntiAlias(antiAlias);
- mMinutePaint.setAntiAlias(antiAlias);
- mSecondPaint.setAntiAlias(antiAlias);
- mTickPaint.setAntiAlias(antiAlias);
- }
- invalidate();
- updateTimer();
+ if (mLowBitAmbient) {
+ boolean antiAlias = !inAmbientMode;
+ mHourPaint.setAntiAlias(antiAlias);
+ mMinutePaint.setAntiAlias(antiAlias);
+ mSecondPaint.setAntiAlias(antiAlias);
+ mTickPaint.setAntiAlias(antiAlias);
}
+ invalidate();
+ updateTimer();
}
</pre>
@@ -478,7 +475,7 @@ public void onDraw(Canvas canvas, Rect bounds) {
float hrLength = centerX - 80;
// Only draw the second hand in interactive mode.
- if (!mAmbient) {
+ if (!isInAmbientMode()) {
float secX = (float) Math.sin(secRot) * secLength;
float secY = (float) -Math.cos(secRot) * secLength;
canvas.drawLine(centerX, centerY, centerX + secX, centerY +
diff --git a/docs/html/training/wearables/watch-faces/index.jd b/docs/html/training/wearables/watch-faces/index.jd
index c7affd1..453c30e 100644
--- a/docs/html/training/wearables/watch-faces/index.jd
+++ b/docs/html/training/wearables/watch-faces/index.jd
@@ -21,6 +21,14 @@ page.title=Creating Watch Faces
</div>
</a>
+<a class="notice-developers-video wide"
+ href="https://www.youtube.com/watch?v=AK38PJZmIW8">
+<div>
+ <h3>Video</h3>
+ <p>DevBytes: Watch Faces for Android Wear</p>
+</div>
+</a>
+
<p>Watch faces in Android Wear leverage a dynamic digital canvas to tell time using colors,
animations, and relevant contextual information. The <a
href="https://play.google.com/store/apps/details?id=com.google.android.wearable.app">Android
diff --git a/docs/html/training/wearables/watch-faces/service.jd b/docs/html/training/wearables/watch-faces/service.jd
index 7ab575e..35366c5 100644
--- a/docs/html/training/wearables/watch-faces/service.jd
+++ b/docs/html/training/wearables/watch-faces/service.jd
@@ -64,42 +64,15 @@ Project</a>.</p>
<h3 id="Dependencies">Dependencies</h3>
-<p>For the handheld app, edit the <code>build.gradle</code> file in the <code>mobile</code> module
-to add these dependencies:</p>
-
-<pre>
-apply plugin: 'com.android.application'
-
-android { ... }
-
-dependencies {
- ...
- wearApp project(':wear')
- compile 'com.google.android.gms:play-services:6.5.+'
-}
-</pre>
-
-<p>For the wearable app, edit the <code>build.gradle</code> file in the <code>wear</code> module
-to add these dependencies:</p>
-
-<pre>
-apply plugin: 'com.android.application'
-
-android { ... }
-
-dependencies {
- ...
- compile 'com.google.android.support:wearable:1.1.+'
- compile 'com.google.android.gms:play-services-wearable:6.5.+'
-}
-</pre>
-
<p>The Wearable Support Library provides the necessary classes that you extend to create watch
face implementations. The Google Play services client libraries (<code>play-services</code> and
<code>play-services-wearable</code>) are required to sync data items between the companion device
and the wearable with the <a href="{@docRoot}training/wearables/data-layer/index.html">Wearable
Data Layer API</a>.</p>
+<p>Android Studio automatically adds the required entries in your <code>build.gradle</code>
+files when you create the project in the instructions above.</p>
+
<h3 id="Reference">Wearable Support Library API Reference</h3>
<p>The reference documentation provides detailed information about the classes you use to