Skip to content

Commit e26e858

Browse files
SUPERCILEXsamtstern
authored andcommitted
Improve email UX when a user makes a typo in their email (#698)
* Forward user to sign-in page * Refactor checkAccountExists into much better code * Move getTopProvider to ProviderUtils * Change exception to NPE when null email is passed in * Add continueWith implementation for fix broken tests * Cleanup * Go back to using the first provider the user singed up with
1 parent f2a78b2 commit e26e858

File tree

10 files changed

+174
-91
lines changed

10 files changed

+174
-91
lines changed

auth/src/main/java/com/firebase/ui/auth/provider/AuthCredentialHelper.java renamed to auth/src/main/java/com/firebase/ui/auth/provider/ProviderUtils.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,31 @@
1414

1515
package com.firebase.ui.auth.provider;
1616

17+
import android.support.annotation.NonNull;
1718
import android.support.annotation.Nullable;
19+
import android.text.TextUtils;
1820

1921
import com.firebase.ui.auth.IdpResponse;
22+
import com.firebase.ui.auth.ui.TaskFailureLogger;
23+
import com.google.android.gms.tasks.Continuation;
24+
import com.google.android.gms.tasks.Task;
25+
import com.google.android.gms.tasks.Tasks;
2026
import com.google.firebase.auth.AuthCredential;
2127
import com.google.firebase.auth.FacebookAuthProvider;
28+
import com.google.firebase.auth.FirebaseAuth;
2229
import com.google.firebase.auth.GoogleAuthProvider;
30+
import com.google.firebase.auth.ProviderQueryResult;
2331
import com.google.firebase.auth.TwitterAuthProvider;
2432

25-
public final class AuthCredentialHelper {
33+
import java.util.List;
34+
35+
public final class ProviderUtils {
36+
private static final String TAG = "ProviderUtils";
37+
38+
private ProviderUtils() {
39+
throw new AssertionError("No instance for you!");
40+
}
41+
2642
@Nullable
2743
public static AuthCredential getAuthCredential(IdpResponse idpResponse) {
2844
switch (idpResponse.getProviderType()) {
@@ -36,4 +52,24 @@ public static AuthCredential getAuthCredential(IdpResponse idpResponse) {
3652
return null;
3753
}
3854
}
55+
56+
public static Task<String> fetchTopProvider(FirebaseAuth auth, @NonNull String email) {
57+
if (TextUtils.isEmpty(email)) {
58+
return Tasks.forException(new NullPointerException("Email cannot be empty"));
59+
}
60+
61+
return auth.fetchProvidersForEmail(email)
62+
.addOnFailureListener(
63+
new TaskFailureLogger(TAG, "Error fetching providers for email"))
64+
.continueWith(new Continuation<ProviderQueryResult, String>() {
65+
@Override
66+
public String then(@NonNull Task<ProviderQueryResult> task) throws Exception {
67+
if (!task.isSuccessful()) return null;
68+
69+
List<String> providers = task.getResult().getProviders();
70+
return providers == null || providers.isEmpty()
71+
? null : providers.get(0);
72+
}
73+
});
74+
}
3975
}

auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import com.firebase.ui.auth.IdpResponse;
3131
import com.firebase.ui.auth.R;
3232
import com.firebase.ui.auth.ResultCodes;
33-
import com.firebase.ui.auth.provider.AuthCredentialHelper;
33+
import com.firebase.ui.auth.provider.ProviderUtils;
3434
import com.firebase.ui.auth.provider.FacebookProvider;
3535
import com.firebase.ui.auth.provider.GoogleProvider;
3636
import com.firebase.ui.auth.provider.IdpProvider;
@@ -76,7 +76,7 @@ protected void onCreate(Bundle savedInstanceState) {
7676
setContentView(R.layout.welcome_back_idp_prompt_layout);
7777

7878
IdpResponse newUserIdpResponse = IdpResponse.fromResultIntent(getIntent());
79-
mPrevCredential = AuthCredentialHelper.getAuthCredential(newUserIdpResponse);
79+
mPrevCredential = ProviderUtils.getAuthCredential(newUserIdpResponse);
8080

8181
User oldUser = User.getUser(getIntent());
8282

@@ -140,7 +140,7 @@ public void onSuccess(final IdpResponse idpResponse) {
140140
return; // do nothing
141141
}
142142

143-
AuthCredential newCredential = AuthCredentialHelper.getAuthCredential(idpResponse);
143+
AuthCredential newCredential = ProviderUtils.getAuthCredential(idpResponse);
144144
if (newCredential == null) {
145145
Log.e(TAG, "No credential returned");
146146
finish(ResultCodes.CANCELED, IdpResponse.getErrorCodeIntent(ErrorCodes.UNKNOWN_ERROR));

auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackPasswordPrompt.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import com.firebase.ui.auth.IdpResponse;
3636
import com.firebase.ui.auth.R;
3737
import com.firebase.ui.auth.ResultCodes;
38-
import com.firebase.ui.auth.provider.AuthCredentialHelper;
38+
import com.firebase.ui.auth.provider.ProviderUtils;
3939
import com.firebase.ui.auth.ui.AppCompatBase;
4040
import com.firebase.ui.auth.ui.BaseHelper;
4141
import com.firebase.ui.auth.ui.ExtraConstants;
@@ -159,7 +159,7 @@ private void validateAndSignIn(final String email, final String password) {
159159
@Override
160160
public void onSuccess(AuthResult authResult) {
161161
AuthCredential authCredential =
162-
AuthCredentialHelper.getAuthCredential(mIdpResponse);
162+
ProviderUtils.getAuthCredential(mIdpResponse);
163163

164164
// If authCredential is null, the user only has an email account.
165165
// Otherwise, the user has an email account that we need to link to an idp.

auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java

Lines changed: 39 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
import android.widget.EditText;
1818

1919
import com.firebase.ui.auth.R;
20+
import com.firebase.ui.auth.provider.ProviderUtils;
2021
import com.firebase.ui.auth.ui.ExtraConstants;
2122
import com.firebase.ui.auth.ui.FlowParameters;
2223
import com.firebase.ui.auth.ui.FragmentBase;
2324
import com.firebase.ui.auth.ui.ImeHelper;
24-
import com.firebase.ui.auth.ui.TaskFailureLogger;
2525
import com.firebase.ui.auth.ui.User;
2626
import com.firebase.ui.auth.ui.email.fieldvalidators.EmailFieldValidator;
2727
import com.firebase.ui.auth.util.GoogleApiHelper;
@@ -35,9 +35,6 @@
3535
import com.google.android.gms.tasks.OnSuccessListener;
3636
import com.google.android.gms.tasks.Task;
3737
import com.google.firebase.auth.EmailAuthProvider;
38-
import com.google.firebase.auth.ProviderQueryResult;
39-
40-
import java.util.List;
4138

4239
/**
4340
* Fragment that shows a form with an email field and checks for existing accounts with that
@@ -63,7 +60,7 @@ interface CheckEmailListener {
6360
void onExistingIdpUser(User user);
6461

6562
/**
66-
* Email entered does not beling to an existing user.
63+
* Email entered does not belong to an existing user.
6764
*/
6865
void onNewUser(User user);
6966

@@ -83,7 +80,7 @@ interface CheckEmailListener {
8380

8481
private Credential mLastCredential;
8582

86-
public static CheckEmailFragment getInstance(@NonNull FlowParameters flowParameters,
83+
public static CheckEmailFragment newInstance(@NonNull FlowParameters flowParameters,
8784
@Nullable String email) {
8885
CheckEmailFragment fragment = new CheckEmailFragment();
8986
Bundle args = new Bundle();
@@ -171,59 +168,51 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
171168
}
172169
}
173170

174-
public void validateAndProceed() {
171+
private void validateAndProceed() {
175172
String email = mEmailEditText.getText().toString();
176173
if (mEmailFieldValidator.validate(email)) {
177174
checkAccountExists(email);
178175
}
179176
}
180177

181-
public void checkAccountExists(@NonNull final String email) {
178+
private void checkAccountExists(@NonNull final String email) {
182179
mHelper.showLoadingDialog(R.string.progress_dialog_checking_accounts);
183180

184-
if (!TextUtils.isEmpty(email)) {
185-
mHelper.getFirebaseAuth()
186-
.fetchProvidersForEmail(email)
187-
.addOnFailureListener(
188-
new TaskFailureLogger(TAG, "Error fetching providers for email"))
189-
.addOnCompleteListener(
190-
getActivity(),
191-
new OnCompleteListener<ProviderQueryResult>() {
192-
@Override
193-
public void onComplete(@NonNull Task<ProviderQueryResult> task) {
194-
mHelper.dismissDialog();
195-
}
196-
})
197-
.addOnSuccessListener(
198-
getActivity(),
199-
new OnSuccessListener<ProviderQueryResult>() {
200-
@Override
201-
public void onSuccess(ProviderQueryResult result) {
202-
List<String> providers = result.getProviders();
203-
if (providers == null || providers.isEmpty()) {
204-
// Get name from SmartLock, if possible
205-
String name = null;
206-
Uri photoUri = null;
207-
if (mLastCredential != null && mLastCredential.getId().equals(email)) {
208-
name = mLastCredential.getName();
209-
photoUri = mLastCredential.getProfilePictureUri();
210-
}
211-
212-
mListener.onNewUser(new User.Builder(email)
213-
.setName(name)
214-
.setPhotoUri(photoUri)
215-
.build());
216-
} else if (EmailAuthProvider.PROVIDER_ID.equalsIgnoreCase(providers.get(0))) {
217-
mListener.onExistingEmailUser(new User.Builder(email).build());
218-
} else {
219-
mListener.onExistingIdpUser(
220-
new User.Builder(email)
221-
.setProvider(providers.get(0))
222-
.build());
223-
}
224-
}
225-
});
181+
// Get name from SmartLock, if possible
182+
String name = null;
183+
Uri photoUri = null;
184+
if (mLastCredential != null && mLastCredential.getId().equals(email)) {
185+
name = mLastCredential.getName();
186+
photoUri = mLastCredential.getProfilePictureUri();
226187
}
188+
189+
final String finalName = name;
190+
final Uri finalPhotoUri = photoUri;
191+
ProviderUtils.fetchTopProvider(mHelper.getFirebaseAuth(), email)
192+
.addOnSuccessListener(getActivity(), new OnSuccessListener<String>() {
193+
@Override
194+
public void onSuccess(String provider) {
195+
if (provider == null) {
196+
mListener.onNewUser(new User.Builder(email)
197+
.setName(finalName)
198+
.setPhotoUri(finalPhotoUri)
199+
.build());
200+
} else if (EmailAuthProvider.PROVIDER_ID.equalsIgnoreCase(provider)) {
201+
mListener.onExistingEmailUser(new User.Builder(email).build());
202+
} else {
203+
mListener.onExistingIdpUser(
204+
new User.Builder(email).setProvider(provider).build());
205+
}
206+
}
207+
})
208+
.addOnCompleteListener(
209+
getActivity(),
210+
new OnCompleteListener<String>() {
211+
@Override
212+
public void onComplete(@NonNull Task<String> task) {
213+
mHelper.dismissDialog();
214+
}
215+
});
227216
}
228217

229218
private void showEmailAutoCompleteHint() {

auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import android.support.annotation.RestrictTo;
2121
import android.support.design.widget.TextInputLayout;
2222
import android.support.v4.app.FragmentTransaction;
23-
import android.view.View;
2423

2524
import com.firebase.ui.auth.IdpResponse;
2625
import com.firebase.ui.auth.R;
@@ -42,8 +41,8 @@
4241
public class RegisterEmailActivity extends AppCompatBase implements
4342
CheckEmailFragment.CheckEmailListener {
4443

44+
public static final int RC_WELCOME_BACK_IDP = 18;
4545
private static final int RC_SIGN_IN = 17;
46-
private static final int RC_WELCOME_BACK_IDP = 18;
4746

4847
public static Intent createIntent(Context context, FlowParameters flowParams) {
4948
return createIntent(context, flowParams, null);
@@ -71,7 +70,7 @@ protected void onCreate(Bundle savedInstanceState) {
7170
}
7271

7372
// Start with check email
74-
CheckEmailFragment fragment = CheckEmailFragment.getInstance(
73+
CheckEmailFragment fragment = CheckEmailFragment.newInstance(
7574
mActivityHelper.getFlowParams(), email);
7675
getSupportFragmentManager().beginTransaction()
7776
.replace(R.id.fragment_register_email, fragment, CheckEmailFragment.TAG)
@@ -102,7 +101,8 @@ public void onExistingEmailUser(User user) {
102101
WelcomeBackPasswordPrompt.createIntent(
103102
this,
104103
mActivityHelper.getFlowParams(),
105-
new IdpResponse.Builder(EmailAuthProvider.PROVIDER_ID, user.getEmail()).build()),
104+
new IdpResponse.Builder(EmailAuthProvider.PROVIDER_ID,
105+
user.getEmail()).build()),
106106
RC_SIGN_IN);
107107

108108
setSlideAnimation();
@@ -126,22 +126,20 @@ public void onNewUser(User user) {
126126
// New user, direct them to create an account with email/password
127127
// if account creation is enabled in SignInIntentBuilder
128128

129-
boolean createAccount = mActivityHelper.getFlowParams().allowNewEmailAccounts;
129+
TextInputLayout emailLayout = (TextInputLayout) findViewById(R.id.email_layout);
130130

131-
TextInputLayout mEmailLayout = (TextInputLayout) findViewById(R.id.email_layout);
132-
133-
if (createAccount) {
134-
RegisterEmailFragment fragment = RegisterEmailFragment.getInstance(
131+
if (mActivityHelper.getFlowParams().allowNewEmailAccounts) {
132+
RegisterEmailFragment fragment = RegisterEmailFragment.newInstance(
135133
mActivityHelper.getFlowParams(),
136134
user);
137135
FragmentTransaction ft = getSupportFragmentManager().beginTransaction()
138136
.replace(R.id.fragment_register_email, fragment, RegisterEmailFragment.TAG);
139137

140-
if (mEmailLayout != null) ft.addSharedElement(mEmailLayout, "email_field");
138+
if (emailLayout != null) ft.addSharedElement(emailLayout, "email_field");
141139

142140
ft.disallowAddToBackStack().commit();
143141
} else {
144-
mEmailLayout.setError(getString(R.string.error_email_does_not_exist));
142+
emailLayout.setError(getString(R.string.error_email_does_not_exist));
145143
}
146144
}
147145

0 commit comments

Comments
 (0)