diff options
author | Scott Main <smain@google.com> | 2013-10-04 19:08:08 -0700 |
---|---|---|
committer | Scott Main <smain@google.com> | 2014-02-12 08:54:49 -0800 |
commit | a4c5bba07e121569ab504b2191ee576f09407f5b (patch) | |
tree | f55fe97d473bffb1f9aff0c173e60d515f0552e1 /docs | |
parent | 1654264edd68adfd4347d12112aa3124f1d0acb9 (diff) | |
download | frameworks_base-a4c5bba07e121569ab504b2191ee576f09407f5b.zip frameworks_base-a4c5bba07e121569ab504b2191ee576f09407f5b.tar.gz frameworks_base-a4c5bba07e121569ab504b2191ee576f09407f5b.tar.bz2 |
add document about GoogleApiClient and one about GoogleAuthUtil
also remove the Authorization document from the Google Services
section.
bug:10679818
Change-Id: Ibfade1eca68d89afe30b79d75ca5e38a2b3a84a8
Diffstat (limited to 'docs')
-rw-r--r-- | docs/downloads/training/GoogleAuth.zip | bin | 0 -> 40808 bytes | |||
-rw-r--r-- | docs/html/google/auth/api-client.jd | 536 | ||||
-rw-r--r-- | docs/html/google/auth/http-auth.jd | 534 | ||||
-rw-r--r-- | docs/html/google/google_toc.cs | 18 | ||||
-rw-r--r-- | docs/html/google/play-services/setup.jd | 92 | ||||
-rw-r--r-- | docs/html/images/google/GoogleApiClient.png | bin | 0 -> 25771 bytes | |||
-rw-r--r-- | docs/html/images/google/GoogleApiClient@2x.png | bin | 0 -> 62960 bytes |
7 files changed, 1107 insertions, 73 deletions
diff --git a/docs/downloads/training/GoogleAuth.zip b/docs/downloads/training/GoogleAuth.zip Binary files differnew file mode 100644 index 0000000..18e9bf0 --- /dev/null +++ b/docs/downloads/training/GoogleAuth.zip diff --git a/docs/html/google/auth/api-client.jd b/docs/html/google/auth/api-client.jd new file mode 100644 index 0000000..fda3310 --- /dev/null +++ b/docs/html/google/auth/api-client.jd @@ -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; + + @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(); + ... + } + + @Override + public void onConnected(Bundle connectionHint) { + // Connected to Google Play services! + // The good stuff goes here. + } + + @Override + public void onConnectionSuspended(int cause) { + // The connection has been interrupted. + // Disable any UI components that depend on Google APIs + // until onConnected() is called. + } + + @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> + @Override + protected void onStart() { + super.onStart(); + if (!mResolvingError) { // more about this later + mGoogleApiClient.connect(); + } + } + + @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; + + ... + + @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() { } + + @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); + } + + @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> +@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"; + +@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> +@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<DriveApi.MetadataBufferResult>() { + @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 index 0000000..3b2a83f --- /dev/null +++ b/docs/html/google/auth/http-auth.jd @@ -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 & 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 <keystore_alias> -keystore <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> + +@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—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. + */ + @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() { + @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> +@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> + + + diff --git a/docs/html/google/google_toc.cs b/docs/html/google/google_toc.cs index 963cfc1..6ff00c0 100644 --- a/docs/html/google/google_toc.cs +++ b/docs/html/google/google_toc.cs @@ -65,12 +65,6 @@ <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"> @@ -81,6 +75,18 @@ <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"> diff --git a/docs/html/google/play-services/setup.jd b/docs/html/google/play-services/setup.jd index fb5daf8..3137890 100644 --- a/docs/html/google/play-services/setup.jd +++ b/docs/html/google/play-services/setup.jd @@ -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 Binary files differnew file mode 100644 index 0000000..6107641 --- /dev/null +++ b/docs/html/images/google/GoogleApiClient.png diff --git a/docs/html/images/google/GoogleApiClient@2x.png b/docs/html/images/google/GoogleApiClient@2x.png Binary files differnew file mode 100644 index 0000000..a98bc2c --- /dev/null +++ b/docs/html/images/google/GoogleApiClient@2x.png |