This post will guide you through the integration of GooglePlus Login. I assume that you already know how to generate the key and register your application with both the debug and signed keystores.

The approach i have used is a little different since the regular approach that we see on most of the blogs, does provide the Person information, though in some cases (which is still unknown to me) the information is null.

There are times we wish to use the same code throughout the application and also perform login probably in several activities. But since the GoogleApliClient object must be initialized within the lifecycle of the activity.

We therefore, in this post, and example, maintain just one instance of GoogleApiClient and also within the bounds of the lifecycle.

#Step 1: We declare the object in the application class.

public class MyApplication extends Application {
private static MyApplication mApp;
private static Context mContext;
private static GoogleApiClient mGoogleApiClient;
/**
 * returns the app context
 * @return
 */
public static MyApplication getAppContext() {
    if (mApp == null) {
        mApp = (MyApplication) mContext;
    }
    return mApp;
}
@Override
public void onCreate() {
    super.onCreate();
    mApp = this;
    mContext = this;
}
public void setApiClient(GoogleApiClient googleApiClient) {
    mGoogleApiClient = googleApiClient;
}
public GoogleApiClient getApiClient() {
    return mGoogleApiClient;
}
}
// App class ends here

#Step 2: We now create a bean class that will handle all the google plus functions

Let the bean class implement the following:

GoogleApiClient.ConnectionCallbacks, 
GoogleApiClient.OnConnectionFailedListener

These are the properties of the class:

public static final int RC_SIGN_IN = 4001;
public static final int MY_ACTIVITYS_AUTH_REQUEST_CODE = 5001;
private static boolean isSignInProgress;
private static boolean isIntentInProgress;

// this can be an activity or fragment activity
private FragmentActivity mActivity;

private static GoogleApiClient mGoogleApiClient;
private int counter = 0;
private String mEmail;

// a response listener that receives the response from the async task
private ResponseListener mResponseListener;

Add the following to the bean class

public GoogleLogin(FragmentActivity activity, ResponseListener listener) {
    mActivity = activity;
    mResponseListener = listener;
    mGoogleApiClient = createGoogleApiClient();
MyApplication.getAppContext().setApiClient(mGoogleApiClient);
}

private GoogleApiClient createGoogleApiClient() {
    return new GoogleApiClient.Builder(BevRageApplication.getAppContext())
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(Plus.API)
            .addScope(Plus.SCOPE_PLUS_PROFILE)
            .addScope(Plus.SCOPE_PLUS_LOGIN)
            .build();
}

We still haven’t connected to the service. In order to do that we create the connect function which would look like this:

public void connect() {
    GoogleApiClient googleApiClient = getGoogleApiClient();
    if (!googleApiClient.isConnected()) {
        if (!googleApiClient.isConnecting()) {
            this.isSignInProgress = true;
            googleApiClient.connect();
        }else {
            getProfileInformation();
        }
    } else {
        getProfileInformation();
        Toast.makeText(MyApplication.getAppContext(),
                "GooglePlus connected", Toast.LENGTH_LONG).show();
    }
}

In order to connect to the service on a button click, we therefore call the method like this from an activity, or fragment (the code in this example, is in the activity)

mGoogleLogin = new GoogleLogin(this,mListener);
mGoogleLogin.connect();

A dialogue will appear before you to allow GooglePlus to access your data and that you would wish to sign in or not. OnActivityResult would get called once the action gets performed and it would look like this in the Activity:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == GoogleLogin.RC_SIGN_IN) {
        mGoogleLogin.onResult(requestCode, resultCode, data);
    }else if(requestCode == GoogleLogin.MY_ACTIVITYS_AUTH_REQUEST_CODE) {
        mGoogleLogin.onResult(requestCode, resultCode, data);
    }
}

The onResult method in the bean class will look like this:

public void onResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == RC_SIGN_IN) {
        if(resultCode == -1) {
            if (!getGoogleApiClient().isConnecting()) {
                if(data == null) {
                    isIntentInProgress = false;
                }
                getGoogleApiClient().connect();
            }
        }else {
            isIntentInProgress = false;
        }
    }else if(requestCode == MY_ACTIVITYS_AUTH_REQUEST_CODE) {
        // make a service call again
        if(counter == 1) {
            new ConnectAsyncTask(mEmail).execute();
        }
    }
}

While if the service connects successfully the onConnected method that is overridden will be called

@Override
public void onConnected(Bundle bundle) {
    Log.e(TAG, "onConnected ");
    clearFlags();
    getProfileInformation();
}

The other methods implemented will look as under:

@Override
public void onConnectionSuspended(int i) {
    getGoogleApiClient().connect();
}

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (result.hasResolution()) {
        if (!isIntentInProgress) {
            try {
                isIntentInProgress = true;
                result.startResolutionForResult(mActivity, RC_SIGN_IN);
            } catch (IntentSender.SendIntentException e) {
                Toast.makeText(MyApplication.getAppContext(),
                        "Error connecting to Google+, please try again",
                        Toast.LENGTH_LONG).show();
                Log.e(TAG, "Google+", e);
            }
        }
    } else {
        clearFlags();
        GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(),
                mActivity, 0).show();
    }
}

public void disconnect() {
    GoogleApiClient googleApiClient = getGoogleApiClient();
    if (googleApiClient != null && googleApiClient.isConnected()) {
        Plus.AccountApi.clearDefaultAccount(googleApiClient);
        googleApiClient.disconnect();
    }
}

getProfileInformation will in turn look like this:

public void getProfileInformation() {
    Log.e(TAG, "getProfileInformation ");
    try {
        GoogleApiClient googleApiClient = getGoogleApiClient();
        String email = Plus.AccountApi.getAccountName(googleApiClient);
        Person person = Plus.PeopleApi.getCurrentPerson(googleApiClient);
        mEmail = email;

        if(person != null) // you can use this; 
        else {
            // make a service call with 
            // oauth to get the details
            new ConnectAsyncTask(mEmail).execute();
        }
    } catch (Exception e) {
        Toast.makeText(MyApplication.getAppContext(),
                "Error connecting to Google Plus",Toast. LENGTH_LONG).show();
    }
}

These are some methods that are required:

private GoogleApiClient getGoogleApiClient() {
    return mGoogleApiClient;
}

void clearFlags() {
    isSignInProgress = false;
    isIntentInProgress = false;
}

The final step: Make the webservice call to Google with oauth to get the person’s information:

private class ConnectAsyncTask extends AsyncTask<String, Void, GooglePlusPersonData> {
    String sAccessToken = null;
    String mEmail;
    Dialog mDialog;
    boolean isUserAunthenticateError;

    public ConnectAsyncTask(String mail) {
        mEmail = mail;
        isUserAunthenticateError = false;
    }

    @Override
    protected void onPostExecute(GooglePlusPersonData data) {
        super.onPreExecute();
        if(mDialog != null) {mDialog.dismiss();}

        //This contains all the data you wanted.
        if(!TextUtils.isEmpty(sAccessToken)) {
            if(!isUserAunthenticateError) {
                if(mResponseListener != null) {mResponseListener.onResponseReceived(data);}
            }
        }
    }

    @Override
    protected GooglePlusPersonData doInBackground(String... params) {

        HttpURLConnection urlConnection = null;
        try {
            URL url = new URL(
                    "https://www.googleapis.com/oauth2/v1/userinfo");
            sAccessToken = GoogleAuthUtil
                    .getToken(
                            mActivity,
                            mEmail + "",
                            "oauth2:"
                                    + Scopes.PROFILE
                                    + " https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email");

            //This is probably not the right way to do.
            //What I am doing here is, get an AccessToken, invalidate it and get a new
            //AccessToken again. Because I couldn't find a way to check whether the
            //AccessToken is expired or not.

            GoogleAuthUtil.invalidateToken(mActivity, sAccessToken);

            sAccessToken = GoogleAuthUtil
                    .getToken(
                            mActivity,
                            mEmail + "",
                            "oauth2:"
                                    + Scopes.PROFILE
                                    + " https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email");

            urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestProperty("Authorization", "Bearer "
                    + sAccessToken);

            BufferedReader r = new BufferedReader(new InputStreamReader(
                    urlConnection.getInputStream(), "UTF-8"));
            StringBuilder total = new StringBuilder();
            String line;
            while ((line = r.readLine()) != null) {
                total.append(line);
            }
            line = total.toString();
            Log.e(TAG,"line >>>> " + line);
            if (!TextUtils.isEmpty(line)) {
                Gson gson = new Gson();
                GooglePlusPersonData data = gson.fromJson(line, GooglePlusPersonData.class);
                return data;
            } else {
                return null;
            }
        } catch (UserRecoverableAuthException userAuthEx) {
            // Start the user recoverable action using the intent returned
            // by getIntent()
            counter++;
            isUserAunthenticateError = true;
            userAuthEx.printStackTrace();
            mActivity.startActivityForResult(
                    userAuthEx.getIntent(), MY_ACTIVITYS_AUTH_REQUEST_CODE);
            return null;
        } catch (FileNotFoundException e) {
            //You get this exception when the AccessToken is expired.
            //I don't know how its a FileNotFoundException
            //Thats why instead of relying on this, I did the above to get
            //new AccessToken
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
        return null;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mDialog = CustomProgressDialog.createDialog(mActivity);
        mDialog.show();
    }
}

Now, there are several things that i am using here, such as “counter” and also “isUserAuthenticateError”.

Hold on, here is an explanation why. When making a service call like this, GooglePlus generates the same dialogue we first got with the normal GooglePlus login just that this time you get a detailed popup saying that the app would wish to access your email and also profile.

Thus the onActivityResult will get called, and since on the first action, another popup is called, the activityResult will get called again. To prevent a repetitive call to the service we use the counter.

Where as,¬†isUserAuthenticateError, is used to check if we have really got a valid access token to send the user forward or not. Since, in the first case onPostExecute will get called even if there is a¬†“UserRecoverableAuthException” that is thrown, and you would not wish to move ahead without getting the actual data.

If there is anything you do not understand, you can get back to me or comment :).

You will now always get user data whether Person is null or not.

Happy Coding!

God bless!

Advertisements