Skip to content

Commit acd5384

Browse files
SUPERCILEXsamtstern
authored andcommitted
Kill SignInDelegate ☠️ (#1190)
1 parent 603343d commit acd5384

File tree

8 files changed

+279
-461
lines changed

8 files changed

+279
-461
lines changed

auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,113 @@
11
package com.firebase.ui.auth;
22

3+
import android.arch.lifecycle.Observer;
4+
import android.arch.lifecycle.ViewModelProviders;
35
import android.content.Context;
46
import android.content.DialogInterface;
57
import android.content.Intent;
68
import android.net.ConnectivityManager;
79
import android.os.Bundle;
810
import android.support.annotation.RestrictTo;
9-
import android.util.Log;
1011

1112
import com.firebase.ui.auth.data.model.FlowParameters;
13+
import com.firebase.ui.auth.data.model.Resource;
14+
import com.firebase.ui.auth.data.model.State;
15+
import com.firebase.ui.auth.data.model.UserCancellationException;
16+
import com.firebase.ui.auth.data.remote.SignInKickstarter;
1217
import com.firebase.ui.auth.ui.HelperActivityBase;
13-
import com.firebase.ui.auth.util.ExtraConstants;
1418
import com.firebase.ui.auth.util.PlayServicesHelper;
15-
import com.firebase.ui.auth.util.signincontainer.SignInDelegate;
19+
import com.firebase.ui.auth.util.ui.FlowUtils;
1620
import com.firebase.ui.auth.viewmodel.RequestCodes;
1721

1822
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1923
public class KickoffActivity extends HelperActivityBase {
20-
private static final String TAG = "KickoffActivity";
2124
private static final String IS_WAITING_FOR_PLAY_SERVICES = "is_waiting_for_play_services";
2225

26+
private SignInKickstarter mKickstarter;
2327
private boolean mIsWaitingForPlayServices = false;
2428

2529
public static Intent createIntent(Context context, FlowParameters flowParams) {
2630
return HelperActivityBase.createBaseIntent(context, KickoffActivity.class, flowParams);
2731
}
2832

2933
@Override
30-
protected void onCreate(Bundle savedInstance) {
31-
super.onCreate(savedInstance);
34+
protected void onCreate(Bundle savedInstanceState) {
35+
super.onCreate(savedInstanceState);
36+
mKickstarter = ViewModelProviders.of(this).get(SignInKickstarter.class);
37+
mKickstarter.init(getFlowParams());
38+
mKickstarter.getOperation().observe(this, new Observer<Resource<IdpResponse>>() {
39+
@Override
40+
public void onChanged(Resource<IdpResponse> resource) {
41+
if (resource.getState() == State.LOADING) {
42+
getDialogHolder().showLoadingDialog(R.string.fui_progress_dialog_loading);
43+
return;
44+
}
45+
getDialogHolder().dismissDialog();
3246

33-
if (savedInstance == null || savedInstance.getBoolean(IS_WAITING_FOR_PLAY_SERVICES)) {
34-
if (isOffline()) {
35-
Log.d(TAG, "No network connection");
36-
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(
37-
new FirebaseUiException(ErrorCodes.NO_NETWORK)));
38-
return;
47+
if (resource.getState() == State.SUCCESS) {
48+
finish(RESULT_OK, resource.getValue().toIntent());
49+
} else if (resource.getState() == State.FAILURE) {
50+
Exception e = resource.getException();
51+
if (!FlowUtils.handleError(KickoffActivity.this, e)) {
52+
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(e));
53+
} else if (e instanceof UserCancellationException) {
54+
finish(RESULT_CANCELED, null);
55+
}
56+
}
3957
}
58+
});
4059

41-
boolean isPlayServicesAvailable = PlayServicesHelper.makePlayServicesAvailable(
42-
this,
43-
RequestCodes.PLAY_SERVICES_CHECK,
44-
new DialogInterface.OnCancelListener() {
45-
@Override
46-
public void onCancel(DialogInterface dialog) {
47-
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(
48-
new FirebaseUiException(ErrorCodes.PLAY_SERVICES_UPDATE_CANCELLED)));
49-
}
50-
});
60+
if (savedInstanceState == null || savedInstanceState.getBoolean(IS_WAITING_FOR_PLAY_SERVICES)) {
61+
init();
62+
}
63+
}
5164

52-
if (isPlayServicesAvailable) {
53-
start();
54-
} else {
55-
mIsWaitingForPlayServices = true;
56-
}
65+
private void init() {
66+
if (isOffline()) {
67+
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(
68+
new FirebaseUiException(ErrorCodes.NO_NETWORK)));
69+
return;
70+
}
71+
72+
boolean isPlayServicesAvailable = PlayServicesHelper.makePlayServicesAvailable(
73+
this,
74+
RequestCodes.PLAY_SERVICES_CHECK,
75+
new DialogInterface.OnCancelListener() {
76+
@Override
77+
public void onCancel(DialogInterface dialog) {
78+
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(
79+
new FirebaseUiException(ErrorCodes.PLAY_SERVICES_UPDATE_CANCELLED)));
80+
}
81+
});
82+
83+
if (isPlayServicesAvailable) {
84+
mKickstarter.start();
85+
} else {
86+
mIsWaitingForPlayServices = true;
5787
}
5888
}
5989

6090
@Override
6191
public void onSaveInstanceState(Bundle outState) {
62-
// It doesn't matter what we put here, we just don't want outState to be empty
63-
outState.putBoolean(ExtraConstants.HAS_EXISTING_INSTANCE, true);
64-
outState.putBoolean(IS_WAITING_FOR_PLAY_SERVICES, mIsWaitingForPlayServices);
6592
super.onSaveInstanceState(outState);
93+
outState.putBoolean(IS_WAITING_FOR_PLAY_SERVICES, mIsWaitingForPlayServices);
6694
}
6795

6896
@Override
6997
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
7098
super.onActivityResult(requestCode, resultCode, data);
7199
if (requestCode == RequestCodes.PLAY_SERVICES_CHECK) {
72100
if (resultCode == RESULT_OK) {
73-
start();
101+
mKickstarter.start();
74102
} else {
75103
finish(RESULT_CANCELED, IdpResponse.getErrorIntent(
76104
new FirebaseUiException(ErrorCodes.PLAY_SERVICES_UPDATE_CANCELLED)));
77105
}
78106
} else {
79-
SignInDelegate delegate = SignInDelegate.getInstance(this);
80-
if (delegate != null) delegate.onActivityResult(requestCode, resultCode, data);
107+
mKickstarter.onActivityResult(requestCode, resultCode, data);
81108
}
82109
}
83110

84-
private void start() {
85-
FlowParameters flowParams = getFlowParams();
86-
SignInDelegate.delegate(this, flowParams);
87-
}
88-
89111
/**
90112
* Check if there is an active or soon-to-be-active network connection.
91113
*

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,16 +91,6 @@ public static FlowParameters fromBundle(Bundle bundle) {
9191
return bundle.getParcelable(ExtraConstants.EXTRA_FLOW_PARAMS);
9292
}
9393

94-
/**
95-
* Create a bundle containing this FlowParameters object as {@link
96-
* ExtraConstants#EXTRA_FLOW_PARAMS}.
97-
*/
98-
public Bundle toBundle() {
99-
Bundle bundle = new Bundle();
100-
bundle.putParcelable(ExtraConstants.EXTRA_FLOW_PARAMS, this);
101-
return bundle;
102-
}
103-
10494
@Override
10595
public void writeToParcel(Parcel dest, int flags) {
10696
dest.writeString(appName);
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
package com.firebase.ui.auth.data.remote;
2+
3+
import android.app.Activity;
4+
import android.app.Application;
5+
import android.content.Intent;
6+
import android.support.annotation.NonNull;
7+
import android.support.annotation.Nullable;
8+
import android.text.TextUtils;
9+
10+
import com.firebase.ui.auth.AuthUI;
11+
import com.firebase.ui.auth.IdpResponse;
12+
import com.firebase.ui.auth.data.model.IntentRequiredException;
13+
import com.firebase.ui.auth.data.model.PendingIntentRequiredException;
14+
import com.firebase.ui.auth.data.model.Resource;
15+
import com.firebase.ui.auth.data.model.User;
16+
import com.firebase.ui.auth.data.model.UserCancellationException;
17+
import com.firebase.ui.auth.ui.email.EmailActivity;
18+
import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity;
19+
import com.firebase.ui.auth.ui.idp.SingleSignInActivity;
20+
import com.firebase.ui.auth.ui.phone.PhoneActivity;
21+
import com.firebase.ui.auth.util.GoogleApiUtils;
22+
import com.firebase.ui.auth.util.data.ProviderUtils;
23+
import com.firebase.ui.auth.viewmodel.AuthViewModelBase;
24+
import com.firebase.ui.auth.viewmodel.RequestCodes;
25+
import com.google.android.gms.auth.api.credentials.Credential;
26+
import com.google.android.gms.auth.api.credentials.CredentialRequest;
27+
import com.google.android.gms.auth.api.credentials.CredentialRequestResponse;
28+
import com.google.android.gms.common.api.ApiException;
29+
import com.google.android.gms.common.api.CommonStatusCodes;
30+
import com.google.android.gms.common.api.ResolvableApiException;
31+
import com.google.android.gms.tasks.OnCompleteListener;
32+
import com.google.android.gms.tasks.OnFailureListener;
33+
import com.google.android.gms.tasks.OnSuccessListener;
34+
import com.google.android.gms.tasks.Task;
35+
import com.google.firebase.auth.AuthResult;
36+
import com.google.firebase.auth.EmailAuthProvider;
37+
import com.google.firebase.auth.FacebookAuthProvider;
38+
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
39+
import com.google.firebase.auth.FirebaseAuthInvalidUserException;
40+
import com.google.firebase.auth.GoogleAuthProvider;
41+
import com.google.firebase.auth.PhoneAuthProvider;
42+
import com.google.firebase.auth.TwitterAuthProvider;
43+
44+
import java.util.ArrayList;
45+
import java.util.List;
46+
47+
public class SignInKickstarter extends AuthViewModelBase<IdpResponse> {
48+
public SignInKickstarter(Application application) {
49+
super(application);
50+
}
51+
52+
public void start() {
53+
// Only support password credentials if email auth is enabled
54+
boolean supportPasswords = ProviderUtils.getConfigFromIdps(
55+
getArguments().providerInfo, EmailAuthProvider.PROVIDER_ID) != null;
56+
List<String> accountTypes = getCredentialAccountTypes();
57+
58+
// If the request will be empty, avoid the step entirely
59+
boolean willRequestCredentials = supportPasswords || accountTypes.size() > 0;
60+
61+
if (getArguments().enableCredentials && willRequestCredentials) {
62+
setResult(Resource.<IdpResponse>forLoading());
63+
64+
GoogleApiUtils.getCredentialsClient(getApplication())
65+
.request(new CredentialRequest.Builder()
66+
.setPasswordLoginSupported(supportPasswords)
67+
.setAccountTypes(accountTypes.toArray(new String[accountTypes.size()]))
68+
.build())
69+
.addOnCompleteListener(new OnCompleteListener<CredentialRequestResponse>() {
70+
@Override
71+
public void onComplete(@NonNull Task<CredentialRequestResponse> task) {
72+
try {
73+
handleCredential(
74+
task.getResult(ApiException.class).getCredential());
75+
} catch (ResolvableApiException e) {
76+
if (e.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) {
77+
setResult(Resource.<IdpResponse>forUsableFailure(
78+
new PendingIntentRequiredException(
79+
e.getResolution(), RequestCodes.CRED_HINT)));
80+
} else {
81+
startAuthMethodChoice();
82+
}
83+
} catch (ApiException e) {
84+
startAuthMethodChoice();
85+
}
86+
}
87+
});
88+
} else {
89+
startAuthMethodChoice();
90+
}
91+
}
92+
93+
private void startAuthMethodChoice() {
94+
List<AuthUI.IdpConfig> idpConfigs = getArguments().providerInfo;
95+
96+
// If there is only one provider selected, launch the flow directly
97+
if (idpConfigs.size() == 1) {
98+
AuthUI.IdpConfig firstIdpConfig = idpConfigs.get(0);
99+
String firstProvider = firstIdpConfig.getProviderId();
100+
switch (firstProvider) {
101+
case EmailAuthProvider.PROVIDER_ID:
102+
setResult(Resource.<IdpResponse>forUsableFailure(new IntentRequiredException(
103+
EmailActivity.createIntent(getApplication(), getArguments()),
104+
RequestCodes.EMAIL_FLOW)));
105+
break;
106+
case PhoneAuthProvider.PROVIDER_ID:
107+
setResult(Resource.<IdpResponse>forUsableFailure(new IntentRequiredException(
108+
PhoneActivity.createIntent(
109+
getApplication(), getArguments(), firstIdpConfig.getParams()),
110+
RequestCodes.PHONE_FLOW)));
111+
break;
112+
default:
113+
redirectSignIn(firstProvider, null);
114+
break;
115+
}
116+
} else {
117+
setResult(Resource.<IdpResponse>forUsableFailure(new IntentRequiredException(
118+
AuthMethodPickerActivity.createIntent(getApplication(), getArguments()),
119+
RequestCodes.AUTH_PICKER_FLOW)));
120+
}
121+
}
122+
123+
private void redirectSignIn(String provider, String email) {
124+
switch (provider) {
125+
case EmailAuthProvider.PROVIDER_ID:
126+
setResult(Resource.<IdpResponse>forUsableFailure(new IntentRequiredException(
127+
EmailActivity.createIntent(getApplication(), getArguments(), email),
128+
RequestCodes.EMAIL_FLOW)));
129+
break;
130+
case GoogleAuthProvider.PROVIDER_ID:
131+
case FacebookAuthProvider.PROVIDER_ID:
132+
case TwitterAuthProvider.PROVIDER_ID:
133+
setResult(Resource.<IdpResponse>forUsableFailure(new IntentRequiredException(
134+
SingleSignInActivity.createIntent(
135+
getApplication(),
136+
getArguments(),
137+
new User.Builder(provider, email).build()),
138+
RequestCodes.PROVIDER_FLOW)));
139+
break;
140+
default:
141+
startAuthMethodChoice();
142+
}
143+
}
144+
145+
private List<String> getCredentialAccountTypes() {
146+
List<String> accounts = new ArrayList<>();
147+
for (AuthUI.IdpConfig idpConfig : getArguments().providerInfo) {
148+
@AuthUI.SupportedProvider String providerId = idpConfig.getProviderId();
149+
if (providerId.equals(GoogleAuthProvider.PROVIDER_ID)) {
150+
accounts.add(ProviderUtils.providerIdToAccountType(providerId));
151+
}
152+
}
153+
return accounts;
154+
}
155+
156+
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
157+
switch (requestCode) {
158+
case RequestCodes.CRED_HINT:
159+
if (resultCode == Activity.RESULT_OK) {
160+
handleCredential((Credential) data.getParcelableExtra(Credential.EXTRA_KEY));
161+
} else {
162+
startAuthMethodChoice();
163+
}
164+
break;
165+
case RequestCodes.AUTH_PICKER_FLOW:
166+
case RequestCodes.EMAIL_FLOW:
167+
case RequestCodes.PHONE_FLOW:
168+
case RequestCodes.PROVIDER_FLOW:
169+
IdpResponse response = IdpResponse.fromResultIntent(data);
170+
if (response == null) {
171+
setResult(Resource.<IdpResponse>forFailure(new UserCancellationException()));
172+
} else if (response.isSuccessful()) {
173+
setResult(Resource.forSuccess(response));
174+
} else {
175+
setResult(Resource.<IdpResponse>forFailure(response.getError()));
176+
}
177+
}
178+
}
179+
180+
private void handleCredential(final Credential credential) {
181+
String id = credential.getId();
182+
String password = credential.getPassword();
183+
if (TextUtils.isEmpty(password)) {
184+
String identity = credential.getAccountType();
185+
if (identity == null) {
186+
startAuthMethodChoice();
187+
} else {
188+
redirectSignIn(
189+
ProviderUtils.accountTypeToProviderId(credential.getAccountType()), id);
190+
}
191+
} else {
192+
final IdpResponse response = new IdpResponse.Builder(
193+
new User.Builder(EmailAuthProvider.PROVIDER_ID, id).build()).build();
194+
195+
getAuth().signInWithEmailAndPassword(id, password)
196+
.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
197+
@Override
198+
public void onSuccess(AuthResult result) {
199+
setResult(Resource.forSuccess(response));
200+
}
201+
})
202+
.addOnFailureListener(new OnFailureListener() {
203+
@Override
204+
public void onFailure(@NonNull Exception e) {
205+
if (e instanceof FirebaseAuthInvalidUserException
206+
|| e instanceof FirebaseAuthInvalidCredentialsException) {
207+
// In this case the credential saved in SmartLock was not
208+
// a valid credential, we should delete it from SmartLock
209+
// before continuing.
210+
GoogleApiUtils.getCredentialsClient(getApplication())
211+
.delete(credential);
212+
}
213+
startAuthMethodChoice();
214+
}
215+
});
216+
}
217+
}
218+
}

0 commit comments

Comments
 (0)