diff --git a/auth/README.md b/auth/README.md index 587b43b59..65d6ebf77 100644 --- a/auth/README.md +++ b/auth/README.md @@ -91,8 +91,8 @@ Twitter app as reported by the [Twitter application manager](https://apps.twitte ``` -In addition, if you are using Smart Lock or require a user's email, you must enable the -"Request email addresses from users" permission in the "Permissions" tab of your app. +In addition, you must enable the "Request email addresses from users" permission +in the "Permissions" tab of your Twitter app. ## Using FirebaseUI for Authentication diff --git a/auth/build.gradle b/auth/build.gradle index 54d63cbf8..5620df3c3 100644 --- a/auth/build.gradle +++ b/auth/build.gradle @@ -44,7 +44,7 @@ dependencies { compile "com.google.android.gms:play-services-auth:$firebase_version" compile 'com.facebook.android:facebook-android-sdk:4.18.0' - compile("com.twitter.sdk.android:twitter:2.2.0@aar") { transitive = true } + compile("com.twitter.sdk.android:twitter:2.3.0@aar") { transitive = true } // The following libraries are needed to prevent incompatibilities with the facebook // library when updating com.android.support libraries: diff --git a/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java b/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java index 87297ea3b..9b9da312c 100644 --- a/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java +++ b/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java @@ -17,6 +17,7 @@ import android.content.Intent; import android.os.Parcel; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.firebase.ui.auth.ui.ExtraConstants; @@ -25,39 +26,37 @@ * A container that encapsulates the result of authenticating with an Identity Provider. */ public class IdpResponse implements Parcelable { - private final String mProviderId; - @Nullable private final String mEmail; private final String mToken; private final String mSecret; private final int mErrorCode; - public IdpResponse(int errorCode) { + private IdpResponse(int errorCode) { this(null, null, null, null, errorCode); } - public IdpResponse(String providerId, String email) { - this(providerId, email, null, null); + public IdpResponse(@NonNull String providerId, @NonNull String email) { + this(providerId, email, null, null, ResultCodes.OK); } - public IdpResponse(String providerId, @Nullable String email, @Nullable String token) { - this(providerId, email, token, null); + public IdpResponse(@NonNull String providerId, @NonNull String email, @NonNull String token) { + this(providerId, email, token, null, ResultCodes.OK); } public IdpResponse( - String providerId, - @Nullable String email, - @Nullable String token, - @Nullable String secret) { + @NonNull String providerId, + @NonNull String email, + @NonNull String token, + @NonNull String secret) { this(providerId, email, token, secret, ResultCodes.OK); } - public IdpResponse( + private IdpResponse( String providerId, - @Nullable String email, - @Nullable String token, - @Nullable String secret, + String email, + String token, + String secret, int errorCode) { mProviderId = providerId; mEmail = email; @@ -87,11 +86,17 @@ public IdpResponse[] newArray(int size) { /** * Get the type of provider. e.g. {@link AuthUI#GOOGLE_PROVIDER} */ - @Nullable public String getProviderType() { return mProviderId; } + /** + * Get the email used to sign in. + */ + public String getEmail() { + return mEmail; + } + /** * Get the token received as a result of logging in with the specified IDP */ @@ -108,14 +113,6 @@ public String getIdpSecret() { return mSecret; } - /** - * Get the email used to sign in. - */ - @Nullable - public String getEmail() { - return mEmail; - } - /** * Get the error code for a failed sign in */ @@ -157,6 +154,6 @@ public static Intent getIntent(IdpResponse response) { } public static Intent getErrorCodeIntent(int errorCode) { - return new Intent().putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, new IdpResponse(errorCode)); + return getIntent(new IdpResponse(errorCode)); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java index ce0fd85f8..806825663 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/FacebookProvider.java @@ -108,7 +108,7 @@ public void startLogin(Activity activity) { @Override public void setAuthenticationCallback(IdpCallback callback) { - this.mCallbackObject = callback; + mCallbackObject = callback; } @Override diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/TwitterProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/TwitterProvider.java index 7a4fdbce6..d8fffddbd 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/TwitterProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/TwitterProvider.java @@ -18,6 +18,8 @@ import com.twitter.sdk.android.core.TwitterSession; import com.twitter.sdk.android.core.identity.TwitterAuthClient; +import java.lang.ref.WeakReference; + import io.fabric.sdk.android.Fabric; public class TwitterProvider extends Callback implements IdpProvider { @@ -46,7 +48,7 @@ public String getProviderId() { @Override public void setAuthenticationCallback(IdpCallback callback) { - this.mCallbackObject = callback; + mCallbackObject = callback; } @Override @@ -61,7 +63,7 @@ public void startLogin(Activity activity) { @Override public void success(Result result) { - mCallbackObject.onSuccess(createIdpResponse(result.data)); + mTwitterAuthClient.requestEmail(result.data, new EmailCallback(result.data, mCallbackObject)); } @Override @@ -70,21 +72,47 @@ public void failure(TwitterException exception) { mCallbackObject.onFailure(new Bundle()); } - private IdpResponse createIdpResponse(TwitterSession twitterSession) { - return new IdpResponse( - TwitterAuthProvider.PROVIDER_ID, - null, - twitterSession.getAuthToken().token, - twitterSession.getAuthToken().secret); - } + private static class EmailCallback extends Callback { + private TwitterSession mTwitterSession; + private WeakReference mCallbackObject; + + public EmailCallback(TwitterSession session, IdpCallback callbackObject) { + mTwitterSession = session; + mCallbackObject = new WeakReference<>(callbackObject); + } + + @Override + public void success(Result emailResult) { + onSuccess(createIdpResponse(emailResult.data)); + } + @Override + public void failure(TwitterException exception) { + Log.e(TAG, "Failure retrieving Twitter email. " + exception.getMessage()); + // If retrieving the email fails, we should still be able to sign in, but Smart Lock + // and account linking won't work. + onSuccess(createIdpResponse(null)); + } + + private void onSuccess(IdpResponse response) { + if (mCallbackObject != null) { + mCallbackObject.get().onSuccess(response); + } + } + + private IdpResponse createIdpResponse(String email) { + return new IdpResponse( + TwitterAuthProvider.PROVIDER_ID, + email, + mTwitterSession.getAuthToken().token, + mTwitterSession.getAuthToken().secret); + } + } public static AuthCredential createAuthCredential(IdpResponse response) { - if (!response.getProviderType().equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)){ + if (!response.getProviderType().equalsIgnoreCase(TwitterAuthProvider.PROVIDER_ID)) { return null; } - return TwitterAuthProvider.getCredential( - response.getIdpToken(), - response.getIdpSecret()); + return TwitterAuthProvider.getCredential(response.getIdpToken(), response.getIdpSecret()); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java index 5d64d7023..c9b9947a3 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java @@ -197,9 +197,7 @@ protected void onDestroy() { } } - public static Intent createIntent( - Context context, - FlowParameters flowParams) { + public static Intent createIntent(Context context, FlowParameters flowParams) { return BaseHelper.createBaseIntent(context, AuthMethodPickerActivity.class, flowParams); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java index deda59510..3c814f26f 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandler.java @@ -69,24 +69,27 @@ public void onComplete(@NonNull Task task) { } else { if (task.getException() instanceof FirebaseAuthUserCollisionException) { final String email = mResponse.getEmail(); - mHelper.getFirebaseAuth() - .fetchProvidersForEmail(email) - .addOnFailureListener(new TaskFailureLogger( - TAG, "Error fetching providers for email")) - .addOnSuccessListener(new StartWelcomeBackFlow(email)) - .addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - // TODO: What to do when signing in with Credential fails - // and we can't continue to Welcome back flow without - // knowing providers? - } - }); + if (email != null) { + mHelper.getFirebaseAuth() + .fetchProvidersForEmail(email) + .addOnFailureListener(new TaskFailureLogger( + TAG, "Error fetching providers for email")) + .addOnSuccessListener(new StartWelcomeBackFlow(email)) + .addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + // TODO: What to do when signing in with Credential fails + // and we can't continue to Welcome back flow without + // knowing providers? + } + }); + return; + } } else { - mHelper.dismissDialog(); Log.e(TAG, "Unexpected exception when signing in with credential", task.getException()); } + mHelper.dismissDialog(); } } diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java index 410fc4236..f8bcc6763 100644 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java +++ b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java @@ -18,7 +18,7 @@ import com.google.firebase.auth.FirebaseUser; public class FakeAuthResult implements AuthResult { - FirebaseUser mFirebaseUser; + private FirebaseUser mFirebaseUser; public FakeAuthResult(FirebaseUser firebaseUser) { mFirebaseUser = firebaseUser; diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeProviderQueryResult.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeProviderQueryResult.java index 95b018e2f..b4ae17e53 100644 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeProviderQueryResult.java +++ b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeProviderQueryResult.java @@ -21,7 +21,7 @@ import java.util.List; public class FakeProviderQueryResult implements ProviderQueryResult { - List mProviders; + private List mProviders; public FakeProviderQueryResult(List providers) { mProviders = providers; diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/provider/TwitterProviderTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/provider/TwitterProviderTest.java deleted file mode 100644 index 0f294b037..000000000 --- a/auth/src/test/java/com/firebase/ui/auth/ui/provider/TwitterProviderTest.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.firebase.ui.auth.ui.provider; - -import android.os.Bundle; - -import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.provider.IdpProvider.IdpCallback; -import com.firebase.ui.auth.provider.TwitterProvider; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; -import com.firebase.ui.auth.testhelpers.FacebookProviderShadow; -import com.firebase.ui.auth.testhelpers.FirebaseAuthWrapperImplShadow; -import com.firebase.ui.auth.testhelpers.GoogleProviderShadow; -import com.twitter.sdk.android.core.Result; -import com.twitter.sdk.android.core.TwitterAuthToken; -import com.twitter.sdk.android.core.TwitterException; -import com.twitter.sdk.android.core.TwitterSession; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import java.util.concurrent.CountDownLatch; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -@RunWith(CustomRobolectricGradleTestRunner.class) -@Config(constants = BuildConfig.class, - shadows = { - FirebaseAuthWrapperImplShadow.class, - GoogleProviderShadow.class, - FacebookProviderShadow.class - }, sdk = 21) -public class TwitterProviderTest { - private static final String FAKE_AUTH_TOKEN = "fakeAuthToken"; - private static final String FAKE_AUTH_SECRET = "fakeAuthSecret"; - private static final long FAKE_USER_ID = 555; - private static final String FAKE_USER_NAME = "testAccountName"; - - private static class AssertResultCallback implements IdpCallback { - private CountDownLatch mCountDownLatch; - private boolean mAssertSuccess; - - public AssertResultCallback(boolean assertSuccess) { - mCountDownLatch = new CountDownLatch(1); - mAssertSuccess = assertSuccess; - } - - private void await() throws InterruptedException { - mCountDownLatch.await(); - } - - @Override - public void onSuccess(IdpResponse idpResponse) { - assertTrue(mAssertSuccess); - mCountDownLatch.countDown(); - } - - @Override - public void onFailure(Bundle extra) { - assertFalse(mAssertSuccess); - mCountDownLatch.countDown(); - } - } - - @Test - public void testSuccessCallsCallback() { - TwitterProvider twitterProvider = new TwitterProvider(RuntimeEnvironment.application); - - AssertResultCallback assertResultCallback = new AssertResultCallback(true); - twitterProvider.setAuthenticationCallback(assertResultCallback); - - TwitterAuthToken twitterAuthToken = new TwitterAuthToken(FAKE_AUTH_TOKEN, FAKE_AUTH_SECRET); - TwitterSession twitterSession = new TwitterSession( - twitterAuthToken, - FAKE_USER_ID, - FAKE_USER_NAME); - - Result result = new Result<>(twitterSession, null); - twitterProvider.success(result); - - try { - assertResultCallback.await(); - } catch (InterruptedException e) { - assertTrue("Interrupted waiting for result", false); - } - } - - @Test - public void testFailureCallsCallback() { - TwitterProvider twitterProvider = new TwitterProvider(RuntimeEnvironment.application); - - AssertResultCallback assertResultCallback = new AssertResultCallback(false); - twitterProvider.setAuthenticationCallback(assertResultCallback); - - TwitterException twitterException = new TwitterException("Fake exception"); - twitterProvider.failure(twitterException); - - try { - assertResultCallback.await(); - } catch (InterruptedException e) { - assertTrue("Interrupted waiting for result", false); - } - } -} diff --git a/constants.gradle b/constants.gradle index 8340dcade..717e2fcc7 100644 --- a/constants.gradle +++ b/constants.gradle @@ -1,5 +1,5 @@ project.ext.firebase_version = '10.0.1' -project.ext.support_library_version = '25.0.1' +project.ext.support_library_version = '25.1.0' project.ext.submodules = ['database', 'auth', 'storage'] project.ext.group = 'com.firebaseui'