diff --git a/Habitica/build.gradle b/Habitica/build.gradle index 92808771c..e3deea60e 100644 --- a/Habitica/build.gradle +++ b/Habitica/build.gradle @@ -144,8 +144,10 @@ dependencies { releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' //Push Notifications - compile 'com.google.firebase:firebase-core:9.0.2' - compile 'com.google.firebase:firebase-messaging:9.0.2' + compile 'com.google.firebase:firebase-core:9.4.0' + compile 'com.google.firebase:firebase-messaging:9.4.0' + + compile 'com.google.android.gms:play-services-auth:9.4.0' } android { diff --git a/Habitica/res/layout/activity_login.xml b/Habitica/res/layout/activity_login.xml index 29a3fc588..a60eb8557 100644 --- a/Habitica/res/layout/activity_login.xml +++ b/Habitica/res/layout/activity_login.xml @@ -186,6 +186,19 @@ android:layout_span="2"/> + + + diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java b/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java index 0c2da6999..53ac42bf1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/APIHelper.java @@ -281,9 +281,9 @@ public class APIHelper implements Action1 { return this.apiService.connectLocal(auth); } - public Observable connectSocial(String userId, String accessToken) { + public Observable connectSocial(String network, String userId, String accessToken) { UserAuthSocial auth = new UserAuthSocial(); - auth.setNetwork("facebook"); + auth.setNetwork(network); UserAuthSocialTokens authResponse = new UserAuthSocialTokens(); authResponse.setClient_id(userId); authResponse.setAccess_token(accessToken); diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java index 6b00b712e..ea075f4a7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/LoginActivity.java @@ -1,5 +1,20 @@ package com.habitrpg.android.habitica.ui.activities; +import com.google.android.gms.auth.GoogleAuthException; +import com.google.android.gms.auth.GoogleAuthUtil; +import com.google.android.gms.auth.GooglePlayServicesAvailabilityException; +import com.google.android.gms.auth.UserRecoverableAuthException; +import com.google.android.gms.auth.api.Auth; +import com.google.android.gms.auth.api.signin.GoogleSignInAccount; +import com.google.android.gms.auth.api.signin.GoogleSignInOptions; +import com.google.android.gms.auth.api.signin.GoogleSignInResult; +import com.google.android.gms.common.AccountPicker; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.GooglePlayServicesUtil; +import com.google.android.gms.common.Scopes; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.Scope; + import com.amplitude.api.Amplitude; import com.facebook.AccessToken; import com.facebook.CallbackManager; @@ -20,11 +35,14 @@ import com.magicmicky.habitrpgwrapper.lib.models.UserAuthResponse; import org.json.JSONException; import org.json.JSONObject; +import android.accounts.AccountManager; +import android.app.Dialog; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v7.preference.PreferenceManager; import android.text.SpannableString; @@ -40,11 +58,19 @@ import android.widget.ProgressBar; import android.widget.TableRow; import android.widget.TextView; +import java.io.IOException; + import javax.inject.Inject; import butterknife.BindView; import butterknife.ButterKnife; +import butterknife.OnClick; +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.exceptions.Exceptions; import rx.functions.Action1; +import rx.functions.Func0; +import rx.schedulers.Schedulers; /** * @author Mickael Goubin @@ -54,6 +80,9 @@ public class LoginActivity extends BaseActivity private final static String TAG_ADDRESS = "address"; private final static String TAG_USERID = "user"; private final static String TAG_APIKEY = "key"; + static final int REQUEST_CODE_PICK_ACCOUNT = 1000; + private static final int REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR = 1001; + @Inject public APIHelper apiHelper; @@ -84,6 +113,7 @@ public class LoginActivity extends BaseActivity TextView mForgotPWTV; private Menu menu; private CallbackManager callbackManager; + private String googleEmail; @Override protected int getLayoutResId() { @@ -113,7 +143,7 @@ public class LoginActivity extends BaseActivity @Override public void onSuccess(LoginResult loginResult) { AccessToken accessToken = AccessToken.getCurrentAccessToken(); - apiHelper.connectSocial(accessToken.getUserId(), accessToken.getToken()) + apiHelper.connectSocial("facebook", accessToken.getUserId(), accessToken.getToken()) .compose(apiHelper.configureApiCallObserver()) .subscribe(LoginActivity.this, throwable -> { hideProgress(); @@ -262,6 +292,16 @@ public class LoginActivity extends BaseActivity Log.e("scanresult", "Could not parse scanResult", e); } } + + if (requestCode == REQUEST_CODE_PICK_ACCOUNT) { + if (resultCode == RESULT_OK) { + googleEmail = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); + handleGoogleLoginResult(); + } + } + if (requestCode == REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR) { + handleGoogleLoginResult(); + } } private void parse(String contents) { @@ -378,4 +418,56 @@ public class LoginActivity extends BaseActivity this.startMainActivity(); } } + + @OnClick(R.id.google_login_button) + public void handleGoogleLogin() { + String[] accountTypes = new String[]{"com.google"}; + Intent intent = AccountPicker.newChooseAccountIntent(null, null, + accountTypes, false, null, null, null, null); + startActivityForResult(intent, REQUEST_CODE_PICK_ACCOUNT); + } + + private void handleGoogleLoginResult() { + String scopesString = Scopes.PLUS_LOGIN + " " + Scopes.PROFILE + " " + Scopes.EMAIL; + String scopes = "oauth2:" + scopesString; + Observable.defer(() -> { + try { + return Observable.just(GoogleAuthUtil.getToken(LoginActivity.this, googleEmail, scopes)); + } catch (IOException e) { + throw Exceptions.propagate(e); + } catch (GoogleAuthException e) { + throw Exceptions.propagate(e); + } + + }) + .flatMap(token -> apiHelper.connectSocial("google", googleEmail, token)) + .compose(apiHelper.configureApiCallObserver()) + .subscribe(LoginActivity.this, throwable -> { + hideProgress(); + if (GoogleAuthException.class.isAssignableFrom(throwable.getCause().getClass())) { + handleGoogleAuthException((GoogleAuthException)throwable.getCause()); + } + }); + } + + private void handleGoogleAuthException(final Exception e) { + if (e instanceof GooglePlayServicesAvailabilityException) { + // The Google Play services APK is old, disabled, or not present. + // Show a dialog created by Google Play services that allows + // the user to update the APK + int statusCode = ((GooglePlayServicesAvailabilityException)e) + .getConnectionStatusCode(); + Dialog dialog = GooglePlayServicesUtil.getErrorDialog(statusCode, + LoginActivity.this, + REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); + dialog.show(); + } else if (e instanceof UserRecoverableAuthException) { + // Unable to authenticate, such as when the user has not yet granted + // the app access to the account, but the user can fix this. + // Forward the user to an activity in Google Play services. + Intent intent = ((UserRecoverableAuthException)e).getIntent(); + startActivityForResult(intent, + REQUEST_CODE_RECOVER_FROM_PLAY_SERVICES_ERROR); + } + } }