Jan 22, 2013

Android Google Play Service : Authorization

Google Play Service, the most promising release for me. I wish google play service will get accepted as a regular practice by developers and users in app for authentication. 

Google Play Service has three major APIs
  1. Authorization 
  2. Google+
  3. Google Maps
This tutorial contains explanation of Authorization API. It's standard way to authorize your user, It leverage existing Google Play Accounts. 

This demo app has four buttons 
  1. isGooglePlayServiceAvailable() - To check availability of google play service.
  2. Get Account Name - To allow user to select gmail account (popup will display only if user has configured multiple gmail accounts)
  3. getToken() - To fetch authorization token.
  4. invalidateToken() - To invalidate token.
Read following link to know "How Google Play Service works?"
http://developer.android.com/google/play-services/index.html  

 

 

 

Flow of Google Play Service : Authorization API



Above flow chart illustrates google play service authorization process, It has four major steps.
  1. Check Google Play Service available
  2. Select Gmail Account
  3. Ask for Permission
  4. Get Token
When you call GoogleAuthUtil.getToken(), for the first time, it throws UserRecoverableAuthException with one intent, you need to start activity with that intent to ask permission from user.

"UserRecoverableAuthException: This exception is thrown when an error occurs that users can resolve, such as not yet granting access to their accounts or if they changed their password. This exception class contains a getIntent() method that you can call to obtain an intent that you can use withstartActivityForResult() to obtain the user's resolution. You will need to handle theonActivityResult() callback when this activity returns to take action based on the user's actions."

I can't figure out reason why Google Play Service throws exception for first time to ask permission,There should be methods to check permission and grant permission

Step 1 : Download Google Play Service  

Google Play Service is not a part of Android SDK so you need to download library using Android SDK Manager. Read this like for more information http://developer.android.com/google/play-services/setup.html

Step 2 : Import Google Play Service library project from <Android SDK>/extras/google/google_play_services copy/libproject/google-play-services_lib

Step 3: Create Android project named "GooglePlayService" (Don't confuse with name It's project name)

Step 4 : Add Reference of Google Play Service Library in our project
Right click on project name -> select properties -> select Android -> Click on Add button -> select google play service libproject



Step 5 : make gui as display in screen shots, open activity_main.xml from layout
below layout has four buttons (namely btnCheckService, btnAccountName, btGetToken, btnInvalidate) for different actions and two textview to display gmail account and token.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/btnCheckService"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:onClick="buttonClicked"
        android:text=" isGooglePlayServicesAvailable()" />

    <Button
        android:id="@+id/btnAccountNames"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/btnCheckService"
        android:onClick="buttonClicked"
        android:text="Get Account Names" />

    <TextView
        android:id="@+id/txtAccount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/btnAccountNames"
        android:text="Selected Account"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btnGetToken"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/txtAccount"
        android:onClick="buttonClicked"
        android:text="getToken" />

    <TextView
        android:id="@+id/txtToken"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/btnGetToken"
        android:text="Token"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/btnInvalidate"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/txtToken"
        android:onClick="buttonClicked"
        android:text="Invalidate Token" />

</RelativeLayout>

Step 6 : Edit MainActivity.java
package com.kpbird.googleplayservice;

import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.auth.GoogleAuthUtil;
import com.google.android.gms.auth.UserRecoverableAuthException;
import com.google.android.gms.common.AccountPicker;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;

public class MainActivity extends Activity {

 private TextView txtAccount;
 private TextView txtToken;
 private static final int USER_RECOVERABLE_AUTH = 5;
 private static final int ACCOUNT_PICKER = 2;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  txtAccount = (TextView) findViewById(R.id.txtAccount);
  txtToken = (TextView) findViewById(R.id.txtToken);
 }

 public void buttonClicked(View v) {
  if (v.getId() == R.id.btnCheckService) {
   checkStatus();
  } else if (v.getId() == R.id.btnAccountNames) {
   getAccountNames();
  } else if (v.getId() == R.id.btnGetToken) {
   new GetAuthToken(this, txtAccount.getText().toString()).execute();
  } else if(v.getId() == R.id.btnInvalidate){
   GoogleAuthUtil.invalidateToken(this, txtToken.getText().toString());
  }
  
 }

 public void checkStatus() {
  int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
  switch (status) {
  case ConnectionResult.SUCCESS:
   Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show();
   break;
  case ConnectionResult.SERVICE_MISSING:
   Toast.makeText(this, "Service Missing", Toast.LENGTH_SHORT).show();
   break;
  case ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED:
   Toast.makeText(this, "Service Version Update Required",
     Toast.LENGTH_SHORT).show();
   break;
  case ConnectionResult.SERVICE_DISABLED:
   Toast.makeText(this, "Service Disabled", Toast.LENGTH_SHORT).show();
   break;
  }
 }

 private void getAccountNames() {
  Intent intent = AccountPicker.newChooseAccountIntent(null, null,
    new String[] { "com.google" }, false, null, null, null, null);
  startActivityForResult(intent, ACCOUNT_PICKER);
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == ACCOUNT_PICKER && resultCode == RESULT_OK) {
   String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
   txtAccount.setText(accountName);
   
  } else if (requestCode == USER_RECOVERABLE_AUTH && resultCode == RESULT_OK) {
   new GetAuthToken(this, txtAccount.getText().toString()).execute();
  } else if (requestCode == USER_RECOVERABLE_AUTH && resultCode == RESULT_CANCELED) {
   Toast.makeText(this, "User rejected authorization.",
     Toast.LENGTH_SHORT).show();
  }
 }

 class GetAuthToken extends AsyncTask {

  private MainActivity mActivity;
  private String mEmail;

  public GetAuthToken(MainActivity mActivity, String mEmail) {
   this.mActivity = mActivity;
   this.mEmail = mEmail;
  }

  @Override
  protected void onPreExecute() {
  }

  @Override
  protected String doInBackground(Void... params) {
   try {
    Log.i("MainActivity", mEmail);
    String token = GoogleAuthUtil.getToken(mActivity, mEmail,"oauth2:https://www.googleapis.com/auth/userinfo.profile");
    Log.i("MainActivity", token);
    return token;

   } catch (UserRecoverableAuthException userRecoverableException) {
    mActivity.startActivityForResult(userRecoverableException.getIntent(),USER_RECOVERABLE_AUTH);
   } catch (Exception e) {
    e.printStackTrace();
   }
   return null;
  }

  @Override
  protected void onPostExecute(String result) {
   if (result != null)
    txtToken.setText(result);
  }

 }
}

Above code contain following important methods

  • buttonClicked : listener for all buttons.
  • checkStatus : It is use to check play service status, simple method contain only one call "
    GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);"
  • getAccountName : It is use to pick account, There are two ways by which you can select an account 1. android.accounts.Account class 2. AccountPicker class. AccountPicker is an easy and clean method to pick an account. It displays picker dialog if use has multiple gmail accounts, whereupon it provides response in onActivityResult.
  • GetAuthToken (AsyncTask) : It use to fetch auth token in doInBackground method using
    GoogleAuthUtil.getToken(), When you run first time it will throw UserRecoverableAuthException exception with one intent. We need to startActivityForResult with the intent and we will get response in onActivityResult. We can take further action based on user's response. call getToken() second time if user accepts/allow application.
Download Source : Click
[To download zip file Click File menu ->Download]

4 comments:

  1. erikswed10411:28 PM

    short and precise thanks. From this how would you go about to the the refresh_token and access_token from this authorization token. The scope is "authToken = GoogleAuthUtil.getToken(a, accountName, "oauth2:https://www.googleapis.com/auth/googletalk");"

    ReplyDelete
  2. If someone not have Google play service app in his phone then??

    ReplyDelete
  3. Peter Teoh7:04 PM

    How Google Play Service works? the link is invalid and replaced by the following:

    http://developer.android.com/google/play-services/index.html

    ReplyDelete
  4. Abdelrahman El-Tamawy9:27 PM

    Thank you very very much. You saved my day. The simplest and best working example ever.

    ReplyDelete