As you all are aware that Google Plus is shutting down in March 2019 and so are all its services. I have had a legacy android app on play store that was using the GoogleApiClient for authentication with Google Plus services, alas, I had to upgrade the application to use the new GoogleSignInClient. And, I am glad that I did so for the following reasons:
- GoogleSignInClient API is based on Task Api
- It does not involve managing connection as with GoogleApiClient API's, so no callback hell and boilerplate code for managing connection state.
- You can get other information such as user's first name, last name, and email, directly from the result.
So how does it look.
The initialization is pretty much the similar to the earlier post.
Initialize the GoogleSignInClient in onCreate method.
Initialize the GoogleSignInOptions with Profile scope, which gives you basic profile information as before. You can request for email via requestEmail on the builder.
As before, you can request the authorization token to perform the request on behalf of the user from your backend server, this is available as server_auth_code on the response.
The client verification token is available in client_token on the response.
@Override protected void onCreate(Bundle savedInstanceState) { mActivity = this; GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(serverToken).requestServerAuthCode(serverToken). requestEmail(). requestScopes(new Scope(Scopes.PROFILE)) .build(); mGoogleSignInClient = GoogleSignIn.getClient(this, gso); }
The fresh sign in method is mentioned below, note that you can attempt silentSignIn if user has already signed in your app before. This will be shown later.
private void signIn() { Intent intent = mGoogleSignInClient.getSignInIntent(); if (!mIntentInProgress) { mIntentInProgress = true; showProgressDialog(); startActivityForResult(intent, RC_SIGN_IN); } }
Handling the result. We just check for the request code and call the Task<GoogleSignInAccount> getSignedInAccountFromIntent method with the intent data.
After we obtain the task, we can just call the addOnCompleteListener method of the Task API, to check for success or failure and maybe retry on failure?.
In case of Success, we refresh the UI to show that the user has logged in and also store the client_token and server_auth_code. getProfileInfo method just extracts the relevant profile information from the user.
In case of failure, which can be caused, if we attempted silentSignIn, and it failed with SIGN_IN_REQUIRED error code, we retry with fresh signin. Finally, If the request fails with SIGN_IN_FAILED, we cannot use that account for sign in and inform the user of the same.
@Override protected void onActivityResult(int requestCode, int responseCode, Intent data) { mIntentInProgress = false; if (requestCode == RC_SIGN_IN) { hideProgressDialog(); Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data); handleSignInResult(task, false); } } private void handleSignInResult(Task<GoogleSignInAccount> task, final boolean silent) { task.addOnCompleteListener(new OnCompleteListener<GoogleSignInAccount>() { @Override public void onComplete(@NonNull Task<GoogleSignInAccount> task) { if (task.isSuccessful()) { GoogleSignInAccount result = task.getResult(); isSignedIn = true; invalidateOptionsMenu(); SharedPreferences.Editor editor = preferences.edit(); editor.putString("client_token", result.getIdToken()); editor.putString("server_auth_code", result.getServerAuthCode()); editor.apply(); getProfileInfo(result); } else { Exception e = task.getException(); if (e instanceof ApiException) { ApiException apiEx = (ApiException) e; if (silent && apiEx.getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_REQUIRED) { signIn(); } if (apiEx.getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_FAILED) { FeedReaderApplication .showSnackOrToast(findViewById(R.id.main_parent_view), R.string.sign_in_failed, true); } } } } }); }
Silent Sign In: If the user has already signed in earlier, and we have obtained the tokens, instead of starting the sign in flow, we can attempt silentSignIn. We already handle the failures in handleSignInResult method. In addition, silentSignIn should be our default and should be called in onResume method of activity. Before calling silentSignIn, we can check whether user is connected to the internet.
private void attemptSilentSignIn() { Task<GoogleSignInAccount> task = mGoogleSignInClient.silentSignIn(); handleSignInResult(task, true); } private void signInUsingNewAPI() { ConnectionChecker checker = new ConnectionChecker(FeedReaderApplication.getAppContext()); boolean isConnected = checker.isConnectedToInternet(); if (!isSignedIn && isConnected) { attemptSilentSignIn(); } }
Signing Out:
public void signOutFromGoogle() { mGoogleSignInClient.signOut(); isSignedIn = false; }
Revoking Access:
public void revokeGoogleAccess() { mGoogleSignInClient.revokeAccess(); isSignedIn = false; }
Finally, A helper method is shown below for extracting user profile information from GoogleSignInAccount
public void getProfileInfo(GoogleSignInAccount account) { String personName = account.getDisplayName(); String firstName = account.getDisplayName(); String lastName = account.getDisplayName(); Uri photoUrl = account.getPhotoUrl(); String email = account.getEmail(); }
Conclusions
The Task API and GoogleSignInClient makes it a lot easier to manage sign in process and flow. You don't have to take my word for it, just look at the earlier post.