@@ -175,6 +175,7 @@ respective installation guides:
175175
176176 - [ Install Twitter SDK] ( https://fabric.io/kits/android/twitterkit/install )
177177 - [ Install Facebook SDK] ( https://developers.facebook.com/docs/android/getting-started#androidstudio )
178+ - [ Install Google SDK] ( https://developers.google.com/identity/sign-in/android/sign-in )
178179
179180### Login Activities
180181
@@ -216,7 +217,7 @@ it will load the `LoginButtonsActivity`:
216217
217218![ ] ( https://raw.githubusercontent.com/ServiceStack/docs/master/docs/images/java/java-android-login-buttons.png )
218219
219- Where we just use Twitter's and Facebook 's Login Button widgets to render the UI in
220+ Where we just use Twitter's, Facebook's and Google 's Login Button widgets to render the UI in
220221[ login_buttons.xml] ( https://github.com/ServiceStackApps/AndroidJavaChat/blob/master/src/androidchat/app/src/main/res/layout/login_buttons.xml ) :
221222
222223``` xml
@@ -233,6 +234,11 @@ Where we just use Twitter's and Facebook's Login Button widgets to render the UI
233234 android : layout_marginTop =" 30dp"
234235 android : layout_marginBottom =" 30dp" />
235236
237+ <com .google.android.gms.common.SignInButton
238+ android : id =" @+id/sign_in_button"
239+ android : layout_width =" wrap_content"
240+ android : layout_height =" wrap_content" />
241+
236242<Button
237243 android : text =" Guest Login"
238244 android : layout_width =" wrap_content"
@@ -324,6 +330,9 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
324330 super . onActivityResult(requestCode, resultCode, data);
325331 btnTwitterLogin. onActivityResult(requestCode, resultCode, data);
326332 facebookCallback. onActivityResult(requestCode, resultCode, data);
333+ if (requestCode == RC_SIGN_IN ) {
334+ handleGoogleSignInResult(Auth . GoogleSignInApi . getSignInResultFromIntent(data));
335+ }
327336}
328337```
329338
@@ -384,6 +393,114 @@ The Authentication request to our Chat Server is similar to Twitter's except we
384393AccessToken to Authenticate with the Server and we don't need to explicitly save the User's Access Token
385394as Facebook's SDK does this for us behind the scenes.
386395
396+ ### Signing in with Google SignIn Button
397+
398+ Whilst the sign-in process is similar, Google SignIn requires a lot more effort to setup and leaves you
399+ to implement a lot of the mechanics yourself starting with having to choose an arbitrary Request Code
400+ which you'll need to use to manually check whether the Google SignIn Activity has completed, this can
401+ be any number, e.g:
402+
403+ ``` java
404+ private static final int RC_SIGN_IN = 9001 ; // Arbitrary Request Code
405+ ```
406+
407+ Then you'll need to configure your preferred ` GoogleSignInOptions ` , as we want to be able to retrieve the
408+ AccessToken we need to popoulate ` requestServerAuthCode() ` with our Google OAuth App Id:
409+
410+ > If you don't have a Google App, one can be created at [ console.developers.google.com/apis/credentials] ( https://console.developers.google.com/apis/credentials )
411+
412+ ``` java
413+ SignInButton btnGoogleSignIn = (SignInButton ) findViewById(R . id. sign_in_button);
414+ GoogleSignInOptions gso = new GoogleSignInOptions .Builder (GoogleSignInOptions . DEFAULT_SIGN_IN )
415+ .requestEmail()
416+ .requestServerAuthCode(getResources(). getString(R . string. google_key))
417+ .build();
418+ googleApiClient = new GoogleApiClient .Builder (this )
419+ .enableAutoManage(this , r - > { /* Handle On Connection Failed...*/ })
420+ .addApi(Auth . GOOGLE_SIGN_IN_API , gso)
421+ .build();
422+ btnGoogleSignIn. setOnClickListener(v - > {
423+ Intent signInIntent = Auth . GoogleSignInApi . getSignInIntent(googleApiClient);
424+ startActivityForResult(signInIntent, RC_SIGN_IN );
425+ });
426+ ```
427+
428+ You'll then need to configure a ` GoogleApiClient ` with your SignIn Options and then manually bind
429+ Google's ` SignInButton ` to launch a new SignIn Intent with our custom ` RC_SIGN_IN ` .
430+
431+ Once the User has authorized with Google we're notified in ` onActivityResult() ` which gets called back
432+ with our custom ` RC_SIGN_IN ` to let us know we can process the Google SignIn Result:
433+
434+ ``` java
435+ @Override
436+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
437+ // ...
438+ if (requestCode == RC_SIGN_IN ) {
439+ handleGoogleSignInResult(Auth . GoogleSignInApi . getSignInResultFromIntent(data));
440+ }
441+ }
442+ ```
443+
444+ If the ` GoogleSignInResult ` was successful the User has Signed in locally to our App at that point but
445+ as we need to Authenticate the User with the Chat Server we need to retrieve their AccessToken.
446+ Unfortunately Google only returns us a Server Auth Code which we need to use to call another Google API,
447+ passing in our OAuth App Secret to get the User's AccessToken for our App:
448+
449+ ``` java
450+ private void handleGoogleSignInResult(GoogleSignInResult result) {
451+ if (result. isSuccess()) {
452+ GoogleSignInAccount acct = result. getSignInAccount();
453+ UiHelpers . setStatus(txtStatus, " Local google sign-in successful, signing into server..." );
454+
455+ Activity activity = this ;
456+ OkHttpClient client = new OkHttpClient ();
457+ RequestBody requestBody = new FormBody .Builder ()
458+ .add(" grant_type" , " authorization_code" )
459+ .add(" client_id" , getResources(). getString(R . string. google_key))
460+ .add(" client_secret" , getResources(). getString(R . string. google_secret))
461+ .add(" redirect_uri" ," " )
462+ .add(" code" , acct. getServerAuthCode())
463+ .build();
464+ Request request = new Request .Builder ()
465+ .url(" https://www.googleapis.com/oauth2/v4/token" )
466+ .post(requestBody)
467+ .build();
468+ client. newCall(request). enqueue(new okhttp3. Callback () {
469+ @Override
470+ public void onFailure (Call call , IOException e ) {
471+ UiHelpers . setStatus(txtStatus, " Failed to retrieve AccessToken from Google" );
472+ }
473+
474+ @Override
475+ public void onResponse (Call call , Response response ) throws IOException {
476+ String json = response. body(). string();
477+ JsonObject obj = JsonUtils . toJsonObject(json);
478+ String accessToken = obj. get(" access_token" ). getAsString();
479+
480+ App . get(). saveGoogleAccessToken(accessToken);
481+ App . get(). getServiceClient(). postAsync(new dtos. Authenticate ()
482+ .setProvider(" GoogleOAuth" )
483+ .setAccessToken(accessToken)
484+ .setRememberMe(true ),
485+ r - > {
486+ UiHelpers . setStatus(txtStatus, " Server google sign-in successful, opening chat..." );
487+ stopProgressBar();
488+ startActivity(new Intent (activity, MainActivity . class));
489+ },
490+ error - > {
491+ UiHelpers . setStatusError(txtStatus, " Server google sign-in failed" , error);
492+ stopProgressBar();
493+ });
494+ }
495+ });
496+ }
497+ }
498+ ```
499+
500+ Once we retrieve the ` accessToken ` the process is similar to our Twitter login where we save the AccessToken
501+ to allow auto-SignIn's on App Restarts, we then use the ` accessToken ` to Authenticate with the Chat Server
502+ then load the ` MainActivity ` to establish our Authenticated Server Events connection.
503+
387504### Anonymous Sign In
388505
389506If the User doesn't have a Twitter or Facebook account we also let them login as a guest by skipping
0 commit comments