Skip to content

Commit 0da111b

Browse files
SUPERCILEXsamtstern
authored andcommitted
Rank providers by usability when picking top provider (#1362)
1 parent 2274696 commit 0da111b

File tree

13 files changed

+78
-55
lines changed

13 files changed

+78
-55
lines changed

auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class FlowParameters implements Parcelable {
4040
public final String appName;
4141

4242
@NonNull
43-
public final List<IdpConfig> providerInfo;
43+
public final List<IdpConfig> providers;
4444

4545
@StyleRes
4646
public final int themeId;
@@ -60,7 +60,7 @@ public class FlowParameters implements Parcelable {
6060

6161
public FlowParameters(
6262
@NonNull String appName,
63-
@NonNull List<IdpConfig> providerInfo,
63+
@NonNull List<IdpConfig> providers,
6464
@StyleRes int themeId,
6565
@DrawableRes int logoId,
6666
@Nullable String termsOfServiceUrl,
@@ -69,8 +69,8 @@ public FlowParameters(
6969
boolean enableHints,
7070
boolean enableAnonymousUpgrade) {
7171
this.appName = Preconditions.checkNotNull(appName, "appName cannot be null");
72-
this.providerInfo = Collections.unmodifiableList(
73-
Preconditions.checkNotNull(providerInfo, "providerInfo cannot be null"));
72+
this.providers = Collections.unmodifiableList(
73+
Preconditions.checkNotNull(providers, "providers cannot be null"));
7474
this.themeId = themeId;
7575
this.logoId = logoId;
7676
this.termsOfServiceUrl = termsOfServiceUrl;
@@ -90,7 +90,7 @@ public static FlowParameters fromIntent(Intent intent) {
9090
@Override
9191
public void writeToParcel(Parcel dest, int flags) {
9292
dest.writeString(appName);
93-
dest.writeTypedList(providerInfo);
93+
dest.writeTypedList(providers);
9494
dest.writeInt(themeId);
9595
dest.writeInt(logoId);
9696
dest.writeString(termsOfServiceUrl);
@@ -137,7 +137,7 @@ public FlowParameters[] newArray(int size) {
137137
};
138138

139139
public boolean isSingleProviderFlow() {
140-
return providerInfo.size() == 1;
140+
return providers.size() == 1;
141141
}
142142

143143
public boolean isTermsOfServiceUrlProvided() {

auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public SignInKickstarter(Application application) {
5656
public void start() {
5757
// Only support password credentials if email auth is enabled
5858
boolean supportPasswords = ProviderUtils.getConfigFromIdps(
59-
getArguments().providerInfo, EmailAuthProvider.PROVIDER_ID) != null;
59+
getArguments().providers, EmailAuthProvider.PROVIDER_ID) != null;
6060
List<String> accountTypes = getCredentialAccountTypes();
6161

6262
// If the request will be empty, avoid the step entirely
@@ -97,7 +97,7 @@ public void onComplete(@NonNull Task<CredentialRequestResponse> task) {
9797
private void startAuthMethodChoice() {
9898
// If there is only one provider selected, launch the flow directly
9999
if (getArguments().isSingleProviderFlow()) {
100-
AuthUI.IdpConfig firstIdpConfig = getArguments().providerInfo.get(0);
100+
AuthUI.IdpConfig firstIdpConfig = getArguments().providers.get(0);
101101
String firstProvider = firstIdpConfig.getProviderId();
102102
switch (firstProvider) {
103103
case EmailAuthProvider.PROVIDER_ID:
@@ -157,7 +157,7 @@ private void redirectSignIn(String provider, String id) {
157157

158158
private List<String> getCredentialAccountTypes() {
159159
List<String> accounts = new ArrayList<>();
160-
for (AuthUI.IdpConfig idpConfig : getArguments().providerInfo) {
160+
for (AuthUI.IdpConfig idpConfig : getArguments().providers) {
161161
@AuthUI.SupportedProvider String providerId = idpConfig.getProviderId();
162162
if (providerId.equals(GoogleAuthProvider.PROVIDER_ID)) {
163163
accounts.add(ProviderUtils.providerIdToAccountType(providerId));

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public void fetchCredential() {
3333

3434
public void fetchProvider(final String email) {
3535
setResult(Resource.<User>forLoading());
36-
ProviderUtils.fetchTopProvider(getAuth(), email)
36+
ProviderUtils.fetchTopProvider(getAuth(), getArguments(), email)
3737
.addOnCompleteListener(new OnCompleteListener<String>() {
3838
@Override
3939
public void onComplete(@NonNull Task<String> task) {
@@ -53,7 +53,7 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d
5353
setResult(Resource.<User>forLoading());
5454
final Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
5555
final String email = credential.getId();
56-
ProviderUtils.fetchTopProvider(getAuth(), email)
56+
ProviderUtils.fetchTopProvider(getAuth(), getArguments(), email)
5757
.addOnCompleteListener(new OnCompleteListener<String>() {
5858
@Override
5959
public void onComplete(@NonNull Task<String> task) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void onNewUser(User user) {
111111
TextInputLayout emailLayout = findViewById(R.id.email_layout);
112112

113113
AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
114-
getFlowParams().providerInfo, EmailAuthProvider.PROVIDER_ID);
114+
getFlowParams().providers, EmailAuthProvider.PROVIDER_ID);
115115
if (emailConfig.getParams().getBoolean(ExtraConstants.ALLOW_NEW_EMAILS, true)) {
116116
RegisterEmailFragment fragment = RegisterEmailFragment.newInstance(user);
117117
FragmentTransaction ft = getSupportFragmentManager().beginTransaction()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
147147

148148
// Get configuration
149149
AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
150-
getFlowParams().providerInfo, EmailAuthProvider.PROVIDER_ID);
150+
getFlowParams().providers, EmailAuthProvider.PROVIDER_ID);
151151
boolean requireName = emailConfig.getParams()
152152
.getBoolean(ExtraConstants.REQUIRE_NAME, true);
153153
mPasswordFieldValidator = new PasswordFieldValidator(

auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
8989
mHandler = ViewModelProviders.of(this).get(SocialProviderResponseHandler.class);
9090
mHandler.init(params);
9191

92-
populateIdpList(params.providerInfo, mHandler);
92+
populateIdpList(params.providers, mHandler);
9393

9494
int logoId = params.logoId;
9595
if (logoId == AuthUI.NO_LOGO) {

auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
4747
String provider = user.getProviderId();
4848

4949
AuthUI.IdpConfig providerConfig =
50-
ProviderUtils.getConfigFromIdps(getFlowParams().providerInfo, provider);
50+
ProviderUtils.getConfigFromIdps(getFlowParams().providers, provider);
5151
if (providerConfig == null) {
5252
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
5353
ErrorCodes.DEVELOPER_ERROR,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
9898

9999
String providerId = existingUser.getProviderId();
100100
AuthUI.IdpConfig config =
101-
ProviderUtils.getConfigFromIdps(getFlowParams().providerInfo, providerId);
101+
ProviderUtils.getConfigFromIdps(getFlowParams().providers, providerId);
102102
if (config == null) {
103103
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
104104
ErrorCodes.DEVELOPER_ERROR,

auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import com.firebase.ui.auth.AuthUI;
2323
import com.firebase.ui.auth.IdpResponse;
24-
import com.firebase.ui.auth.util.Preconditions;
24+
import com.firebase.ui.auth.data.model.FlowParameters;
2525
import com.google.android.gms.auth.api.credentials.IdentityProviders;
2626
import com.google.android.gms.tasks.Continuation;
2727
import com.google.android.gms.tasks.Task;
@@ -36,6 +36,7 @@
3636
import com.google.firebase.auth.SignInMethodQueryResult;
3737
import com.google.firebase.auth.TwitterAuthProvider;
3838

39+
import java.util.ArrayList;
3940
import java.util.List;
4041

4142
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@@ -154,41 +155,63 @@ public static AuthUI.IdpConfig getConfigFromIdpsOrThrow(List<AuthUI.IdpConfig> i
154155
return config;
155156
}
156157

157-
public static Task<String> fetchTopProvider(FirebaseAuth auth, @NonNull String email) {
158+
public static Task<List<String>> fetchSortedProviders(@NonNull FirebaseAuth auth,
159+
@NonNull final FlowParameters params,
160+
@NonNull String email) {
158161
if (TextUtils.isEmpty(email)) {
159162
return Tasks.forException(new NullPointerException("Email cannot be empty"));
160163
}
161164

162165
return auth.fetchSignInMethodsForEmail(email)
163-
.continueWith(new Continuation<SignInMethodQueryResult, String>() {
166+
.continueWith(new Continuation<SignInMethodQueryResult, List<String>>() {
164167
@Override
165-
public String then(@NonNull Task<SignInMethodQueryResult> task) {
166-
if (!task.isSuccessful()) return null;
167-
168+
public List<String> then(@NonNull Task<SignInMethodQueryResult> task) {
168169
List<String> methods = task.getResult().getSignInMethods();
169-
return methods == null || methods.isEmpty()
170-
? null : methods.get(methods.size() - 1);
170+
if (methods == null) { methods = new ArrayList<>(); }
171+
172+
List<String> allowedProviders = new ArrayList<>(params.providers.size());
173+
for (AuthUI.IdpConfig provider : params.providers) {
174+
allowedProviders.add(provider.getProviderId());
175+
}
176+
177+
List<String> lastSignedInProviders = new ArrayList<>(methods.size());
178+
for (String method : methods) {
179+
String id = signInMethodToProviderId(method);
180+
if (allowedProviders.contains(id)) {
181+
lastSignedInProviders.add(0, id);
182+
}
183+
}
184+
185+
// Reorder providers from most to least usable. Usability is determined by
186+
// how many steps a user needs to perform to log in.
187+
maximizePriority(lastSignedInProviders, GoogleAuthProvider.PROVIDER_ID);
188+
189+
return lastSignedInProviders;
190+
}
191+
192+
private void maximizePriority(List<String> providers, String id) {
193+
if (providers.remove(id)) { providers.add(0, id); }
171194
}
172-
}).continueWith(new Continuation<String, String>() {
195+
});
196+
}
197+
198+
public static Task<String> fetchTopProvider(
199+
@NonNull FirebaseAuth auth,
200+
@NonNull FlowParameters params,
201+
@NonNull String email) {
202+
return fetchSortedProviders(auth, params, email)
203+
.continueWith(new Continuation<List<String>, String>() {
173204
@Override
174-
public String then(@NonNull Task<String> task) {
175-
String method = task.getResult();
176-
if (method == null) {
205+
public String then(@NonNull Task<List<String>> task) {
206+
if (!task.isSuccessful()) return null;
207+
List<String> providers = task.getResult();
208+
209+
if (providers.isEmpty()) {
177210
return null;
178211
} else {
179-
return signInMethodToProviderId(method);
212+
return providers.get(0);
180213
}
181214
}
182215
});
183216
}
184-
185-
public static String getTopProvider(@NonNull List<String> providers) {
186-
return providers == null || providers.isEmpty() ? null :
187-
providers.get(providers.size() - 1);
188-
}
189-
190-
public static boolean isExistingProvider(@NonNull List<String> providers, String provider) {
191-
if (providers == null) throw new IllegalArgumentException("The list of providers is null.");
192-
return providers.contains(provider);
193-
}
194217
}

auth/src/main/java/com/firebase/ui/auth/viewmodel/email/EmailProviderResponseHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public void onFailure(@NonNull Exception e) {
6969
// Collision with existing user email without anonymous upgrade
7070
// it should be very hard for the user to even get to this error
7171
// due to CheckEmailFragment.
72-
ProviderUtils.fetchTopProvider(getAuth(), email)
72+
ProviderUtils.fetchTopProvider(getAuth(), getArguments(), email)
7373
.addOnSuccessListener(new StartWelcomeBackFlow(email))
7474
.addOnFailureListener(new OnFailureListener() {
7575
@Override

0 commit comments

Comments
 (0)