001package com.gigya.android.sdk.providers.provider;
002
003import android.app.Activity;
004import android.content.Context;
005import android.content.Intent;
006import android.os.Bundle;
007import android.support.annotation.NonNull;
008import android.support.annotation.Nullable;
009import android.support.v7.app.AppCompatActivity;
010
011import com.gigya.android.sdk.GigyaLogger;
012import com.gigya.android.sdk.api.IBusinessApiService;
013import com.gigya.android.sdk.persistence.IPersistenceService;
014import com.gigya.android.sdk.ui.HostActivity;
015import com.gigya.android.sdk.utils.FileUtils;
016import com.google.android.gms.auth.api.signin.GoogleSignIn;
017import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
018import com.google.android.gms.auth.api.signin.GoogleSignInClient;
019import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
020import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
021import com.google.android.gms.common.ConnectionResult;
022import com.google.android.gms.common.GoogleApiAvailability;
023import com.google.android.gms.common.api.ApiException;
024import com.google.android.gms.tasks.OnCompleteListener;
025import com.google.android.gms.tasks.Task;
026
027import org.json.JSONObject;
028
029import java.util.Map;
030
031import static com.gigya.android.sdk.GigyaDefinitions.Providers.GOOGLE;
032
033public class GoogleProvider extends Provider {
034
035    private static final int RC_SIGN_IN = 0;
036    private GoogleSignInClient _googleClient;
037    private String _clientId;
038
039    final private FileUtils _fileUtils;
040
041    public GoogleProvider(Context context,
042                          IPersistenceService persistenceService,
043                          IBusinessApiService businessApiService,
044                          ProviderCallback providerCallback,
045                          FileUtils fileUtils) {
046        super(context, persistenceService, businessApiService, providerCallback);
047        _fileUtils = fileUtils;
048    }
049
050    @Override
051    public String getName() {
052        return GOOGLE;
053    }
054
055    public static boolean isAvailable(Context context) {
056        try {
057            Class.forName("com.google.android.gms.auth.api.signin.GoogleSignInClient");
058            return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
059        } catch (Throwable t) {
060            return false;
061        }
062    }
063
064    @Override
065    public void login(final Map<String, Object> loginParams, final String loginMode) {
066        if (_connecting) {
067            return;
068        }
069        _connecting = true;
070        _loginMode = loginMode;
071        try {
072            _clientId = _fileUtils.stringFromMetaData("googleClientId");
073        } catch (Exception ex) {
074            _connecting = false;
075            ex.printStackTrace();
076        }
077        if (_clientId == null) {
078            onLoginFailed("Missing server client id. Check manifest implementation");
079            return;
080        }
081        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
082                .requestServerAuthCode(_clientId)
083                .requestEmail()
084                .build();
085        _googleClient = GoogleSignIn.getClient(_context, gso);
086
087        GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(_context);
088        if (account != null) {
089            // This option should not happen theoretically because we logout out explicitly.
090            onLoginSuccess(loginParams, getProviderSessions(account.getServerAuthCode(), -1L, null), loginMode);
091            finish(null);
092            return;
093        }
094
095        HostActivity.present(_context, new HostActivity.HostActivityLifecycleCallbacks() {
096
097            @Override
098            public void onCreate(AppCompatActivity activity, @Nullable Bundle savedInstanceState) {
099                Intent signInIntent = _googleClient.getSignInIntent();
100                activity.startActivityForResult(signInIntent, RC_SIGN_IN);
101            }
102
103            @Override
104            public void onActivityResult(AppCompatActivity activity, int requestCode, int resultCode, @Nullable Intent data) {
105                if (requestCode == RC_SIGN_IN) {
106                    Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
107                    handleSignInResult(loginParams, activity, task);
108                }
109            }
110        });
111    }
112
113    private void handleSignInResult(final Map<String, Object> loginParams, AppCompatActivity activity, Task<GoogleSignInAccount> completedTask) {
114        try {
115            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
116            if (account == null) {
117                onLoginFailed("Account unavailable");
118            } else {
119                /* Fetch server auth code */
120                final String authCode = account.getServerAuthCode();
121                if (authCode == null) {
122                    onLoginFailed("Id token no available");
123                } else {
124                    onLoginSuccess(loginParams, getProviderSessions(authCode, -1L, null), _loginMode);
125                }
126            }
127            finish(activity);
128        } catch (ApiException e) {
129            final int exceptionStatusCode = e.getStatusCode();
130            switch (exceptionStatusCode) {
131                case GoogleSignInStatusCodes.SIGN_IN_CANCELLED:
132                    onCanceled();
133                    break;
134                case GoogleSignInStatusCodes.SIGN_IN_FAILED:
135                default:
136                    onLoginFailed(GoogleSignInStatusCodes.getStatusCodeString(exceptionStatusCode));
137                    break;
138            }
139            finish(activity);
140        }
141    }
142
143    @Override
144    public void logout() {
145        super.logout();
146        if (_googleClient == null) {
147            if (_clientId == null) {
148                GigyaLogger.error("GoogleLoginProvider", "provider client id unavailable for logout");
149                return;
150            }
151            GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
152                    .requestServerAuthCode(_clientId)
153                    .requestEmail()
154                    .build();
155            _googleClient = GoogleSignIn.getClient(_context, gso);
156        }
157        _googleClient.signOut();
158    }
159
160    @Override
161    public String getProviderSessions(String tokenOrCode, long expiration, String uid) {
162        // code is relevant.
163        try {
164            return new JSONObject()
165                    .put(getName(), new JSONObject()
166                            .put("code", tokenOrCode)).toString();
167        } catch (Exception ex) {
168            _connecting = false;
169            ex.printStackTrace();
170        }
171        return null;
172    }
173
174    @Override
175    public boolean supportsTokenTracking() {
176        return false;
177    }
178
179    @Override
180    public void trackTokenChange() {
181        // Stub.
182    }
183
184    private void finish(final Activity activity) {
185        if (_googleClient != null) {
186            _googleClient.signOut().addOnCompleteListener(new OnCompleteListener<Void>() {
187                @Override
188                public void onComplete(@NonNull Task<Void> task) {
189                    if (activity != null) {
190                        activity.finish();
191                    }
192                }
193            });
194        } else {
195            if (activity != null) {
196                activity.finish();
197            }
198        }
199    }
200}