• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1page.title=Authorizing with Google for REST APIs
2page.tags="oauth 2.0","GoogleAuthUtil"
3
4trainingnavtop=true
5startpage=true
6
7@jd:body
8
9
10<div id="qv-wrapper">
11  <div id="qv">
12
13<h2>In this document</h2>
14<ol>
15  <li><a href="#Register">Register Your App</a></li>
16  <li><a href="#AccountPicker">Invoke the Account Picker</a></li>
17  <li><a href="#AccountName">Retrieve the Account Name</a></li>
18  <li><a href="#ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</a></li>
19  <li><a href="#HandleExceptions">Handle Exceptions</a></li>
20</ol>
21<h2>Try it out</h2>
22
23<div class="download-box">
24<a href="http://developer.android.com/shareables/training/GoogleAuth.zip"
25  class="button">Download the sample app</a>
26<p class="filename">GoogleAuth.zip</p>
27</div>
28
29</div>
30</div>
31
32<p>When you want your Android app to access Google APIs using the user's Google account over
33HTTP, the <a href=
34"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
35class and related APIs provide your users a secure and consistent experience for picking an
36account and retrieving an OAuth 2.0 token for your app.</p>
37
38<p>You can then use that token in your HTTP-based communications with Google API services
39that are not included in the <a href="{@docRoot}google/play-services/index.html">Google Play
40services</a> library, such as the Blogger or Translate APIs.</p>
41
42<p class="note"><strong>Note:</strong> An OAuth 2.0 token using <a href=
43"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
44is required only for certain types of Google
45APIs that you need to access over HTTP. If you're instead using the <a
46href="{@docRoot}google/play-services/index.html">Google Play services library</a> to access Google
47APIs such as <a href="{@docRoot}google/play-services/plus.html">Google+</a> or <a
48href="{@docRoot}google/play-services/games.html">Play Games</a>, you don't need an OAuth 2.0
49token and you can instead access these services using the {@code GoogleApiClient}. For more
50information, read <a href="{@docRoot}google/auth/api-client.html">Accessing Google Play
51Services APIs</a>.</p>
52
53<p>To get started with <a href=
54"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html">{@code GoogleAuthUtil}</a>
55for accessing Google's REST APIs, you must set up your Android app project with the Google Play
56services library. Follow the procedures in <a href=
57"{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p>
58
59
60
61
62<h2 id="Register">Register Your App</h2>
63
64<p>Before you can publish an app that retrieves an OAuth 2.0 token for Google REST APIs,
65you must register your Android app with the Google Cloud Console by providing your app's
66package name and the SHA1 fingerprint of the keystore with which you sign your release APK.</p>
67
68<p class="caution"><strong>Caution:</strong> While you are testing an APK that's <a
69href="{@docRoot}tools/publishing/app-signing.html#debugmode">signed with a
70debug key</a>, Google does not require that your app be registered in Google Cloud Console. However,
71your app must be registered in Google Cloud Console in order to continue working once it is
72<a href="{@docRoot}tools/publishing/app-signing.html#releasemode">signed
73with a release key</a>.</p>
74
75<p>To register your Android app with Google Cloud Console:</p>
76
77<ol>
78<li>Visit <a href="https://cloud.google.com/console" class="external-link" target="_blank"
79>Google Cloud Console</a>.
80<li>If you have an existing project to which you're adding an Android app, select the project.
81Otherwise, click <strong>Create project</strong> at the top, enter your project name and ID,
82then click <strong>Create</strong>.
83<p class="note"><strong>Note:</strong> The name you provide for the project is the name that
84appears to users in the Google Settings app in the list of <em>Connected apps</em>.</p>
85<li>In the left-side navigation, select <strong>APIs &amp; auth</strong>.
86<li>Enable the API you'd like to use by setting the Status to <strong>ON</strong>.
87
88<li>In the left-side navigation, select <strong>Credentials</strong>.
89<li>Click <strong>Create new client ID</strong> or <strong>Create new key</strong>
90as appropriate for your app.</li>
91<li>Complete the form that appears by filling in your Android app details.
92<p>To get the SHA1 fingerprint for your app, run the following command in a terminal:
93<pre class="no-pretty-print">
94keytool -exportcert -alias &lt;keystore_alias> -keystore &lt;keystore_path> -list -v
95</pre>
96<p>For example, you're using a debug-key with Eclipse, then the command looks like this:</p>
97<pre class="no-pretty-print">
98keytool -exportcert -alias androiddebugkey-keystore ~/.android/debug.keystore -list -v
99</pre>
100<p>Then the keystore password is "android".</p>
101</li>
102<li>Click <strong>Create</strong>.
103</ol>
104
105<p>The Credentials page then provides the available credentials such as an OAuth 2.0 client ID and
106an Android Key, but you don't need these to authorize your Android users. Simply registering your
107app with the package name and SHA1 makes the Google services accessible by your app.
108
109
110<p>To acquire the OAuth 2.0 token that will grant you access to Google APIs over HTTP, you need to
111first identify the user's Google account with which you'll query the servers. For this task, the
112Google Play services library provides a convenient account picker dialog you can invoke using
113<a href="{@docRoot}reference/com/google/android/gms/common/AccountPicker.html">{@code
114AccountPicker}</a>. The result delivered to your activity from the account picker is the account
115name you'll use to request the OAuth 2.0 token in the next lesson.</p>
116
117<p class="note"><strong>Note:</strong> In order to use the APIs discussed here, you must
118include the Google Play services library with your project. If you haven't set up your project
119with the library yet, read the guide to <a
120href="{@docRoot}google/play-services/setup.html">Setup Google Play Services SDK</a>.</p>
121
122
123
124<h2 id="AccountPicker">Invoke the Account Picker</h2>
125
126<p>To open the account picker dialog that's managed by the Google Play services library, call
127{@link android.app.Activity#startActivityForResult startActivityForResult()} using an {@link
128android.content.Intent} returned by <a href=
129"{@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)">
130{@code AccountPicker.newChooseAccountIntent}</a>.</p>
131
132
133<p>For example:</p>
134<pre>
135static final int REQUEST_CODE_PICK_ACCOUNT = 1000;
136
137private void pickUserAccount() {
138    String[] accountTypes = new String[]{"com.google"};
139    Intent intent = AccountPicker.newChooseAccountIntent(null, null,
140            accountTypes, false, null, null, null, null);
141    startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT);
142}
143</pre>
144
145<p>When this code executes, a dialog appears for the user to pick an account. When the user
146selects the account, your activity receives the result in the {@link
147android.app.Activity#onActivityResult onActivityResult()} callback.</p>
148
149<p>Most apps should pass the <a href=
150"{@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)">
151{@code newChooseAccountIntent()}</a> method the same arguments shown in the above example,
152which indicate that:</p>
153
154
155<ul>
156<li>There is no currently selected account.</li>
157<li>There is no restricted list of accounts.</li>
158<li>The dialog should list only accounts from the "com.google" domain.</li>
159<li>Don't prompt the user to pick an account if there's only one available account (just use that
160one). However, even if only one account currently exists, the dialog may include an option for the
161user to add a new account.</li>
162<li>There is no custom title for the dialog.</li>
163<li>There is no specific auth token type required.</li>
164<li>There are no restrictions based on account features.</li>
165<li>There are no authenticator-specific options.</li>
166</ul>
167
168<p>For more details about these arguments, see the <a href=
169"{@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)">
170{@code newChooseAccountIntent()}</a> method documentation.</p>
171
172
173
174
175<h2 id="AccountName">Retrieve the Account Name</h2>
176
177<p>Once the user selects an account, your activity receives a call to its
178{@link android.app.Activity#onActivityResult onActivityResult()} method. The received
179{@link android.content.Intent} includes an extra for
180{@link android.accounts.AccountManager#KEY_ACCOUNT_NAME}, specifying the account name
181(an email address) you must use to acquire the OAuth 2.0 token.</p>
182
183<p>Here's an example implementation of the callback {@link android.app.Activity#onActivityResult
184onActivityResult()} that receives the selected account:</p>
185
186<pre>
187String mEmail; // Received from <a href=
188"{@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)"
189>{@code newChooseAccountIntent()}</a>; passed to <a href=
190"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a>
191
192&#64;Override
193protected void onActivityResult(int requestCode, int resultCode, Intent data) {
194    if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
195        // Receiving a result from the AccountPicker
196        if (resultCode == RESULT_OK) {
197            mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
198            // With the account name acquired, go get the auth token
199            getUsername();
200        } else if (resultCode == RESULT_CANCELED) {
201            // The account picker dialog closed without selecting an account.
202            // Notify users that they must pick an account to proceed.
203            Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show();
204        }
205    }
206    // Later, more code will go here to handle the result from some exceptions...
207}
208</pre>
209
210<p>You can now pass the account name held by {@code mEmail} to <a href=
211"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
212{@code GoogleAuthUtil.getToken()}</a> (which is what the {@code getUsername()} method
213does), but because it performs network transactions, this method should not be called from the
214UI thread. The next lesson shows how to create an {@link android.os.AsyncTask} to get the auth token
215on a separate thread.</p>
216
217
218<p>Once you have retrieved the account name for the user's Google account, you can call <a href=
219"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
220{@code GoogleAuthUtil.getToken()}</a>, which returns the access token string required by Google API
221services.</p>
222
223
224<p>Calling this method is generally a straightforward procedure, but you must be
225aware that:</p>
226<ul>
227<li>The <a href=
228"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
229{@code GoogleAuthUtil.getToken()}</a> method requires a network connection, so your app must
230acquire the {@link android.Manifest.permission#INTERNET} permission. You should also check whether
231the device has a network connection at runtime by querying {@link android.net.NetworkInfo}, which
232requires that your app also acquire the {@link android.Manifest.permission#ACCESS_NETWORK_STATE}
233permissions&mdash;for more details, read <a href=
234"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a>.</li>
235<li>Because the <a href=
236"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
237{@code GoogleAuthUtil.getToken()}</a> method performs a synchronous network transaction, you should
238always perform this call from a worker thread to avoid blocking your app's UI thread.</li>
239<li>As is true when performing any network transaction, you should be prepared to handle
240exceptions that may occur. There are also specific exceptions that
241<a href=
242"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
243{@code GoogleAuthUtil.getToken()}</a> may throw, defined as <a href=
244"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
245GoogleAuthException}</a> objects.</li>
246</ul>
247
248<p>This lesson shows how you can gracefully handle these concerns by performing authentication in
249an {@link android.os.AsyncTask} and providing users with the appropriate information and available
250actions during known exceptions.</p>
251
252<p class="note"><strong>Note:</strong> The code shown in this lesson, using <a href=
253"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code GoogleAuthUtil.getToken()}</a>,
254is appropriate when you will be requesting the OAuth token from an {@link android.app.Activity}.
255However, if you need to request the OAuth token from a {@link android.app.Service}, then you
256should instead use <a
257href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code
258getTokenWithNotification()}</a>. This method works the same as <a href=
259"{@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
260also creates an appropriate
261<a href="{@docRoot}guide/topics/ui/notifiers/notifications.html">notification</a>
262that allows the user can recover from the error.
263The sample available for download above includes code showing how to use this method instead.</p>
264
265
266<h2 id="ExtendAsyncTask">Extend AsyncTask to Get the Auth Token</h2>
267
268<p>The {@link android.os.AsyncTask} class provides a simple way to create a worker thread for jobs
269that should not run on your UI thread. This lesson focuses on how to create such a thread
270to get your auth token; for a more complete discussion about {@link android.os.AsyncTask},
271read <a href="{@docRoot}training/articles/perf-anr.html">Keeping Your
272App Responsive</a> and the {@link android.os.AsyncTask} class reference.</p>
273
274
275<p>The {@link android.os.AsyncTask#doInBackground doInBackground()} method in your {@link
276android.os.AsyncTask} class is where you should call the <a href=
277"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
278{@code GoogleAuthUtil.getToken()}</a> method. You can also use it to catch some of the generic
279exceptions that may occur during your network transactions.</p>
280
281<p>For example, here's part of an {@link android.os.AsyncTask} subclass that calls <a href=
282"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
283{@code GoogleAuthUtil.getToken()}</a>:</p>
284
285<pre>
286public class GetUsernameTask extends AsyncTask<Void, Void, Void>{
287    Activity mActivity;
288    String mScope;
289    String mEmail;
290
291    GetUsernameTask(Activity activity, String name, String scope) {
292        this.mActivity = activity;
293        this.mScope = scope;
294        this.mEmail = name;
295    }
296
297    /**
298     * Executes the asynchronous job. This runs when you call execute()
299     * on the AsyncTask instance.
300     */
301    &#64;Override
302    protected Void doInBackground(Void... params) {
303        try {
304            String token = fetchToken();
305            if (token != null) {
306                // <b>Insert the good stuff here.</b>
307                // Use the token to access the user's Google data.
308                ...
309            }
310        } catch (IOException e) {
311            // The fetchToken() method handles Google-specific exceptions,
312            // so this indicates something went wrong at a higher level.
313            // TIP: Check for network connectivity before starting the AsyncTask.
314            ...
315        }
316        return null;
317    }
318
319    /**
320     * Gets an authentication token from Google and handles any
321     * GoogleAuthException that may occur.
322     */
323    protected String fetchToken() throws IOException {
324        try {
325            <b>return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);</b>
326        } catch (UserRecoverableAuthException userRecoverableException) {
327            // GooglePlayServices.apk is either old, disabled, or not present
328            // so we need to show the user some UI in the activity to recover.
329            mActivity.handleException(userRecoverableException);
330        } catch (GoogleAuthException fatalException) {
331            // Some other type of unrecoverable exception has occurred.
332            // Report and log the error as appropriate for your app.
333            ...
334        }
335        return null;
336    }
337    ...
338}
339</pre>
340
341<p>In order to call <a href=
342"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
343{@code GoogleAuthUtil.getToken()}</a>, you must provide the app {@link android.content.Context},
344the account name retrieved from the account picker, and the scope for your auth
345token request. The above sample code (and the attached sample) defines these
346arguments with class members that the host activity passes to the {@link android.os.AsyncTask} class constructor. For more information about setting the scope, see
347the <a href="#SpecifyingScopes">Specifying Scopes</a> section below. </p>
348
349<p class="note"><strong>Note:</strong>
350As shown by the {@code fetchToken()} method above, you must handle
351special exceptions that may occur during the <a href=
352"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
353{@code GoogleAuthUtil.getToken()}</a> method. The next section shows how you should
354respond to these exceptions.</p>
355
356<p>Once you have an {@link android.os.AsyncTask} subclass defined,
357you can instantiate and execute an instance after you get the user's
358account name from the account picker.
359For example, back in the {@link android.app.Activity} class you can do something like this:</p>
360
361<pre>
362String mEmail; // Received from <a href=
363"{@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)"
364>{@code newChooseAccountIntent()}</a>; passed to <a href=
365"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">{@code getToken()}</a>
366private static final String SCOPE =
367        "oauth2:https://www.googleapis.com/auth/userinfo.profile";
368
369/**
370 * Attempts to retrieve the username.
371 * If the account is not yet known, invoke the picker. Once the account is known,
372 * start an instance of the AsyncTask to get the auth token and do work with it.
373 */
374private void getUsername() {
375    if (mEmail == null) {
376        pickUserAccount();
377    } else {
378        if (isDeviceOnline()) {
379            <b>new GetUsernameTask(HelloActivity.this, mEmail, SCOPE).execute();</b>
380        } else {
381            Toast.makeText(this, R.string.not_online, Toast.LENGTH_LONG).show();
382        }
383    }
384}
385</pre>
386
387<p>The {@code pickUserAccount()} method is shown in the first lesson, <a
388href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>.
389
390<p>For information about how to check whether the device is currently online (as performed by
391the {@code isDeviceOnline()} method above), see the attached sample app or the
392<a href=
393"{@docRoot}training/basics/network-ops/connecting.html">Connecting to the Network</a> lesson.</p>
394
395<p>The only part left is how you should handle the exceptions that may occur when you call
396<a href=
397"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
398{@code GoogleAuthUtil.getToken()}</a>.</p>
399
400<h3 id="SpecifyingScopes">Specifying scopes</h3>
401<p>The scope string is used to specify which Google services can be accessed by
402  an app using the requested auth token. An auth token can be associated with
403  multiple scopes.</p>
404<p>When specifying the scopes in your auth token request, prefix the
405  scope string with {@code "oauth2:"} followed by a list of one or more OAuth scope
406  values. Use a space to separate each scope value in the list. To see a list of
407  valid OAuth scope values for Google services, browse
408  the <a href="https://developers.google.com/oauthplayground/"
409  class="external-link">OAuth 2.0 Playground</a>.</p>
410<p class="note"><strong>Tip:</strong> Specify {@code "oauth2:&lt;scope&gt;"}
411  for a single scope. Specify
412  {@code "oauth2:&lt;scope1&gt; &lt;scope2&gt; &lt;scopeN&gt;"} for multiple
413  scopes (using a space to separate each scope).</p>
414<p>For example, to access the Google Books API, the scope is
415  {@code "oauth2:https://www.googleapis.com/auth/books"}. To add an additional
416  scope, say for Google+ login, your code might look like this:</p>
417<pre>
418private final static String BOOKS_API_SCOPE
419        = "https://www.googleapis.com/auth/books";
420private fina; static String GPLUS_SCOPE
421        = "https://www.googleapis.com/auth/plus.login";
422private final static String mScopes
423        = "oauth2:" + BOOKS_API_SCOPE + " " + GPLUS_SCOPE;
424String token = GoogleAuthUtil.getToken(mActivity, mEmail, mScopes);
425</pre>
426
427<h2 id="HandleExceptions">Handle Exceptions</h2>
428
429<p>As shown in the <code>fetchToken()</code> method above, you must catch all occurrences of <a href=
430"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
431GoogleAuthException}</a> when you call <a href=
432"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getToken(android.content.Context,%20java.lang.String,%20java.lang.String)">
433{@code GoogleAuthUtil.getToken()}</a>.</p>
434
435<p>To provide users information and a proper solution to issues that may occur while acquiring the
436auth token, it's important that you properly handle the following subclasses of <a href=
437"{@docRoot}reference/com/google/android/gms/auth/GoogleAuthException.html">{@code
438GoogleAuthException}</a>:</p>
439
440
441<dl>
442<dt><a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code UserRecoverableAuthException}</a></dt>
443  <dd>This is an error that users can resolve through some verification. For example, users may
444  need to confirm that your app is allowed to access their Google data or they may need to re-enter
445  their account password. When you receive this exception, call <a href=
446"{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html#getIntent()">{@code
447getIntent()}</a> on the instance and pass the returned {@link android.content.Intent} to {@link
448android.app.Activity#startActivityForResult startActivityForResult()} to give users the opportunity
449to solve the problem, such as by logging in.</dd>
450
451<dt><a href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html">{@code GooglePlayServicesAvailabilityException}</a></dt>
452    <dd>This is a specific type of <a
453    href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
454    UserRecoverableAuthException}</a> indicating that the user's current version
455of Google Play services is outdated. Although the recommendation above for
456<a href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
457    UserRecoverableAuthException}</a> also works for this exception, calling {@link
458android.app.Activity#startActivityForResult startActivityForResult()} will immediately send users
459 to Google Play Store to install an update, which may be confusing. So you should instead call <a
460 href="{@docRoot}reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html#getConnectionStatusCode()">
461{@code getConnectionStatusCode()}</a> and pass the result to <a href=
462"{@docRoot}reference/com/google/android/gms/common/GooglePlayServicesUtil.html#getErrorDialog(int,%20android.app.Activity,%20int,%20android.content.DialogInterface.OnCancelListener)">
463{@code GooglePlayServicesUtil.getErrorDialog()}</a>. This returns a {@link android.app.Dialog}
464that includes an appropriate message and a button to take users to Google Play Store so they
465can install an update.</dd>
466</dl>
467
468<p>For example, the <code>fetchToken()</code> method in the above sample code catches any
469occurrence of <a
470href="{@docRoot}reference/com/google/android/gms/auth/UserRecoverableAuthException.html">{@code
471UserRecoverableAuthException}</a> and passes it back to the activity with a method called
472{@code handleException()}. Here's what that method in the activity may look like:</p>
473
474
475<pre>
476static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001;
477
478/**
479 * This method is a hook for background threads and async tasks that need to
480 * provide the user a response UI when an exception occurs.
481 */
482public void handleException(final Exception e) {
483    // Because this call comes from the AsyncTask, we must ensure that the following
484    // code instead executes on the UI thread.
485    runOnUiThread(new Runnable() {
486        &#64;Override
487        public void run() {
488            if (e instanceof GooglePlayServicesAvailabilityException) {
489                // The Google Play services APK is old, disabled, or not present.
490                // Show a dialog created by Google Play services that allows
491                // the user to update the APK
492                int statusCode = ((GooglePlayServicesAvailabilityException)e)
493                        .getConnectionStatusCode();
494                Dialog dialog = GooglePlayServicesUtil.getErrorDialog(statusCode,
495                        HelloActivity.this,
496                        REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR);
497                dialog.show();
498            } else if (e instanceof UserRecoverableAuthException) {
499                // Unable to authenticate, such as when the user has not yet granted
500                // the app access to the account, but the user can fix this.
501                // Forward the user to an activity in Google Play services.
502                Intent intent = ((UserRecoverableAuthException)e).getIntent();
503                startActivityForResult(intent,
504                        REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR);
505            }
506        }
507    });
508}
509</pre>
510
511<p>Notice that in both cases, the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR}
512request code is passed with the request to handle the exception with a dialog or activity.
513This way, when the user completes the appropriate action to resolve the exception,
514your {@link android.app.Activity#onActivityResult onActivityResult()} method receives an
515intent that includes this request code and you can try to acquire the auth
516token again.</p>
517
518
519<p>For example, the following code is a complete implementation of {@link
520android.app.Activity#onActivityResult onActivityResult()} that handles results for
521both the {@code REQUEST_CODE_PICK_ACCOUNT} action (shown in the previous lesson, <a
522href="{@docRoot}training/auth-google/picking-account.html">Picking the User's Account</a>)
523and the {@code REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR} action, which occurs after the user
524completes one of the actions above to resolve an exception.</p>
525
526
527<pre>
528&#64;Override
529protected void onActivityResult(int requestCode, int resultCode, Intent data) {
530    if (requestCode == REQUEST_CODE_PICK_ACCOUNT) {
531        // Receiving a result from the AccountPicker
532        if (resultCode == RESULT_OK) {
533            mEmail = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
534            // With the account name acquired, go get the auth token
535            getUsername();
536        } else if (resultCode == RESULT_CANCELED) {
537            // The account picker dialog closed without selecting an account.
538            // Notify users that they must pick an account to proceed.
539            Toast.makeText(this, R.string.pick_account, Toast.LENGTH_SHORT).show();
540        }
541    } else if ((requestCode == REQUEST_CODE_RECOVER_FROM_AUTH_ERROR ||
542            requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR)
543            && resultCode == RESULT_OK) {
544        // Receiving a result that follows a GoogleAuthException, try auth again
545        getUsername();
546    }
547}
548</pre>
549
550<p>For a complete set of code that acquires the OAuth token and queries a Google service
551over HTTP (including how to use <a
552href="{@docRoot}reference/com/google/android/gms/auth/GoogleAuthUtil.html#getTokenWithNotification(android.content.Context, java.lang.String, java.lang.String, android.os.Bundle)">{@code
553getTokenWithNotification()}</a> when you need to acquire the token from
554a {@link android.app.Service}), see the sample app available for download at the top
555of this page.</p>
556
557
558
559