OSDN Git Service

add document about GoogleApiClient and one about GoogleAuthUtil
authorScott Main <smain@google.com>
Sat, 5 Oct 2013 02:08:08 +0000 (19:08 -0700)
committerScott Main <smain@google.com>
Wed, 12 Feb 2014 16:54:49 +0000 (08:54 -0800)
also remove the Authorization document from the Google Services
section.
bug:10679818

Change-Id: Ibfade1eca68d89afe30b79d75ca5e38a2b3a84a8

docs/downloads/training/GoogleAuth.zip [new file with mode: 0644]
docs/html/google/auth/api-client.jd [new file with mode: 0644]
docs/html/google/auth/http-auth.jd [new file with mode: 0644]
docs/html/google/google_toc.cs
docs/html/google/play-services/setup.jd
docs/html/images/google/GoogleApiClient.png [new file with mode: 0644]
docs/html/images/google/GoogleApiClient@2x.png [new file with mode: 0644]

diff --git a/docs/downloads/training/GoogleAuth.zip b/docs/downloads/training/GoogleAuth.zip
new file mode 100644 (file)
index 0000000..18e9bf0
Binary files /dev/null and b/docs/downloads/training/GoogleAuth.zip differ
diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd
new file mode 100644 (file)
index 0000000..fda3310
--- /dev/null
@@ -0,0 +1,536 @@
+page.title=Accessing Google Play Services APIs
+page.tags="oauth 2.0","GoogleAuthUtil"
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="qv-wrapper">
+  <div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#Starting">Start a Connection</a>
+    <ol>
+      <li><a href="#HandlingFailures">Handle connection failures</a></li>
+      <li><a href="#MaintainingState">Maintain state while resolving an error</a></li>
+    </ol>
+  </li>
+  <li><a href="#Communicating">Communicate with Google Services</a>
+    <ol>
+      <li><a href="#Async">Using asynchronous calls</a></li>
+      <li><a href="#Sync">Using synchronous calls</a></li>
+    </ol>
+  </li>
+</ol>
+</div>
+</div>
+
+
+<p>When you want to make a connection to one of the Google APIs provided in the Google Play services
+library (such as Google+, Games, or Drive), you need to create an instance of <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> ("Google API Client"). The Google API Client provides a common entry point to all
+the Google Play services and manages the network connection between the user's device and each
+Google service.</p>
+
+<div class="sidebox" style="clear:right;width:190px">
+<h2>Connecting to REST APIs</h2>
+<p>If the Google API you want to use is not included in the Google Play services library, you can
+connect using the appropriate REST API, but you must obtain an OAuth 2.0 token. For more
+information, read <a href="{@docRoot}google/auth/http-auth.html">Authorizing with Google
+for REST APIs</a>.</p>
+</div>
+
+<p>This guide shows how you can use Google API Client to:</p>
+<ul>
+<li>Connect to one or more Google Play services asynchronously and handle failures.</li>
+<li>Perform synchronous and asynchronous API calls to any of the Google Play services.</li>
+</ul>
+
+<p class="note">
+<strong>Note:</strong> If you have an existing app that connects to Google Play services with a
+subclass of <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.html">{@code GooglePlayServicesClient}</a>, you should migrate to <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> as soon as possible.</p>
+
+
+<img src="{@docRoot}images/google/GoogleApiClient@2x.png" width="464px" alt="" />
+<p class="img-caption">
+<strong>Figure 1.</strong> An illustration showing how the Google API Client provides an
+interface for connecting and making calls to any of the available Google Play services such as
+Google Play Games and Google Drive.</p>
+
+
+
+<p>To get started, you must first install the Google Play services library (revision 15 or higher) for
+your Android SDK. If you haven't done so already, follow the instructions in <a
+href="{@docRoot}google/play-services/setup.html">Set Up Google
+Play Services SDK</a>.</p>
+
+
+
+
+<h2 id="Starting">Start a Connection</h2>
+
+<p>Once your project is linked to the Google Play services library, create an instance of <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> using the <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html">{@code
+GoogleApiClient.Builder}</a> APIs in your activity's {@link
+android.app.Activity#onCreate onCreate()} method. The <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html">{@code
+GoogleApiClient.Builder}</a> class
+provides methods that allow you to specify the Google APIs you want to use and your desired OAuth
+2.0 scopes. For example, here's a <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance that connects with the Google
+Drive service:</p>
+<pre>
+GoogleApiClient mGoogleApiClient = new GoogleApiClient.Builder(this)
+    .addApi(Drive.API)
+    .addScope(Drive.SCOPE_FILE)
+    .build();
+</pre>
+
+<p>You can add multiple APIs and multiple scopes to the same <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> by appending
+additional calls to
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html#addApi(com.google.android.gms.common.api.Api)"
+>{@code addApi()}</a> and
+<a href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.Builder.html#addScope(com.google.android.gms.common.api.Scope)"
+>{@code addScope()}</a>.</p>
+
+<p>However, before you can begin a connection by calling <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a> on the <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a>, you must specify an implementation for the callback interfaces, <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.ConnectionCallbacks.html"
+>{@code ConnectionCallbacks}</a> and <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.OnConnectionFailedListener.html"
+>{@code onConnectionFailedListener}</a>. These interfaces receive callbacks in
+response to the asynchronous <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a> method when the connection to Google Play services
+succeeds, fails, or becomes suspended.</p>
+
+<p>For example, here's an activity that implements the callback interfaces and adds them to the Google
+API Client:</p>
+
+<pre>
+import gms.common.api.*;
+import gms.drive.*;
+import android.support.v4.app.FragmentActivity;
+
+public class MyActivity extends FragmentActivity
+        implements ConnectionCallbacks, OnConnectionFailedListener {
+    private GoogleApiClient mGoogleApiClient;
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Create a GoogleApiClient instance
+        mGoogleApiClient = new GoogleApiClient.Builder(this)
+                .addApi(Drive.API)
+                .addScope(Drive.SCOPE_FILE)
+                .addConnectionCallbacks(this)
+                .addOnConnectionFailedListener(this)
+                .build();
+        ...
+    }
+
+    &#64;Override
+    public void onConnected(Bundle connectionHint) {
+        // Connected to Google Play services!
+        // The good stuff goes here.
+    }
+
+    &#64;Override
+    public void onConnectionSuspended(int cause) {
+        // The connection has been interrupted.
+        // Disable any UI components that depend on Google APIs
+        // until onConnected() is called.
+    }
+
+    &#64;Override
+    public void onConnectionFailed(ConnectionResult result) {
+        // This callback is important for handling errors that
+        // may occur while attempting to connect with Google.
+        //
+        // More about this in the next section.
+        ...
+    }
+}
+</pre>
+
+<p>With the callback interfaces defined, you're ready to call <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a>. To gracefully manage
+the lifecycle of the connection, you should call <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a> during the activity's {@link
+android.app.Activity#onStart onStart()} (unless you want to connect later), then call <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#disconnect()"
+>{@code disconnect()}</a> during the {@link android.app.Activity#onStop onStop()} method. For example:</p>
+<pre>
+    &#64;Override
+    protected void onStart() {
+        super.onStart();
+        if (!mResolvingError) {  // more about this later
+            mGoogleApiClient.connect();
+        }
+    }
+
+    &#64;Override
+    protected void onStop() {
+        mGoogleApiClient.disconnect();
+        super.onStop();
+    }
+</pre>
+
+<p>However, if you run this code, there's a good chance it will fail and your app will receive a call
+to <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)"
+>{@code onConnectionFailed()}</a> with the <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SIGN_IN_REQUIRED"
+>{@code SIGN_IN_REQUIRED}</a> error because the user account
+has not been specified. The next section shows how to handle this error and others.</p>
+
+
+
+
+<h3 id="HandlingFailures">Handle connection failures</h3>
+
+<p>When you receive a call to the <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)"
+>{@code onConnectionFailed()}</a> callback, you should call <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#hasResolution()"
+>{@code hasResolution()}</a> on the provided <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html"
+>{@code ConnectionResult}</a> object. If it returns true, you can
+request the user take immediate action to resolve the error by calling <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#startResolutionForResult(android.app.Activity, int)">{@code startResolutionForResult()}</a> on the <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html"
+>{@code ConnectionResult}</a> object. The <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#startResolutionForResult(android.app.Activity, int)"
+>{@code startResolutionForResult()}</a> behaves the same as {@link
+android.app.Activity#startActivityForResult startActivityForResult()} and launches the
+appropriate activity for the user
+to resolve the error (such as an activity to select an account).</p>
+
+<p>If <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#hasResolution()"
+>{@code hasResolution()}</a> returns false, you should instead call <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)"
+>{@code GooglePlayServicesUtil.getErrorDialog()}</a>, passing it the error code. This returns a {@link
+android.app.Dialog} provided by Google Play services that's appropriate for the given error. The
+dialog may simply provide a message explaining the error, but it may also provide an action to
+launch an activity that can resolve the error (such as when the user needs to install a newer
+version of Google Play services).</p>
+
+<p>For example, your <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)"
+>{@code onConnectionFailed()}</a> callback method should now look like this:</p>
+
+<pre>
+public class MyActivity extends FragmentActivity
+        implements ConnectionCallbacks, OnConnectionFailedListener {
+
+    // Request code to use when launching the resolution activity
+    private static final int REQUEST_RESOLVE_ERROR = 1001;
+    // Unique tag for the error dialog fragment
+    private static final String DIALOG_ERROR = "dialog_error";
+    // Bool to track whether the app is already resolving an error
+    private boolean mResolvingError = false;
+
+    ...
+
+    &#64;Override
+    public void onConnectionFailed(ConnectionResult result) {
+        if (mResolvingError) {
+            // Already attempting to resolve an error.
+            return;
+        } else if (result.hasResolution()) {
+            try {
+                mResolvingError = true;
+                result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
+            } catch (SendIntentException e) {
+                // There was an error with the resolution intent. Try again.
+                mGoogleApiClient.connect();
+            }
+        } else {
+            // Show dialog using GooglePlayServicesUtil.getErrorDialog()
+            showErrorDialog(result.getErrorCode());
+            mResolvingError = true;
+        }
+    }
+
+    // The rest of this code is all about building the error dialog
+
+    /* Creates a dialog for an error message */
+    private void showErrorDialog(int errorCode) {
+        // Create a fragment for the error dialog
+        ErrorDialogFragment dialogFragment = new ErrorDialogFragment();
+        // Pass the error that should be displayed
+        Bundle args = new Bundle();
+        args.putInt(DIALOG_ERROR, errorCode);
+        dialogFragment.setArguments(args);
+        dialogFragment.show(getSupportFragmentManager(), "errordialog");
+    }
+
+    /* Called from ErrorDialogFragment when the dialog is dismissed. */
+    public void onDialogDismissed() {
+        mResolvingError = false;
+    }
+
+    /* A fragment to display an error dialog */
+    public static class ErrorDialogFragment extends DialogFragment {
+        public ErrorDialogFragment() { }
+
+        &#64;Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            // Get the error code and retrieve the appropriate dialog
+            int errorCode = this.getArguments().getInt(DIALOG_ERROR);
+            return GooglePlayServicesUtil.getErrorDialog(errorCode,
+                    this.getActivity(), REQUEST_RESOLVE_ERROR);
+        }
+
+        &#64;Override
+        public void onDismiss(DialogInterface dialog) {
+            ((MainActivity)getActivity()).onDialogDismissed();
+        }
+    }
+}
+</pre>
+
+<p>Once the user completes the resolution provided by <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#startResolutionForResult(android.app.Activity, int)"
+>{@code startResolutionForResult()}</a> or <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)"
+>{@code GooglePlayServicesUtil.getErrorDialog()}</a>, your activity receives the {@link
+android.app.Activity#onActivityResult onActivityResult()} callback with the {@link
+android.app.Activity#RESULT_OK}
+result code. You can then call <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a> again. For example:</p>
+
+<pre>
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (requestCode == REQUEST_RESOLVE_ERROR) {
+        mResolvingError = false;
+        if (resultCode == RESULT_OK) {
+            // Make sure the app is not already connected or attempting to connect
+            if (!mGoogleApiClient.isConnecting() &&
+                    !mGoogleApiClient.isConnected()) {
+                mGoogleApiClient.connect();
+            }
+        }
+    }
+}
+</pre>
+
+<p>In the above code, you probably noticed the boolean, {@code mResolvingError}. This keeps track of
+the app state while the user is resolving the error to avoid repetitive attempts to resolve the
+same error. For instance, while the account picker dialog is showing to resolve the <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SIGN_IN_REQUIRED"
+>{@code SIGN_IN_REQUIRED}</a> error, the user may rotate the screen. This recreates your activity and causes
+your {@link android.app.Activity#onStart onStart()} method to be called again, which then calls <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html#connect()"
+>{@code connect()}</a> again. This results in another call to <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#startResolutionForResult(android.app.Activity, int)"
+>{@code startResolutionForResult()}</a>, which
+creates another account picker dialog in front of the existing one.</p>
+
+<p>This boolean is effective only
+if retained across activity instances, though. The next section explains further.</p>
+
+
+
+<h3 id="MaintainingState">Maintain state while resolving an error</h3>
+
+<p>To avoid executing the code in <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)"
+>{@code onConnectionFailed()}</a> while a previous attempt to resolve an
+error is ongoing, you need to retain a boolean that tracks whether your app is already attempting
+to resolve an error.</p>
+
+<p>As shown in the code above, you should set a boolean to {@code true} each time you call <a
+href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#startResolutionForResult(android.app.Activity, int)"
+>{@code startResolutionForResult()}</a> or display the dialog from <a
+href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)"
+>{@code GooglePlayServicesUtil.getErrorDialog()}</a>. Then when you
+receive {@link android.app.Activity#RESULT_OK} in the {@link android.app.Activity#onActivityResult
+onActivityResult()} callback, set the boolean to {@code false}.</p>
+
+<p>To keep track of the boolean across activity restarts (such as when the user rotates the screen),
+save the boolean in the activity's saved instance data using {@link
+android.app.Activity#onSaveInstanceState onSaveInstanceState()}:</p>
+
+<pre>
+private static final String STATE_RESOLVING_ERROR = "resolving_error";
+
+&#64;Override
+protected void onSaveInstanceState(Bundle outState) {
+    super.onSaveInstanceState(outState);
+    outState.putBoolean(STATE_RESOLVING_ERROR, mResolvingError);
+}
+</pre>
+
+<p>Then recover the saved state during {@link android.app.Activity#onCreate onCreate()}:</p>
+
+<pre>
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+
+    ...
+    mResolvingError = savedInstanceState != null
+            && savedInstanceState.getBoolean(STATE_RESOLVING_ERROR, false);
+}
+</pre>
+
+<p>Now you're ready to safely run your app and connect to Google Play services.
+How you can perform read and write requests to any of the Google Play services
+using <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> is discussed in the next section.</p>
+
+<p>For more information about each services's APIs available once you're connected,
+consult the corresponding documentation, such as for
+<a href="{@docRoot}google/play-services/games.html">Google Play Games</a> or
+<a href="{@docRoot}google/play-services/drive.html">Google Drive</a>.
+</p>
+
+
+
+
+<h2 id="Communicating">Communicate with Google Services</h2>
+
+<p>Once connected, your client can make read and write calls using the service-specific APIs for which
+your app is authorized, as specified by the APIs and scopes you added to your <a
+href="{@docRoot}reference/com/google/android/gms/common/api/GoogleApiClient.html">{@code
+GoogleApiClient}</a> instance.</p>
+
+<p class="note">
+<strong>Note:</strong> Before making calls to specific Google services, you may first need to
+register your app in the Google Developer Console. For specific instructions, refer to the
+appropriate getting started guide for the API you're using, such as <a href=
+"https://developers.google.com/drive/android/get-started">Google Drive</a> or <a href=
+"https://developers.google.com/+/mobile/android/getting-started">Google+</a>.</p>
+
+<p>When you perform a read or write request using Google API Client, the immediate result is returned
+as a <a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">{@code
+PendingResult}</a> object. This is an object representing the request, which hasn't yet
+been delivered to the Google service.</p>
+
+<p>For example, here's a request to read a file from Google Drive that provides a
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">{@code
+PendingResult}</a> object:</p>
+
+<pre>
+Query query = new Query.Builder()
+        .addFilter(Filters.eq(SearchableField.TITLE, filename));
+PendingResult result = Drive.DriveApi.query(mGoogleApiClient, query);
+</pre>
+
+<p>Once you have the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">{@code
+PendingResult}</a>, you can continue by making the request either asynchronous
+or synchronous.</p>
+
+
+<h3 id="Async">Using asynchronous calls</h3>
+
+<p>To make the request asynchronous, call <a
+href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#setResultCallback(com.google.android.gms.common.api.ResultCallback<R>)"
+>{@code setResultCallback()}</a> on the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">{@code
+PendingResult}</a> and
+provide an implementation of the <a
+href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html"
+>{@code ResultCallback}</a> interface. For example, here's the request
+executed asynchronously:</p>
+
+<pre>
+private void loadFile(String filename) {
+    // Create a query for a specific filename in Drive.
+    Query query = new Query.Builder()
+            .addFilter(Filters.eq(SearchableField.TITLE, filename))
+            .build();
+    // Invoke the query asynchronously with a callback method
+    Drive.DriveApi.query(mGoogleApiClient, query)
+            .setResultCallback(new ResultCallback&lt;DriveApi.MetadataBufferResult>() {
+        &#64;Override
+        public void onResult(DriveApi.MetadataBufferResult result) {
+            // Success! Handle the query result.
+            ...
+        }
+    });
+}
+</pre>
+
+<p>When your app receives a <a
+href="{@docRoot}reference/com/google/android/gms/common/api/Result.html">{@code Result}</a>
+object in the <a
+href="{@docRoot}reference/com/google/android/gms/common/api/ResultCallback.html#onResult(R)"
+>{@code onResult()}</a> callback, it is delivered as an instance of the
+appropriate subclass as specified by the API you're using, such as <a
+href="{@docRoot}reference/com/google/android/gms/drive/DriveApi.MetadataBufferResult.html"
+>{@code DriveApi.MetadataBufferResult}</a>.</p>
+
+
+<h3 id="Sync">Using synchronous calls</h3>
+
+<p>If you want your code to execute in a strictly defined order, perhaps because the result of one
+call is needed as an argument to another, you can make your request synchronous by calling <a
+href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"
+>{@code await()}</a> on the
+<a href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html">{@code
+PendingResult}</a>. This blocks the thread and returns the <a
+href="{@docRoot}reference/com/google/android/gms/common/api/Result.html">{@code Result}</a> object
+when the request completes, which is delivered as an instance of the
+appropriate subclass as specified by the API you're using, such as <a
+href="{@docRoot}reference/com/google/android/gms/drive/DriveApi.MetadataBufferResult.html"
+>{@code DriveApi.MetadataBufferResult}</a>.</p>
+
+<p>Because calling <a
+href="{@docRoot}reference/com/google/android/gms/common/api/PendingResult.html#await()"
+>{@code await()}</a> blocks the thread until the result arrives, it's important that you
+never perform this call on the UI thread. So, if you want to perform synchronous requests to a
+Google Play service, you should create a new thread, such as with {@link android.os.AsyncTask} in
+which to perform the request. For example, here's how to perform the same file request to Google
+Drive as a synchronous call:</p>
+
+<pre>
+private void loadFile(String filename) {
+    new GetFileTask().execute(filename);
+}
+
+private class GetFileTask extends AsyncTask<String, Void, Void> {
+    protected void doInBackground(String filename) {
+        Query query = new Query.Builder()
+                .addFilter(Filters.eq(SearchableField.TITLE, filename))
+                .build();
+        // Invoke the query synchronously
+        DriveApi.MetadataBufferResult result =
+                Drive.DriveApi.query(mGoogleApiClient, query).await();
+
+        // Continue doing other stuff synchronously
+        ...
+    }
+}
+</pre>
+
+<p class="note">
+<strong>Tip:</strong> You can also enqueue read requests while not connected to Google Play
+services. For example, execute a method to read a file from Google Drive regardless of whether your
+Google API Client is connected yet. Then once a connection is established, the read requests
+execute and you'll receive the results. Any write requests, however, will generate an error if you
+call them while your Google API Client is not connected.</p>
+
diff --git a/docs/html/google/auth/http-auth.jd b/docs/html/google/auth/http-auth.jd
new file mode 100644 (file)
index 0000000..3b2a83f
--- /dev/null
@@ -0,0 +1,534 @@
+page.title=Authorizing with Google for REST APIs
+page.tags="oauth 2.0","GoogleAuthUtil"
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+
+<div id="qv-wrapper">
+  <div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#Register">Register Your App</a></li>
+  <li><a href="#AccountPicker">Invoke the Account Picker</a></li>
+  <li><a href="#AccountName">Retrieve the Account Name</a></li>
+  <li><a href="#ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</a></li>
+  <li><a href="#HandleExceptions">Handle Exceptions</a></li>
+</ol>
+<h2>Try it out</h2>
+
+<div class="download-box">
+<a href="http://developer.android.com/shareables/training/GoogleAuth.zip"
+  class="button">Download the sample app</a>
+<p class="filename">GoogleAuth.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>When you want your Android app to access Google APIs using the user's Google account over
+HTTP, the <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
+class and related APIs provide your users a secure and consistent experience for picking an
+account and retrieving an OAuth 2.0 token for your app.</p>
+
+<p>You can then use that token in your HTTP-based communications with Google API services
+that are not included in the <a href="{@docRoot}google/play-services/index.html">Google Play
+services</a> library, such as the Blogger or Translate APIs.</p>
+
+<p class="note"><strong>Note:</strong> An OAuth 2.0 token using <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
+is required only for certain types of Google
+APIs that you need to access over HTTP. If you're instead using the <a
+href="{@docRoot}google/play-services/index.html">Google Play services library</a> to access Google
+APIs such as <a href="{@docRoot}google/play-services/plus.html">Google+</a> or <a
+href="{@docRoot}google/play-services/games.html">Play Games</a>, you don't need an OAuth 2.0
+token and you can instead access these services using the {@code GoogleApiClient}. For more
+information, read <a href="{@docRoot}google/auth/api-client.html">Accessing Google Play
+Services APIs</a>.</p>
+
+<p>To get started with <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
+for accessing Google's REST APIs, you must set up your Android app project with the Google Play
+services library. Follow the procedures in <a href=
+"{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p>
+
+
+
+
+<h2 id="Register">Register Your App</h2>
+
+<p>Before you can publish an app that retrieves an OAuth 2.0 token for Google REST APIs,
+you must register your Android app with the Google Cloud Console by providing your app's
+package name and the SHA1 fingerprint of the keystore with which you sign your release APK.</p>
+
+<p class="caution"><strong>Caution:</strong> While you are testing an APK that's <a
+href="{@docRoot}tools/publishing/app-signing.html#debugmode">signed with a
+debug key</a>, Google does not require that your app be registered in Google Cloud Console. However,
+your app must be registered in Google Cloud Console in order to continue working once it is
+<a href="{@docRoot}tools/publishing/app-signing.html#releasemode">signed
+with a release key</a>.</p>
+
+<p>To register your Android app with Google Cloud Console:</p>
+
+<ol>
+<li>Visit <a href="https://cloud.google.com/console" class="external-link" target="_blank"
+>Google Cloud Console</a>.
+<li>If you have an existing project to which you're adding an Android app, select the project.
+Otherwise, click <strong>Create project</strong> at the top, enter your project name and ID,
+then click <strong>Create</strong>.
+<p class="note"><strong>Note:</strong> The name you provide for the project is the name that
+appears to users in the Google Settings app in the list of <em>Connected apps</em>.</p>
+<li>In the left-side navigation, select <strong>APIs &amp; auth</strong>.
+<li>Enable the API you'd like to use by setting the Status to <strong>ON</strong>.
+
+<li>In the left-side navigation, select <strong>Credentials</strong>.
+<li>Click <strong>Create new client ID</strong> or <strong>Create new key</strong>
+as appropriate for your app.</li>
+<li>Complete the form that appears by filling in your Android app details.
+<p>To get the SHA1 fingerprint for your app, run the following command in a terminal:
+<pre class="no-pretty-print">
+keytool -exportcert -alias &lt;keystore_alias> -keystore &lt;keystore_path> -list -v
+</pre>
+<p>For example, you're using a debug-key with Eclipse, then the command looks like this:</p>
+<pre class="no-pretty-print">
+keytool -exportcert -alias androiddebugkey-keystore ~/.android/debug.keystore -list -v
+</pre>
+<p>Then the keystore password is "android".</p>
+</li>
+<li>Click <strong>Create</strong>.
+</ol>
+
+<p>The Credentials page then provides the available credentials such as an OAuth 2.0 client ID and
+an Android Key, but you don't need these to authorize your Android users. Simply registering your
+app with the package name and SHA1 makes the Google services accessible by your app.
+
+
+<p>To acquire the OAuth 2.0 token that will grant you access to Google APIs over HTTP, you need to
+first identify the user's Google account with which you'll query the servers. For this task, the
+Google Play services library provides a convenient account picker dialog you can invoke using
+<a href="{@docRoot}reference/com/google/android/gms/common/AccountPicker.html">{@code
+AccountPicker}</a>. The result delivered to your activity from the account picker is the account
+name you'll use to request the OAuth 2.0 token in the next lesson.</p>
+
+<p class="note"><strong>Note:</strong> In order to use the APIs discussed here, you must
+include the Google Play services library with your project. If you haven't set up your project
+with the library yet, read the guide to <a
+href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p>
+
+
+
+<h2 id="AccountPicker">Invoke the Account Picker</h2>
+
+<p>To open the account picker dialog that's managed by the Google Play services library, call
+{@link android.app.Activity#startActivityForResult startActivityForResult()} using an {@link
+android.content.Intent} returned by <a href=
+"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)">
+{@code AccountPicker.newChooseAccountIntent}</a>.</p>
+
+
+<p>For example:</p>
+<pre>
+static final int REQUEST_CODE_PICK_ACCOUNT = 1000;
+
+private void pickUserAccount() {
+    String[] accountTypes = new String[]{"com.google"};
+    Intent intent = AccountPicker.newChooseAccountIntent(null, null,
+            accountTypes, false, null, null, null, null);
+    startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT);
+}
+</pre>
+
+<p>When this code executes, a dialog appears for the user to pick an account. When the user
+selects the account, your activity receives the result in the {@link
+android.app.Activity#onActivityResult onActivityResult()} callback.</p>
+
+<p>Most apps should pass the <a href=
+"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)">
+{@code newChooseAccountIntent()}</a> method the same arguments shown in the above example,
+which indicate that:</p>
+
+
+<ul>
+<li>There is no currently selected account.</li>
+<li>There is no restricted list of accounts.</li>
+<li>The dialog should list only accounts from the "com.google" domain.</li>
+<li>Don't prompt the user to pick an account if there's only one available account (just use that
+one). However, even if only one account currently exists, the dialog may include an option for the
+user to add a new account.</li>
+<li>There is no custom title for the dialog.</li>
+<li>There is no specific auth token type required.</li>
+<li>There are no restrictions based on account features.</li>
+<li>There are no authenticator-specific options.</li>
+</ul>
+
+<p>For more details about these arguments, see the <a href=
+"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)">
+{@code newChooseAccountIntent()}</a> method documentation.</p>
+
+
+
+
+<h2 id="AccountName">Retrieve the Account Name</h2>
+
+<p>Once the user selects an account, your activity receives a call to its
+{@link android.app.Activity#onActivityResult onActivityResult()} method. The received
+{@link android.content.Intent} includes an extra for
+{@link android.accounts.AccountManager#KEY_ACCOUNT_NAME}, specifying the account name
+(an email address) you must use to acquire the OAuth 2.0 token.</p>
+
+<p>Here's an example implementation of the callback {@link android.app.Activity#onActivityResult
+onActivityResult()} that receives the selected account:</p>
+
+<pre>
+String mEmail; // Received from <a href=
+"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)"
+>{@code newChooseAccountIntent()}</a>; passed to <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a>
+
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
+        // Receiving a result from the AccountPicker
+        if (resultCode == RESULT_OK) {
+            mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+            // With the account name acquired, go get the auth token
+            getUsername();
+        } else if (resultCode == RESULT_CANCELED) {
+            // The account picker dialog closed without selecting an account.
+            // Notify users that they must pick an account to proceed.
+            Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show();
+        }
+    }
+    // Later, more code will go here to handle the result from some exceptions...
+}
+</pre>
+
+<p>You can now pass the account name held by {@code mEmail} to <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> (which is what the {@code getUsername()} method
+does), but because it performs network transactions, this method should not be called from the
+UI thread. The next lesson shows how to create an {@link android.os.AsyncTask} to get the auth token
+on a separate thread.</p>
+
+
+<p>Once you have retrieved the account name for the user's Google account, you can call <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a>, which returns the access token string required by Google API
+services.</p>
+
+
+<p>Calling this method is generally a straightforward procedure, but you must be
+aware that:</p>
+<ul>
+<li>The <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> method requires a network connection, so your app must
+acquire the {@link android.Manifest.permission#INTERNET} permission. You should also check whether
+the device has a network connection at runtime by querying {@link android.net.NetworkInfo}, which
+requires that your app also acquire the {@link android.Manifest.permission#ACCESS_NETWORK_STATE}
+permissions&mdash;for more details, read <a href=
+"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a>.</li>
+<li>Because the <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> method performs a synchronous network transaction, you should
+always perform this call from a worker thread to avoid blocking your app's UI thread.</li>
+<li>As is true when performing any network transaction, you should be prepared to handle
+exceptions that may occur. There are also specific exceptions that
+<a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> may throw, defined as <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
+GoogleAuthException}</a> objects.</li>
+</ul>
+
+<p>This lesson shows how you can gracefully handle these concerns by performing authentication in
+an {@link android.os.AsyncTask} and providing users with the appropriate information and available
+actions during known exceptions.</p>
+
+<p class="note"><strong>Note:</strong> The code shown in this lesson, using <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code GoogleAuthUtil.getToken()}</a>,
+is appropriate when you will be requesting the OAuth token from an {@link android.app.Activity}.
+However, if you need to request the OAuth token from a {@link android.app.Service}, then you
+should instead use <a
+href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code
+getTokenWithNotification()}</a>. This method works the same as <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code GoogleAuthUtil.getToken()}</a>, but if an error occurs, it
+also creates an appropriate
+<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">notification</a>
+that allows the user can recover from the error.
+The sample available for download above includes code showing how to use this method instead.</p>
+
+
+<h2 id="ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</h2>
+
+<p>The {@link android.os.AsyncTask} class provides a simple way to create a worker thread for jobs
+that should not run on your UI thread. This lesson focuses on how to create such a thread
+to get your auth token; for a more complete discussion about {@link android.os.AsyncTask},
+read <a href="{@docRoot}training/articles/perf-anr.html">Keeping Your
+App Responsive</a> and the {@link android.os.AsyncTask} class reference.</p>
+
+
+<p>The {@link android.os.AsyncTask#doInBackground doInBackground()} method in your {@link
+android.os.AsyncTask} class is where you should call the <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> method. You can also use it to catch some of the generic
+exceptions that may occur during your network transactions.</p>
+
+<p>For example, here's part of an {@link android.os.AsyncTask} subclass that calls <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a>:</p>
+
+<pre>
+public class GetUsernameTask extends AsyncTask<Void, Void, Void>{
+    Activity mActivity;
+    String mScope;
+    String mEmail;
+
+    GetUsernameTask(Activity activity, String name, String scope) {
+        this.mActivity = activity;
+        this.mScope = scope;
+        this.mEmail = name;
+    }
+
+    /**
+     * Executes the asynchronous job. This runs when you call execute()
+     * on the AsyncTask instance.
+     */
+    &#64;Override
+    protected Void doInBackground(Void... params) {
+        try {
+            String token = fetchToken();
+            if (token != null) {
+                // <b>Insert the good stuff here.</b>
+                // Use the token to access the user's Google data.
+                ...
+            }
+        } catch (IOException e) {
+            // The fetchToken() method handles Google-specific exceptions,
+            // so this indicates something went wrong at a higher level.
+            // TIP: Check for network connectivity before starting the AsyncTask.
+            ...
+        }
+        return null;
+    }
+
+    /**
+     * Gets an authentication token from Google and handles any
+     * GoogleAuthException that may occur.
+     */
+    protected String fetchToken() throws IOException {
+        try {
+            <b>return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);</b>
+        } catch (UserRecoverableAuthException userRecoverableException) {
+            // GooglePlayServices.apk is either old, disabled, or not present
+            // so we need to show the user some UI in the activity to recover.
+            mActivity.handleException(userRecoverableException);
+        } catch (GoogleAuthException fatalException) {
+            // Some other type of unrecoverable exception has occurred.
+            // Report and log the error as appropriate for your app.
+            ...
+        }
+        return null;
+    }
+    ...
+}
+</pre>
+
+<p>In order to call <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a>, you must provide the app {@link android.content.Context},
+the account name retrieved from the account picker, and the scope for your auth
+token request. The above sample code (and the attached sample) defines these arguments with
+class members that the host activity passes to
+the {@link android.os.AsyncTask} class constructor.</p>
+
+<p class="note"><strong>Note:</strong>
+As shown by the {@code fetchToken()} method above, you must handle
+special exceptions that may occur during the <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a> method. The next section shows how you should
+respond to these exceptions.</p>
+
+<p>Once you have an {@link android.os.AsyncTask} subclass defined,
+you can instantiate and execute an instance after you get the user's
+account name from the account picker.
+For example, back in the {@link android.app.Activity} class you can do something like this:</p>
+
+<pre>
+String mEmail; // Received from <a href=
+"{@docRoot}reference/com/google/android/gms/common/AccountPicker.html#newChooseAccountIntent(android.accounts.Account,%20java.util.ArrayList%3Candroid.accounts.Account%3E,%20java.lang.String[],%20boolean,%20java.lang.String,%20java.lang.String,%20java.lang.String[],%20android.os.Bundle)"
+>{@code newChooseAccountIntent()}</a>; passed to <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a>
+private static final String SCOPE =
+        "oauth2:https://www.googleapis.com/auth/userinfo.profile";
+
+/**
+ * Attempts to retrieve the username.
+ * If the account is not yet known, invoke the picker. Once the account is known,
+ * start an instance of the AsyncTask to get the auth token and do work with it.
+ */
+private void getUsername() {
+    if (mEmail == null) {
+        pickUserAccount();
+    } else {
+        if (isDeviceOnline()) {
+            <b>new GetUsernameTask(HelloActivity.this, mEmail, SCOPE).execute();</b>
+        } else {
+            Toast.makeText(this, R.string.not_online, Toast.LENGTH_LONG).show();
+        }
+    }
+}
+</pre>
+
+<p>The {@code pickUserAccount()} method is shown in the first lesson, <a
+href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>.
+
+<p>For information about how to check whether the device is currently online (as performed by
+the {@code isDeviceOnline()} method above), see the attached sample app or the
+<a href=
+"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a> lesson.</p>
+
+<p>The only part left is how you should handle the exceptions that may occur when you call
+<a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a>.</p>
+
+
+
+
+<h2 id="HandleExceptions">Handle Exceptions</h2>
+
+<p>As shown in the <code>fetchToken()</code> method above, you must catch all occurrences of <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
+GoogleAuthException}</a> when you call <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
+{@code GoogleAuthUtil.getToken()}</a>.</p>
+
+<p>To provide users information and a proper solution to issues that may occur while acquiring the
+auth token, it's important that you properly handle the following subclasses of <a href=
+"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
+GoogleAuthException}</a>:</p>
+
+
+<dl>
+<dt><a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code UserRecoverableAuthException}</a></dt>
+  <dd>This is an error that users can resolve through some verification. For example, users may
+  need to confirm that your app is allowed to access their Google data or they may need to re-enter
+  their account password. When you receive this exception, call <a href=
+"{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html#getIntent()">{@code
+getIntent()}</a> on the instance and pass the returned {@link android.content.Intent} to {@link
+android.app.Activity#startActivityForResult startActivityForResult()} to give users the opportunity
+to solve the problem, such as by logging in.</dd>
+
+<dt><a href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html">{@code GooglePlayServicesAvailabilityException}</a></dt>
+    <dd>This is a specific type of <a
+    href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
+    UserRecoverableAuthException}</a> indicating that the user's current version
+of Google Play services is outdated. Although the recommendation above for
+<a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
+    UserRecoverableAuthException}</a> also works for this exception, calling {@link
+android.app.Activity#startActivityForResult startActivityForResult()} will immediately send users
+ to Google Play Store to install an update, which may be confusing. So you should instead call <a
+ href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html#getConnectionStatusCode()">
+{@code getConnectionStatusCode()}</a> and pass the result to <a href=
+"{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int,%20android.app.Activity,%20int,%20android.content.DialogInterface.OnCancelListener)">
+{@code GooglePlayServicesUtil.getErrorDialog()}</a>. This returns a {@link android.app.Dialog}
+that includes an appropriate message and a button to take users to Google Play Store so they
+can install an update.</dd>
+</dl>
+
+<p>For example, the <code>fetchToken()</code> method in the above sample code catches any
+occurrence of <a
+href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
+UserRecoverableAuthException}</a> and passes it back to the activity with a method called
+{@code handleException()}. Here's what that method in the activity may look like:</p>
+
+
+<pre>
+static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001;
+
+/**
+ * This method is a hook for background threads and async tasks that need to
+ * provide the user a response UI when an exception occurs.
+ */
+public void handleException(final Exception e) {
+    // Because this call comes from the AsyncTask, we must ensure that the following
+    // code instead executes on the UI thread.
+    runOnUiThread(new Runnable() {
+        &#64;Override
+        public void run() {
+            if (e instanceof GooglePlayServicesAvailabilityException) {
+                // The Google Play services APK is old, disabled, or not present.
+                // Show a dialog created by Google Play services that allows
+                // the user to update the APK
+                int statusCode = ((GooglePlayServicesAvailabilityException)e)
+                        .getConnectionStatusCode();
+                Dialog dialog = GooglePlayServicesUtil.getErrorDialog(statusCode,
+                        HelloActivity.this,
+                        REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR);
+                dialog.show();
+            } else if (e instanceof UserRecoverableAuthException) {
+                // Unable to authenticate, such as when the user has not yet granted
+                // the app access to the account, but the user can fix this.
+                // Forward the user to an activity in Google Play services.
+                Intent intent = ((UserRecoverableAuthException)e).getIntent();
+                startActivityForResult(intent,
+                        REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR);
+            }
+        }
+    });
+}
+</pre>
+
+<p>Notice that in both cases, the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR}
+request code is passed with the request to handle the exception with a dialog or activity.
+This way, when the user completes the appropriate action to resolve the exception,
+your {@link android.app.Activity#onActivityResult onActivityResult()} method receives an
+intent that includes this request code and you can try to acquire the auth
+token again.</p>
+
+
+<p>For example, the following code is a complete implementation of {@link
+android.app.Activity#onActivityResult onActivityResult()} that handles results for
+both the {@code REQUEST_CODE_PICK_ACCOUNT} action (shown in the previous lesson, <a
+href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>)
+and the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR} action, which occurs after the user
+completes one of the actions above to resolve an exception.</p>
+
+
+<pre>
+&#64;Override
+protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
+        // Receiving a result from the AccountPicker
+        if (resultCode == RESULT_OK) {
+            mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
+            // With the account name acquired, go get the auth token
+            getUsername();
+        } else if (resultCode == RESULT_CANCELED) {
+            // The account picker dialog closed without selecting an account.
+            // Notify users that they must pick an account to proceed.
+            Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show();
+        }
+    } else if ((requestCode == REQUEST_CODE_RECOVER_FROM_AUTH_ERROR ||
+            requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR)
+            && resultCode == RESULT_OK) {
+        // Receiving a result that follows a GoogleAuthException, try auth again
+        getUsername();
+    }
+}
+</pre>
+
+<p>For a complete set of code that acquires the OAuth token and queries a Google service
+over HTTP (including how to use <a
+href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code
+getTokenWithNotification()}</a> when you need to acquire the token from
+a {@link android.app.Service}), see the sample app available for download at the top
+of this page.</p>
+
+
+
index 963cfc1..6ff00c0 100644 (file)
           <span class="en">Wallet</span>
       </a></div>
   </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="<?cs var:toroot?>google/play-services/auth.html">
-          <span class="en">Authorization</span>
-      </a></div>
-  </li>
-
 
 
   <li class="nav-section">
       <li><a href="<?cs var:toroot?>google/play-services/setup.html">
           <span class="en">Setup</span></a>
       </li>
+      <li class="nav-section">
+        <div class="nav-section-header"><a href="<?cs var:toroot?>google/auth/api-client.html">
+          <span class="en">Accessing Google Play Services APIs</span></a>
+        </div>
+        <ul>
+          <li>
+            <a href="<?cs var:toroot ?>google/auth/http-auth.html">
+              <span class="en">Authorizing with Google for REST APIs</span>
+            </a>
+          </li>
+        </ul>
+      </li>
       <li id="gms-tree-list" class="nav-section">
         <div class="nav-section-header">
           <a href="<?cs var:toroot ?>reference/gms-packages.html">
index fb5daf8..3137890 100644 (file)
@@ -187,14 +187,13 @@ to your <code>gradle.build</code> file's build types. For more information, see
 </ol>
 
 
-
-
 <h2 id="ensure">Ensure Devices Have the Google Play services APK</h2>
 
 <p>As described in the <a href="{@docRoot}google/play-services/index.html">Google Play services
 introduction</a>, Google Play delivers service updates for users on
-Android 2.3 through the Google Play Store app. However, updates might not reach
-all users immediately.</p>
+Android 2.3 and higher through the Google Play Store app. However, updates might not reach
+all users immediately, so your app should verify the version available before attempting to
+perform API transactions.</p>
 
 <p class="caution">
 <strong>Important:</strong>
@@ -204,77 +203,36 @@ all users immediately.</p>
     {@link android.app.Activity#onResume onResume()} method of the main activity.
 </p>
 
-<p>Here are four scenarios that describe the possible state of the Google Play services APK on
-a user's device:</p>
-<ol>
-    <li>
-        A recent version of the Google Play Store app is installed, and the most recent Google Play
-        services APK has been downloaded.
-    </li>
-    <li>
-        A recent version of the Google Play Store app is installed, but the most recent Google Play
-        services APK has <em>not</em> been downloaded.
-    </li>
-    <li>
-        An old version of the Google Play Store app, which does not proactively download Google Play
-        services updates, is present.
-    </li>
-    <li>
-        The Google Play services APK is missing or disabled on the device, which might happen if the
-        user explicitly uninstalls or disables it.
-    </li>
-</ol>
-<p>
-    Case 1 is the success scenario and is the most common. However, because the other scenarios can
-    still happen, you must handle them every time your app connects to a Google Play service to
-    ensure that the Google Play services APK is present, up-to-date, and enabled.
-</p>
-<p>
-    To help you, the Google Play services client library has utility methods to
-    determine whether or not the Google Play services APK is recent enough to support the
-    version of the client library you are using.  If not, the client library sends users to the
-    Google Play Store to download the recent version of the Google Play services APK.
-</p>
+<p>The Google Play services library includes utility methods that help you determine whether or not
+the Google Play services version on the device supports the version of the client library you are
+using. If the version on the device is too old, the system will take the user to Google Play Store
+in order to install the recent version of the Google Play services.</p>
 
-<p class="note">
-<b>Note:</b>
-    The Google Play services APK is not visible by searching the Google Play Store. The client
-    library provides a deep link into the Google Play Store when it detects that the device has a
-    missing or incompatible Google Play services APK.
-</p>
+<p>Because each app uses Google Play services differently, it's up to you decide the appropriate
+place in your app to check verify the Google Play services version. For example, if Google Play
+services is required for your app at all times, you might want to do it when your app first
+launches. On the other hand, if Google Play services is an optional part of your app, you can check
+the version only once the user navigates to that portion of your app.</p>
 
-<p>
-    It is up to you choose the appropriate place in your app to do the following steps to check for
-    a valid Google Play services APK. For example, if Google Play services is required for your app,
-    you might want to do it when your app first launches. On the other hand, if Google Play services
-    is an optional part of your app, you can do these checks if the user navigates to that portion
-    of your app:
-</p>
-
-<ol>
-    <li>
-        Query for the status of Google Play services on the device with the
-<a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)"
->{@code isGooglePlayServicesAvailable()}</a> method, which returns a result code.
-    </li>
-    <li>
-        If the result code is
+<p>To verify the Google Play services version, call <a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#isGooglePlayServicesAvailable(android.content.Context)"
+>{@code isGooglePlayServicesAvailable()}</a>. If the result code is
 <a href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SUCCESS"
 >{@code SUCCESS}</a>,
-        then the Google Play services APK is up-to-date, and you can proceed as normal.
-    </li>
-    <li>
-        If the result code is
+        then the Google Play services APK is up-to-date and you can continue to make a connection.
+If, however, the result code is
 <a href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SERVICE_MISSING"
 >{@code SERVICE_MISSING}</a>,
 <a href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SERVICE_VERSION_UPDATE_REQUIRED"
 >{@code SERVICE_VERSION_UPDATE_REQUIRED}</a>,
         or
 <a href="{@docRoot}reference/com/google/android/gms/common/ConnectionResult.html#SERVICE_DISABLED"
->{@code SERVICE_DISABLED}</a>, then
+>{@code SERVICE_DISABLED}</a>, then the user needs to install an update. So,
   call <a href="{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int, android.app.Activity, int)"
-  >{@code getErrorDialog()}</a>
-  to display an error message to the user, which allows the user to download the APK
-  from the Google Play Store or enable it in the device's system settings.
-    </li>
-</ol>
+  >{@code GooglePlayServicesUtil.getErrorDialog()}</a> and pass it the result error code.
+This returns a {@link android.app.Dialog} you should show, which provides an appropriate message
+about the error and provides an action
+that takes the user to Google Play Store to install the update.</p>
+
+
+<p>To then begin a connection to Google Play services, read <a
+href="{@docRoot}google/auth/api-client.html">Accessing Google Play Services APIs</a>.</p>
\ No newline at end of file
diff --git a/docs/html/images/google/GoogleApiClient.png b/docs/html/images/google/GoogleApiClient.png
new file mode 100644 (file)
index 0000000..6107641
Binary files /dev/null and b/docs/html/images/google/GoogleApiClient.png differ
diff --git a/docs/html/images/google/GoogleApiClient@2x.png b/docs/html/images/google/GoogleApiClient@2x.png
new file mode 100644 (file)
index 0000000..a98bc2c
Binary files /dev/null and b/docs/html/images/google/GoogleApiClient@2x.png differ