People API Android Tutorial – Part 2

Let’s look at using the People API in our Android app. We will see how it allows us to pull data from contacts, as well as data from their G+ profiles. All through the People API alone.
Recap
Part 1 covered configuring a project via the Google API Console. We created credentials for Android and Web client. Next, we enabled Play Services for our project and downloaded a configuration file for the same.
In part 2, we’ll look at its implementation.
Preparing your App
Start by adding these to your Android app’s Gradle dependencies:
repositories { mavenCentral() } dependencies { ... compile 'com.google.android.gms:play-services-plus:8.4.0' compile 'com.google.android.gms:play-services-auth:8.4.0' compile 'com.google.api-client:google-api-client:1.21.0' // People API compile 'com.google.apis:google-api-services-people:v1-rev2-1.21.0' }
Don’t forget to add an Internet permission in your AndroidManifest.xml file.
<uses-permission android:name="android.permission.INTERNET" />
Client ID and Secret
Take a look at your Web Client credentials in your Google API Console. You will notice a Client ID and Client Secret. Keep in mind we need these from the Web client, not Android.
For the sake of this tutorial and complexity, I will simply add the Web Client’s Client ID and Secret to my strings.xml file.
Feel free to do the same, but remember that google-services.json file you downloaded in Part 1? Preferably, you must to add the file to your Android Studio project. Then JSON parse that file contents and fetch your keys from that.
Adding Google Sign in
Next, we’ll add a Google Sign in. By signing in, we can obtain a valid access token from our registered Google account. In turn, this token will help us access the People API.
Start by adding the Sign in button widget in your XML layout:
<com.google.android.gms.common.SignInButton android:id="@+id/main_googlesigninbtn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" app:buttonSize="wide" />
When we perform a sign in, we need to tell Play Services, what APIs and Scopes we’re requesting.
GoogleSignInOptions
Notice the Scopes I’m requesting here. With the People API, for the sake of demonstration I’m happy with two.
GoogleSignInOptions signInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) // The serverClientId is an OAuth 2.0 web client ID .requestServerAuthCode(getString(R.string.clientID)) .requestEmail() .requestScopes(new Scope(Scopes.PLUS_LOGIN), new Scope(PeopleScopes.CONTACTS_READONLY), new Scope(PeopleScopes.USER_PHONENUMBERS_READ)) .build();
GoogleApiClient
Next we build an instance of the GoogleApiClient. This helps handling whether Play Services exists on our app and handles it accordingly.
// To connect with Google Play Services and Sign In mGoogleApiClient = new GoogleApiClient.Builder(this) .enableAutoManage(this, this) .addOnConnectionFailedListener(this) .addConnectionCallbacks(this) .addApi(Auth.GOOGLE_SIGN_IN_API, signInOptions) .build();
While you’re at it, you can implement the GoogleApiClient.OnConnectionFailedListener and GoogleApiClient.ConnectionCallbacks interfaces. They’re pretty handy!
We won’t take any chances with whether the needed APIs are available or not. So let’s add a safety check if the connection fails. This code snippet fetches the error message and displays it in a dialog.
@Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { GoogleApiAvailability mGoogleApiAvailability = GoogleApiAvailability.getInstance(); Dialog dialog = mGoogleApiAvailability.getErrorDialog(this, connectionResult.getErrorCode(), RC_API_CHECK); dialog.show(); }
Enable your API Client connection in the onStart() method.
@Override protected void onStart() { super.onStart(); mGoogleApiClient.connect(); }
Google Sign-in Click Listener
Lastly, we define the on-click action for our Google Sign In button.
@Override public void onClick(View v) { switch (v.getId()) { case R.id.main_googlesigninbtn: getIdToken(); break; } } private void getIdToken() { // Shows an account picker to let the user choose a Google account from the device. // If the GoogleSignInOptions only asks for IDToken and/or profile and/or email then no // consent screen will be shown here. Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); startActivityForResult(signInIntent, RC_INTENT); }
Note RC_INTENT here. It’s simply a request code, used to recognize the results returned from the sign-in. We can handle this in onActivityResult().
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case RC_INTENT: GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); if (result.isSuccess()) { GoogleSignInAccount acct = result.getSignInAccount(); Log.d(TAG, "onActivityResult:GET_TOKEN:success:" + result.getStatus().isSuccess()); // This is what we need to exchange with the server. new PeoplesAsync().execute(acct.getServerAuthCode()); } else { Log.d(TAG, result.getStatus().toString() + "\nmsg: " + result.getStatus().getStatusMessage()); } break; } }
We can verify the success of the sign-in using GoogleSignInResult. Use the result to fetch the server auth code.
We now have what we need to authenticate our calls to the People API.
Finally, People API
I can’t believe it took us one and a half posts to get to the actual thing! So without wasting time, let’s start implementing this.
Here’s 3 things we need to do:
- Exchange the server auth code we got for a token response
- Create a credential using the response
- Use the credentials to obtain an instance of People API
Simple? The code snippet below demonstrates this.
public static People setUp(Context context, String serverAuthCode) throws IOException { HttpTransport httpTransport = new NetHttpTransport(); JacksonFactory jsonFactory = JacksonFactory.getDefaultInstance(); // Redirect URL for web based applications. // Can be empty too. String redirectUrl = "urn:ietf:wg:oauth:2.0:oob"; // STEP 1 GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest( httpTransport, jsonFactory, context.getString(R.string.clientID), context.getString(R.string.clientSecret), serverAuthCode, redirectUrl).execute(); // STEP 2 GoogleCredential credential = new GoogleCredential.Builder() .setClientSecrets(context.getString(R.string.clientID), context.getString(R.string.clientSecret)) .setTransport(httpTransport) .setJsonFactory(jsonFactory) .build(); credential.setFromTokenResponse(tokenResponse); // STEP 3 return new People.Builder(httpTransport, jsonFactory, credential) .setApplicationName(APPLICATION_NAME) .build(); }
Pass in the server auth code you obtained from the sign-in result in the previous step. Again, notice how I’m referencing my Client ID and Client Secret here. They are simply strings. But you must JSON parse them from the google-services.json file.
NOTE:
Use the Client ID and Secret of your Web client and NOT Android Client!
Calling it in
With the above setUp() method done, we just need to call it.
People peopleService = setUp(MainActivity.this, serverAuthCode); ListConnectionsResponse response = peopleService.people().connections() .list("people/me") .setRequestMaskIncludeField("person.names,person.emailAddresses,person.phoneNumbers") .execute(); List<Person> connections = response.getConnections();
We’re pretty much done! We now have a list of our connections, and we get get a lot of information from that.
You would have noticed I requested Scopes for contacts and their phone numbers during Sign-in. So let’s see an example for fetching phone numbers.
NOTE:
Use must the setRequestMaskIncludeField() method to specify which data fields you want to request. Omitting this ‘ideally’ means the API will return ALL data sets under the sun. But that doesn’t happen, and is a known issue. Check this SO post for more.
Here’s the full list of People API scopes.
Once you fetch the connections into a list, simply iterate through it to fetch data.
for (Person person : connections) { if (!person.isEmpty()) { List<PhoneNumber> phoneNumbers = person.getPhoneNumbers(); if (phoneNumbers != null) for (PhoneNumber phoneNumber : phoneNumbers) Log.d(TAG, "phone: " + phoneNumber.getValue());
Hope the code there is self-explanatory. Fetching the phone numbers from a person, is yet another list. So we simply iterate down another level to fetch the phone numbers. Simple as that. If you want the email IDs, it is person.getEmailAddresses()
.
You’d be surprised to know the rich data that the People API allows you to access. Here’s the complete list for your reference.
Just keep in mind good UX and wrap all these calls in an AsyncTask. Show a progress indicator if it takes too long to load.
Final Output
Here’s a little demo of how the API works.
Ideally upon first launch, the app will display an account picker and then request permissions. Of course I’m only displaying the name from the People API. But I’m printing the rest to the logs.
You can check out the source code for this on GitHub.
Wrapping it up
That’s it for this two-part post on the People API. Hope you’ve got a good feel about how powerful the API can be. In fact Google plans to completely replace the Contacts API with People API in the near future. So now’s a really good time to migrate!
Plus, you can even query one’s Google+ related data from this API. While configuration might seem to be a handful, the long-term usefulness of the API far outweighs it.
So what do you think? Know anything important I might have missed? Any cool features it has, that I’m not aware of? Drop ’em in the comments below!
Please help me, Every time i am getting error as follow
400 Bad Request
{
“error” : “unsupported_grant_type”,
“error_description” : “Invalid grant_type: ”
}
Thank you so much, I spent a lot of time and finally I found it!! 😀
Is there a Kotlin code for this ??
Unfortunately no. But Android Studio should allow you to easily convert to Kotlin!
Thanks.
This is the only one that worked.
All the other methods I found through various posts were outdated, due to the deprecation of Google Plus API I tried for days.
Note to somebody else trying. If you want to integrate this in an existing app you must make it work its not just copying and pasting.
Thanks again
Hey Richard,
I’m glad that it worked for you!
return new People.Builder(httpTransport, jsonFactory, credential)
.setApplicationName(APPLICATION_NAME)
.build();
is dont work any more.
@disqus_TPD8mpYmRO:disqus i am also getting same error. token response is null. i donno why. searching for verly long time. unable to find. if anyone knows the solution please tell immediately. Thanks in advance
Caused by: java.lang.NullPointerException
com.google.api.client.repackaged.com.google.common.base.Preconditions.checkNotNull
GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(
httpTransport,
jsonFactory,
context.getString(R.string.clientID),
context.getString(R.string.clientSecret),
serverAuthCode,
redirectUrl).execute();
How to get client sceret key? Please help
Thanks for this nice tutorial. I have an issue with the signed apk, missing required parameter:code, what is that meant?
I alwast got null for the response.getConnections(); in Asyntask, what reason causing the null response connections ?