| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package io.flutter.plugins.googlesignin; |
| |
| import android.accounts.Account; |
| import android.app.Activity; |
| import android.content.Context; |
| import android.content.Intent; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.VisibleForTesting; |
| import com.google.android.gms.auth.GoogleAuthUtil; |
| import com.google.android.gms.auth.UserRecoverableAuthException; |
| import com.google.android.gms.auth.api.signin.GoogleSignIn; |
| import com.google.android.gms.auth.api.signin.GoogleSignInAccount; |
| import com.google.android.gms.auth.api.signin.GoogleSignInClient; |
| import com.google.android.gms.auth.api.signin.GoogleSignInOptions; |
| import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes; |
| import com.google.android.gms.common.api.ApiException; |
| import com.google.android.gms.common.api.CommonStatusCodes; |
| import com.google.android.gms.common.api.Scope; |
| import com.google.android.gms.tasks.OnCompleteListener; |
| import com.google.android.gms.tasks.RuntimeExecutionException; |
| import com.google.android.gms.tasks.Task; |
| import com.google.common.base.Joiner; |
| import com.google.common.base.Strings; |
| import io.flutter.embedding.engine.plugins.FlutterPlugin; |
| import io.flutter.embedding.engine.plugins.activity.ActivityAware; |
| import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding; |
| import io.flutter.plugin.common.BinaryMessenger; |
| import io.flutter.plugin.common.MethodCall; |
| import io.flutter.plugin.common.MethodChannel; |
| import io.flutter.plugin.common.MethodChannel.MethodCallHandler; |
| import io.flutter.plugin.common.MethodChannel.Result; |
| import io.flutter.plugin.common.PluginRegistry; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.concurrent.Callable; |
| import java.util.concurrent.ExecutionException; |
| import java.util.concurrent.Future; |
| |
| /** Google sign-in plugin for Flutter. */ |
| public class GoogleSignInPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware { |
| private static final String CHANNEL_NAME = "plugins.flutter.io/google_sign_in_android"; |
| |
| private static final String METHOD_INIT = "init"; |
| private static final String METHOD_SIGN_IN_SILENTLY = "signInSilently"; |
| private static final String METHOD_SIGN_IN = "signIn"; |
| private static final String METHOD_GET_TOKENS = "getTokens"; |
| private static final String METHOD_SIGN_OUT = "signOut"; |
| private static final String METHOD_DISCONNECT = "disconnect"; |
| private static final String METHOD_IS_SIGNED_IN = "isSignedIn"; |
| private static final String METHOD_CLEAR_AUTH_CACHE = "clearAuthCache"; |
| private static final String METHOD_REQUEST_SCOPES = "requestScopes"; |
| |
| private Delegate delegate; |
| private MethodChannel channel; |
| private ActivityPluginBinding activityPluginBinding; |
| |
| @SuppressWarnings("deprecation") |
| public static void registerWith(io.flutter.plugin.common.PluginRegistry.Registrar registrar) { |
| GoogleSignInPlugin instance = new GoogleSignInPlugin(); |
| instance.initInstance(registrar.messenger(), registrar.context(), new GoogleSignInWrapper()); |
| instance.setUpRegistrar(registrar); |
| } |
| |
| @VisibleForTesting |
| public void initInstance( |
| BinaryMessenger messenger, Context context, GoogleSignInWrapper googleSignInWrapper) { |
| channel = new MethodChannel(messenger, CHANNEL_NAME); |
| delegate = new Delegate(context, googleSignInWrapper); |
| channel.setMethodCallHandler(this); |
| } |
| |
| @VisibleForTesting |
| @SuppressWarnings("deprecation") |
| public void setUpRegistrar(PluginRegistry.Registrar registrar) { |
| delegate.setUpRegistrar(registrar); |
| } |
| |
| private void dispose() { |
| delegate = null; |
| channel.setMethodCallHandler(null); |
| channel = null; |
| } |
| |
| private void attachToActivity(ActivityPluginBinding activityPluginBinding) { |
| this.activityPluginBinding = activityPluginBinding; |
| activityPluginBinding.addActivityResultListener(delegate); |
| delegate.setActivity(activityPluginBinding.getActivity()); |
| } |
| |
| private void disposeActivity() { |
| activityPluginBinding.removeActivityResultListener(delegate); |
| delegate.setActivity(null); |
| activityPluginBinding = null; |
| } |
| |
| @Override |
| public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { |
| initInstance( |
| binding.getBinaryMessenger(), binding.getApplicationContext(), new GoogleSignInWrapper()); |
| } |
| |
| @Override |
| public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { |
| dispose(); |
| } |
| |
| @Override |
| public void onAttachedToActivity(ActivityPluginBinding activityPluginBinding) { |
| attachToActivity(activityPluginBinding); |
| } |
| |
| @Override |
| public void onDetachedFromActivityForConfigChanges() { |
| disposeActivity(); |
| } |
| |
| @Override |
| public void onReattachedToActivityForConfigChanges(ActivityPluginBinding activityPluginBinding) { |
| attachToActivity(activityPluginBinding); |
| } |
| |
| @Override |
| public void onDetachedFromActivity() { |
| disposeActivity(); |
| } |
| |
| @Override |
| public void onMethodCall(MethodCall call, Result result) { |
| switch (call.method) { |
| case METHOD_INIT: |
| String signInOption = call.argument("signInOption"); |
| List<String> requestedScopes = call.argument("scopes"); |
| String hostedDomain = call.argument("hostedDomain"); |
| String clientId = call.argument("clientId"); |
| delegate.init(result, signInOption, requestedScopes, hostedDomain, clientId); |
| break; |
| |
| case METHOD_SIGN_IN_SILENTLY: |
| delegate.signInSilently(result); |
| break; |
| |
| case METHOD_SIGN_IN: |
| delegate.signIn(result); |
| break; |
| |
| case METHOD_GET_TOKENS: |
| String email = call.argument("email"); |
| boolean shouldRecoverAuth = call.argument("shouldRecoverAuth"); |
| delegate.getTokens(result, email, shouldRecoverAuth); |
| break; |
| |
| case METHOD_SIGN_OUT: |
| delegate.signOut(result); |
| break; |
| |
| case METHOD_CLEAR_AUTH_CACHE: |
| String token = call.argument("token"); |
| delegate.clearAuthCache(result, token); |
| break; |
| |
| case METHOD_DISCONNECT: |
| delegate.disconnect(result); |
| break; |
| |
| case METHOD_IS_SIGNED_IN: |
| delegate.isSignedIn(result); |
| break; |
| |
| case METHOD_REQUEST_SCOPES: |
| List<String> scopes = call.argument("scopes"); |
| delegate.requestScopes(result, scopes); |
| break; |
| |
| default: |
| result.notImplemented(); |
| } |
| } |
| |
| /** |
| * A delegate interface that exposes all of the sign-in functionality for other plugins to use. |
| * The below {@link Delegate} implementation should be used by any clients unless they need to |
| * override some of these functions, such as for testing. |
| */ |
| public interface IDelegate { |
| /** Initializes this delegate so that it is ready to perform other operations. */ |
| public void init( |
| Result result, |
| String signInOption, |
| List<String> requestedScopes, |
| String hostedDomain, |
| String clientId); |
| |
| /** |
| * Returns the account information for the user who is signed in to this app. If no user is |
| * signed in, tries to sign the user in without displaying any user interface. |
| */ |
| public void signInSilently(Result result); |
| |
| /** |
| * Signs the user in via the sign-in user interface, including the OAuth consent flow if scopes |
| * were requested. |
| */ |
| public void signIn(Result result); |
| |
| /** |
| * Gets an OAuth access token with the scopes that were specified during initialization for the |
| * user with the specified email address. |
| * |
| * <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to |
| * complete, the method will attempt to recover authentication and rerun method. |
| */ |
| public void getTokens(final Result result, final String email, final boolean shouldRecoverAuth); |
| |
| /** |
| * Clears the token from any client cache forcing the next {@link #getTokens} call to fetch a |
| * new one. |
| */ |
| public void clearAuthCache(final Result result, final String token); |
| |
| /** |
| * Signs the user out. Their credentials may remain valid, meaning they'll be able to silently |
| * sign back in. |
| */ |
| public void signOut(Result result); |
| |
| /** Signs the user out, and revokes their credentials. */ |
| public void disconnect(Result result); |
| |
| /** Checks if there is a signed in user. */ |
| public void isSignedIn(Result result); |
| |
| /** Prompts the user to grant an additional Oauth scopes. */ |
| public void requestScopes(final Result result, final List<String> scopes); |
| } |
| |
| /** |
| * Delegate class that does the work for the Google sign-in plugin. This is exposed as a dedicated |
| * class for use in other plugins that wrap basic sign-in functionality. |
| * |
| * <p>All methods in this class assume that they are run to completion before any other method is |
| * invoked. In this context, "run to completion" means that their {@link Result} argument has been |
| * completed (either successfully or in error). This class provides no synchronization constructs |
| * to guarantee such behavior; callers are responsible for providing such guarantees. |
| */ |
| public static class Delegate implements IDelegate, PluginRegistry.ActivityResultListener { |
| private static final int REQUEST_CODE_SIGNIN = 53293; |
| private static final int REQUEST_CODE_RECOVER_AUTH = 53294; |
| @VisibleForTesting static final int REQUEST_CODE_REQUEST_SCOPE = 53295; |
| |
| private static final String ERROR_REASON_EXCEPTION = "exception"; |
| private static final String ERROR_REASON_STATUS = "status"; |
| // These error codes must match with ones declared on iOS and Dart sides. |
| private static final String ERROR_REASON_SIGN_IN_CANCELED = "sign_in_canceled"; |
| private static final String ERROR_REASON_SIGN_IN_REQUIRED = "sign_in_required"; |
| private static final String ERROR_REASON_NETWORK_ERROR = "network_error"; |
| private static final String ERROR_REASON_SIGN_IN_FAILED = "sign_in_failed"; |
| private static final String ERROR_FAILURE_TO_RECOVER_AUTH = "failed_to_recover_auth"; |
| private static final String ERROR_USER_RECOVERABLE_AUTH = "user_recoverable_auth"; |
| |
| private static final String DEFAULT_SIGN_IN = "SignInOption.standard"; |
| private static final String DEFAULT_GAMES_SIGN_IN = "SignInOption.games"; |
| |
| private final Context context; |
| // Only set registrar for v1 embedder. |
| @SuppressWarnings("deprecation") |
| private PluginRegistry.Registrar registrar; |
| // Only set activity for v2 embedder. Always access activity from getActivity() method. |
| private Activity activity; |
| private final BackgroundTaskRunner backgroundTaskRunner = new BackgroundTaskRunner(1); |
| private final GoogleSignInWrapper googleSignInWrapper; |
| |
| private GoogleSignInClient signInClient; |
| private List<String> requestedScopes; |
| private PendingOperation pendingOperation; |
| |
| public Delegate(Context context, GoogleSignInWrapper googleSignInWrapper) { |
| this.context = context; |
| this.googleSignInWrapper = googleSignInWrapper; |
| } |
| |
| @SuppressWarnings("deprecation") |
| public void setUpRegistrar(PluginRegistry.Registrar registrar) { |
| this.registrar = registrar; |
| registrar.addActivityResultListener(this); |
| } |
| |
| public void setActivity(Activity activity) { |
| this.activity = activity; |
| } |
| |
| // Only access activity with this method. |
| public Activity getActivity() { |
| return registrar != null ? registrar.activity() : activity; |
| } |
| |
| private void checkAndSetPendingOperation(String method, Result result) { |
| checkAndSetPendingOperation(method, result, null); |
| } |
| |
| private void checkAndSetPendingOperation(String method, Result result, Object data) { |
| if (pendingOperation != null) { |
| throw new IllegalStateException( |
| "Concurrent operations detected: " + pendingOperation.method + ", " + method); |
| } |
| pendingOperation = new PendingOperation(method, result, data); |
| } |
| |
| /** |
| * Initializes this delegate so that it is ready to perform other operations. The Dart code |
| * guarantees that this will be called and completed before any other methods are invoked. |
| */ |
| @Override |
| public void init( |
| Result result, |
| String signInOption, |
| List<String> requestedScopes, |
| String hostedDomain, |
| String clientId) { |
| try { |
| GoogleSignInOptions.Builder optionsBuilder; |
| |
| switch (signInOption) { |
| case DEFAULT_GAMES_SIGN_IN: |
| optionsBuilder = |
| new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_GAMES_SIGN_IN); |
| break; |
| case DEFAULT_SIGN_IN: |
| optionsBuilder = |
| new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN).requestEmail(); |
| break; |
| default: |
| throw new IllegalStateException("Unknown signInOption"); |
| } |
| |
| // Only requests a clientId if google-services.json was present and parsed |
| // by the google-services Gradle script. |
| // TODO(jackson): Perhaps we should provide a mechanism to override this |
| // behavior. |
| int clientIdIdentifier = |
| context |
| .getResources() |
| .getIdentifier("default_web_client_id", "string", context.getPackageName()); |
| if (!Strings.isNullOrEmpty(clientId)) { |
| optionsBuilder.requestIdToken(clientId); |
| optionsBuilder.requestServerAuthCode(clientId); |
| } else if (clientIdIdentifier != 0) { |
| optionsBuilder.requestIdToken(context.getString(clientIdIdentifier)); |
| optionsBuilder.requestServerAuthCode(context.getString(clientIdIdentifier)); |
| } |
| for (String scope : requestedScopes) { |
| optionsBuilder.requestScopes(new Scope(scope)); |
| } |
| if (!Strings.isNullOrEmpty(hostedDomain)) { |
| optionsBuilder.setHostedDomain(hostedDomain); |
| } |
| |
| this.requestedScopes = requestedScopes; |
| signInClient = GoogleSignIn.getClient(context, optionsBuilder.build()); |
| result.success(null); |
| } catch (Exception e) { |
| result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null); |
| } |
| } |
| |
| /** |
| * Returns the account information for the user who is signed in to this app. If no user is |
| * signed in, tries to sign the user in without displaying any user interface. |
| */ |
| @Override |
| public void signInSilently(Result result) { |
| checkAndSetPendingOperation(METHOD_SIGN_IN_SILENTLY, result); |
| Task<GoogleSignInAccount> task = signInClient.silentSignIn(); |
| if (task.isSuccessful()) { |
| // There's immediate result available. |
| onSignInAccount(task.getResult()); |
| } else { |
| task.addOnCompleteListener( |
| new OnCompleteListener<GoogleSignInAccount>() { |
| @Override |
| public void onComplete(Task<GoogleSignInAccount> task) { |
| onSignInResult(task); |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Signs the user in via the sign-in user interface, including the OAuth consent flow if scopes |
| * were requested. |
| */ |
| @Override |
| public void signIn(Result result) { |
| if (getActivity() == null) { |
| throw new IllegalStateException("signIn needs a foreground activity"); |
| } |
| checkAndSetPendingOperation(METHOD_SIGN_IN, result); |
| |
| Intent signInIntent = signInClient.getSignInIntent(); |
| getActivity().startActivityForResult(signInIntent, REQUEST_CODE_SIGNIN); |
| } |
| |
| /** |
| * Signs the user out. Their credentials may remain valid, meaning they'll be able to silently |
| * sign back in. |
| */ |
| @Override |
| public void signOut(Result result) { |
| checkAndSetPendingOperation(METHOD_SIGN_OUT, result); |
| |
| signInClient |
| .signOut() |
| .addOnCompleteListener( |
| new OnCompleteListener<Void>() { |
| @Override |
| public void onComplete(Task<Void> task) { |
| if (task.isSuccessful()) { |
| finishWithSuccess(null); |
| } else { |
| finishWithError(ERROR_REASON_STATUS, "Failed to signout."); |
| } |
| } |
| }); |
| } |
| |
| /** Signs the user out, and revokes their credentials. */ |
| @Override |
| public void disconnect(Result result) { |
| checkAndSetPendingOperation(METHOD_DISCONNECT, result); |
| |
| signInClient |
| .revokeAccess() |
| .addOnCompleteListener( |
| new OnCompleteListener<Void>() { |
| @Override |
| public void onComplete(Task<Void> task) { |
| if (task.isSuccessful()) { |
| finishWithSuccess(null); |
| } else { |
| finishWithError(ERROR_REASON_STATUS, "Failed to disconnect."); |
| } |
| } |
| }); |
| } |
| |
| /** Checks if there is a signed in user. */ |
| @Override |
| public void isSignedIn(final Result result) { |
| boolean value = GoogleSignIn.getLastSignedInAccount(context) != null; |
| result.success(value); |
| } |
| |
| @Override |
| public void requestScopes(Result result, List<String> scopes) { |
| checkAndSetPendingOperation(METHOD_REQUEST_SCOPES, result); |
| |
| GoogleSignInAccount account = googleSignInWrapper.getLastSignedInAccount(context); |
| if (account == null) { |
| finishWithError(ERROR_REASON_SIGN_IN_REQUIRED, "No account to grant scopes."); |
| return; |
| } |
| |
| List<Scope> wrappedScopes = new ArrayList<>(); |
| |
| for (String scope : scopes) { |
| Scope wrappedScope = new Scope(scope); |
| if (!googleSignInWrapper.hasPermissions(account, wrappedScope)) { |
| wrappedScopes.add(wrappedScope); |
| } |
| } |
| |
| if (wrappedScopes.isEmpty()) { |
| finishWithSuccess(true); |
| return; |
| } |
| |
| googleSignInWrapper.requestPermissions( |
| getActivity(), REQUEST_CODE_REQUEST_SCOPE, account, wrappedScopes.toArray(new Scope[0])); |
| } |
| |
| private void onSignInResult(Task<GoogleSignInAccount> completedTask) { |
| try { |
| GoogleSignInAccount account = completedTask.getResult(ApiException.class); |
| onSignInAccount(account); |
| } catch (ApiException e) { |
| // Forward all errors and let Dart side decide how to handle. |
| String errorCode = errorCodeForStatus(e.getStatusCode()); |
| finishWithError(errorCode, e.toString()); |
| } catch (RuntimeExecutionException e) { |
| finishWithError(ERROR_REASON_EXCEPTION, e.toString()); |
| } |
| } |
| |
| private void onSignInAccount(GoogleSignInAccount account) { |
| Map<String, Object> response = new HashMap<>(); |
| response.put("email", account.getEmail()); |
| response.put("id", account.getId()); |
| response.put("idToken", account.getIdToken()); |
| response.put("serverAuthCode", account.getServerAuthCode()); |
| response.put("displayName", account.getDisplayName()); |
| if (account.getPhotoUrl() != null) { |
| response.put("photoUrl", account.getPhotoUrl().toString()); |
| } |
| finishWithSuccess(response); |
| } |
| |
| private String errorCodeForStatus(int statusCode) { |
| if (statusCode == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) { |
| return ERROR_REASON_SIGN_IN_CANCELED; |
| } else if (statusCode == CommonStatusCodes.SIGN_IN_REQUIRED) { |
| return ERROR_REASON_SIGN_IN_REQUIRED; |
| } else if (statusCode == CommonStatusCodes.NETWORK_ERROR) { |
| return ERROR_REASON_NETWORK_ERROR; |
| } else { |
| return ERROR_REASON_SIGN_IN_FAILED; |
| } |
| } |
| |
| private void finishWithSuccess(Object data) { |
| pendingOperation.result.success(data); |
| pendingOperation = null; |
| } |
| |
| private void finishWithError(String errorCode, String errorMessage) { |
| pendingOperation.result.error(errorCode, errorMessage, null); |
| pendingOperation = null; |
| } |
| |
| private static class PendingOperation { |
| final String method; |
| final Result result; |
| final Object data; |
| |
| PendingOperation(String method, Result result, Object data) { |
| this.method = method; |
| this.result = result; |
| this.data = data; |
| } |
| } |
| |
| /** Clears the token kept in the client side cache. */ |
| @Override |
| public void clearAuthCache(final Result result, final String token) { |
| Callable<Void> clearTokenTask = |
| new Callable<Void>() { |
| @Override |
| public Void call() throws Exception { |
| GoogleAuthUtil.clearToken(context, token); |
| return null; |
| } |
| }; |
| |
| backgroundTaskRunner.runInBackground( |
| clearTokenTask, |
| new BackgroundTaskRunner.Callback<Void>() { |
| @Override |
| public void run(Future<Void> clearTokenFuture) { |
| try { |
| result.success(clearTokenFuture.get()); |
| } catch (ExecutionException e) { |
| result.error(ERROR_REASON_EXCEPTION, e.getCause().getMessage(), null); |
| } catch (InterruptedException e) { |
| result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null); |
| Thread.currentThread().interrupt(); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * Gets an OAuth access token with the scopes that were specified during initialization for the |
| * user with the specified email address. |
| * |
| * <p>If shouldRecoverAuth is set to true and user needs to recover authentication for method to |
| * complete, the method will attempt to recover authentication and rerun method. |
| */ |
| @Override |
| public void getTokens( |
| final Result result, final String email, final boolean shouldRecoverAuth) { |
| if (email == null) { |
| result.error(ERROR_REASON_EXCEPTION, "Email is null", null); |
| return; |
| } |
| |
| Callable<String> getTokenTask = |
| new Callable<String>() { |
| @Override |
| public String call() throws Exception { |
| Account account = new Account(email, "com.google"); |
| String scopesStr = "oauth2:" + Joiner.on(' ').join(requestedScopes); |
| return GoogleAuthUtil.getToken(context, account, scopesStr); |
| } |
| }; |
| |
| // Background task runner has a single thread effectively serializing |
| // the getToken calls. 1p apps can then enjoy the token cache if multiple |
| // getToken calls are coming in. |
| backgroundTaskRunner.runInBackground( |
| getTokenTask, |
| new BackgroundTaskRunner.Callback<String>() { |
| @Override |
| public void run(Future<String> tokenFuture) { |
| try { |
| String token = tokenFuture.get(); |
| HashMap<String, String> tokenResult = new HashMap<>(); |
| tokenResult.put("accessToken", token); |
| result.success(tokenResult); |
| } catch (ExecutionException e) { |
| if (e.getCause() instanceof UserRecoverableAuthException) { |
| if (shouldRecoverAuth && pendingOperation == null) { |
| Activity activity = getActivity(); |
| if (activity == null) { |
| result.error( |
| ERROR_USER_RECOVERABLE_AUTH, |
| "Cannot recover auth because app is not in foreground. " |
| + e.getLocalizedMessage(), |
| null); |
| } else { |
| checkAndSetPendingOperation(METHOD_GET_TOKENS, result, email); |
| Intent recoveryIntent = |
| ((UserRecoverableAuthException) e.getCause()).getIntent(); |
| activity.startActivityForResult(recoveryIntent, REQUEST_CODE_RECOVER_AUTH); |
| } |
| } else { |
| result.error(ERROR_USER_RECOVERABLE_AUTH, e.getLocalizedMessage(), null); |
| } |
| } else { |
| result.error(ERROR_REASON_EXCEPTION, e.getCause().getMessage(), null); |
| } |
| } catch (InterruptedException e) { |
| result.error(ERROR_REASON_EXCEPTION, e.getMessage(), null); |
| Thread.currentThread().interrupt(); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public boolean onActivityResult(int requestCode, int resultCode, Intent data) { |
| if (pendingOperation == null) { |
| return false; |
| } |
| switch (requestCode) { |
| case REQUEST_CODE_RECOVER_AUTH: |
| if (resultCode == Activity.RESULT_OK) { |
| // Recover the previous result and data and attempt to get tokens again. |
| Result result = pendingOperation.result; |
| String email = (String) pendingOperation.data; |
| pendingOperation = null; |
| getTokens(result, email, false); |
| } else { |
| finishWithError( |
| ERROR_FAILURE_TO_RECOVER_AUTH, "Failed attempt to recover authentication"); |
| } |
| return true; |
| case REQUEST_CODE_SIGNIN: |
| // Whether resultCode is OK or not, the Task returned by GoogleSigIn will determine |
| // failure with better specifics which are extracted in onSignInResult method. |
| if (data != null) { |
| onSignInResult(GoogleSignIn.getSignedInAccountFromIntent(data)); |
| } else { |
| // data is null which is highly unusual for a sign in result. |
| finishWithError(ERROR_REASON_SIGN_IN_FAILED, "Signin failed"); |
| } |
| return true; |
| case REQUEST_CODE_REQUEST_SCOPE: |
| finishWithSuccess(resultCode == Activity.RESULT_OK); |
| return true; |
| default: |
| return false; |
| } |
| } |
| } |
| } |