Skip to content

Commit ff16d0d

Browse files
authored
Refactor welcome-back password flow, add ViewModel tests (#1096)
1 parent 781de12 commit ff16d0d

22 files changed

+1013
-336
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import com.firebase.ui.auth.util.AuthHelper;
1616
import com.firebase.ui.auth.util.ExtraConstants;
1717
import com.firebase.ui.auth.util.signincontainer.SaveSmartLock;
18-
import com.firebase.ui.auth.util.ui.FlowHolder;
18+
import com.firebase.ui.auth.viewmodel.FlowHolder;
1919
import com.google.firebase.auth.FirebaseUser;
2020

2121
import static com.firebase.ui.auth.util.Preconditions.checkNotNull;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.firebase.ui.auth.util.ExtraConstants;
3737
import com.firebase.ui.auth.util.ui.ImeHelper;
3838
import com.firebase.ui.auth.util.ui.fieldvalidators.EmailFieldValidator;
39+
import com.firebase.ui.auth.viewmodel.email.RecoverPasswordHandler;
3940
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
4041
import com.google.firebase.auth.FirebaseAuthInvalidUserException;
4142

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

Lines changed: 98 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -14,40 +14,41 @@
1414

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

17+
import android.arch.lifecycle.Observer;
18+
import android.arch.lifecycle.ViewModelProviders;
1719
import android.content.Context;
1820
import android.content.Intent;
21+
import android.content.IntentSender;
1922
import android.graphics.Typeface;
2023
import android.os.Bundle;
21-
import android.support.annotation.NonNull;
2224
import android.support.annotation.Nullable;
2325
import android.support.annotation.RestrictTo;
2426
import android.support.design.widget.TextInputLayout;
2527
import android.text.Spannable;
2628
import android.text.SpannableStringBuilder;
2729
import android.text.TextUtils;
2830
import android.text.style.StyleSpan;
31+
import android.util.Log;
2932
import android.view.View;
3033
import android.view.WindowManager;
3134
import android.widget.EditText;
3235
import android.widget.TextView;
3336

37+
import com.firebase.ui.auth.ErrorCodes;
3438
import com.firebase.ui.auth.IdpResponse;
3539
import com.firebase.ui.auth.R;
40+
import com.firebase.ui.auth.data.model.FirebaseUiException;
3641
import com.firebase.ui.auth.data.model.FlowParameters;
37-
import com.firebase.ui.auth.data.model.User;
38-
import com.firebase.ui.auth.data.remote.ProfileMerger;
42+
import com.firebase.ui.auth.data.model.Resource;
43+
import com.firebase.ui.auth.data.model.State;
3944
import com.firebase.ui.auth.ui.AppCompatBase;
4045
import com.firebase.ui.auth.ui.HelperActivityBase;
41-
import com.firebase.ui.auth.ui.TaskFailureLogger;
4246
import com.firebase.ui.auth.util.ExtraConstants;
4347
import com.firebase.ui.auth.util.data.ProviderUtils;
44-
import com.firebase.ui.auth.util.signincontainer.SaveSmartLock;
4548
import com.firebase.ui.auth.util.ui.ImeHelper;
46-
import com.google.android.gms.tasks.OnFailureListener;
47-
import com.google.android.gms.tasks.OnSuccessListener;
49+
import com.firebase.ui.auth.viewmodel.PendingResolution;
50+
import com.firebase.ui.auth.viewmodel.email.WelcomeBackPasswordHandler;
4851
import com.google.firebase.auth.AuthCredential;
49-
import com.google.firebase.auth.AuthResult;
50-
import com.google.firebase.auth.EmailAuthProvider;
5152

5253
/**
5354
* Activity to link a pre-existing email/password account to a new IDP sign-in by confirming the
@@ -62,8 +63,8 @@ public class WelcomeBackPasswordPrompt extends AppCompatBase
6263
private TextInputLayout mPasswordLayout;
6364
private EditText mPasswordField;
6465
private IdpResponse mIdpResponse;
65-
@Nullable
66-
private SaveSmartLock mSaveSmartLock;
66+
67+
private WelcomeBackPasswordHandler mHandler;
6768

6869
public static Intent createIntent(
6970
Context context,
@@ -81,7 +82,6 @@ protected void onCreate(Bundle savedInstanceState) {
8182
// Show keyboard
8283
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
8384

84-
mSaveSmartLock = getAuthHelper().getSaveSmartLockInstance(this);
8585
mIdpResponse = IdpResponse.fromResultIntent(getIntent());
8686
mEmail = mIdpResponse.getEmail();
8787

@@ -106,21 +106,88 @@ protected void onCreate(Bundle savedInstanceState) {
106106
// Click listeners
107107
findViewById(R.id.button_done).setOnClickListener(this);
108108
findViewById(R.id.trouble_signing_in).setOnClickListener(this);
109+
110+
// Initialize ViewModel with arguments
111+
mHandler = ViewModelProviders.of(this).get(WelcomeBackPasswordHandler.class);
112+
mHandler.init(getFlowHolder().getArguments());
113+
114+
// Fire resolutions when asked
115+
mHandler.getPendingResolution().observe(this,
116+
new Observer<PendingResolution>() {
117+
@Override
118+
public void onChanged(@Nullable PendingResolution resolution) {
119+
onPendingResolution(resolution);
120+
}
121+
});
122+
123+
// Observe the state of the main auth operation
124+
mHandler.getSignInResult().observe(this, new Observer<Resource<IdpResponse>>() {
125+
@Override
126+
public void onChanged(@Nullable Resource<IdpResponse> resource) {
127+
onAuthResult(resource);
128+
}
129+
});
109130
}
110131

111132
@Override
112-
public void onClick(View view) {
113-
final int id = view.getId();
114-
if (id == R.id.button_done) {
115-
validateAndSignIn();
116-
} else if (id == R.id.trouble_signing_in) {
117-
startActivity(RecoverPasswordActivity.createIntent(
118-
this,
119-
getFlowParams(),
120-
mEmail));
133+
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
134+
// Forward activity results to the ViewModel
135+
if (!mHandler.onActivityResult(requestCode, resultCode, data)) {
136+
super.onActivityResult(requestCode, resultCode, data);
137+
}
138+
}
139+
140+
private void onAuthResult(@Nullable Resource<IdpResponse> resource) {
141+
if (resource == null) {
142+
Log.w(TAG, "Got null resource, ignoring.");
143+
return;
144+
}
145+
146+
if (resource.getState() == State.LOADING) {
147+
getDialogHolder().showLoadingDialog(R.string.fui_progress_dialog_signing_in);
148+
}
149+
150+
if (resource.getState() == State.SUCCESS) {
151+
getDialogHolder().dismissDialog();
152+
Log.d(TAG, "onAuthResult:SUCCESS:" + resource.getValue());
153+
154+
finish(RESULT_OK, resource.getValue().toIntent());
155+
}
156+
157+
if (resource.getState() == State.FAILURE) {
158+
getDialogHolder().dismissDialog();
159+
160+
// TODO: Is this message what we want?
161+
String message = resource.getException().getLocalizedMessage();
162+
mPasswordLayout.setError(message);
121163
}
122164
}
123165

166+
private void onPendingResolution(@Nullable PendingResolution resolution) {
167+
if (resolution == null) {
168+
Log.e(TAG, "Got null resolution, can't do anything");
169+
return;
170+
}
171+
172+
try {
173+
startIntentSenderForResult(resolution.getPendingIntent().getIntentSender(),
174+
resolution.getRequestCode(), null, 0, 0, 0);
175+
} catch (IntentSender.SendIntentException e) {
176+
Log.e(TAG, "Failed to send resolution.", e);
177+
178+
IdpResponse errorResponse = IdpResponse.fromError(
179+
new FirebaseUiException(ErrorCodes.UNKNOWN_ERROR, getString(R.string.fui_general_error), e));
180+
finish(RESULT_OK, errorResponse.toIntent());
181+
}
182+
}
183+
184+
private void onForgotPasswordClicked() {
185+
startActivity(RecoverPasswordActivity.createIntent(
186+
this,
187+
getFlowParams(),
188+
mEmail));
189+
}
190+
124191
@Override
125192
public void onDonePressed() {
126193
validateAndSignIn();
@@ -138,64 +205,18 @@ private void validateAndSignIn(final String email, final String password) {
138205
} else {
139206
mPasswordLayout.setError(null);
140207
}
141-
getDialogHolder().showLoadingDialog(R.string.fui_progress_dialog_signing_in);
142208

143-
final AuthCredential authCredential = ProviderUtils.getAuthCredential(mIdpResponse);
209+
AuthCredential authCredential = ProviderUtils.getAuthCredential(mIdpResponse);
210+
mHandler.startSignIn(email, password, mIdpResponse, authCredential);
211+
}
144212

145-
final IdpResponse response;
146-
if (authCredential == null) {
147-
response = new IdpResponse.Builder(
148-
new User.Builder(EmailAuthProvider.PROVIDER_ID, email).build())
149-
.build();
150-
} else {
151-
response = new IdpResponse.Builder(mIdpResponse.getUser())
152-
.setToken(mIdpResponse.getIdpToken())
153-
.setSecret(mIdpResponse.getIdpSecret())
154-
.build();
213+
@Override
214+
public void onClick(View view) {
215+
final int id = view.getId();
216+
if (id == R.id.button_done) {
217+
validateAndSignIn();
218+
} else if (id == R.id.trouble_signing_in) {
219+
onForgotPasswordClicked();
155220
}
156-
157-
// Sign in with known email and the password provided
158-
getAuthHelper().getFirebaseAuth()
159-
.signInWithEmailAndPassword(email, password)
160-
.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
161-
@Override
162-
public void onSuccess(AuthResult authResult) {
163-
// If authCredential is null, the user only has an email account.
164-
// Otherwise, the user has an email account that we need to link to an idp.
165-
if (authCredential == null) {
166-
saveCredentialsOrFinish(
167-
mSaveSmartLock,
168-
authResult.getUser(),
169-
password,
170-
response);
171-
} else {
172-
authResult.getUser()
173-
.linkWithCredential(authCredential)
174-
.continueWithTask(new ProfileMerger(response))
175-
.addOnFailureListener(new TaskFailureLogger(
176-
TAG, "Error signing in with credential " +
177-
authCredential.getProvider()))
178-
.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
179-
@Override
180-
public void onSuccess(AuthResult authResult) {
181-
saveCredentialsOrFinish(
182-
mSaveSmartLock,
183-
authResult.getUser(),
184-
response);
185-
}
186-
});
187-
}
188-
}
189-
})
190-
.addOnFailureListener(
191-
new TaskFailureLogger(TAG, "Error signing in with email and password"))
192-
.addOnFailureListener(this, new OnFailureListener() {
193-
@Override
194-
public void onFailure(@NonNull Exception e) {
195-
getDialogHolder().dismissDialog();
196-
String error = e.getLocalizedMessage();
197-
mPasswordLayout.setError(error);
198-
}
199-
});
200221
}
201222
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package com.firebase.ui.auth.util;
2+
3+
import android.net.Uri;
4+
import android.support.annotation.NonNull;
5+
import android.support.annotation.Nullable;
6+
import android.support.annotation.RestrictTo;
7+
import android.text.TextUtils;
8+
9+
import com.firebase.ui.auth.IdpResponse;
10+
import com.firebase.ui.auth.util.data.ProviderUtils;
11+
import com.google.android.gms.auth.api.credentials.Credential;
12+
import com.google.firebase.auth.FirebaseUser;
13+
14+
/**
15+
* Utility class for working with {@link Credential} objects.
16+
*/
17+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
18+
public class CredentialsUtil {
19+
20+
/**
21+
* Build a credential for the specified {@link FirebaseUser} with optional
22+
* password and {@link IdpResponse}.
23+
*
24+
* See {@link #buildCredential(String, String, String, String, IdpResponse)}.
25+
*/
26+
public static Credential buildCredential(@NonNull FirebaseUser user,
27+
@Nullable String password,
28+
@Nullable IdpResponse idpResponse) {
29+
String name = user.getDisplayName();
30+
String email = user.getEmail();
31+
String profilePicturUri = user.getPhotoUrl() != null
32+
? user.getPhotoUrl().toString()
33+
: null;
34+
35+
return buildCredential(email, password, name, profilePicturUri, idpResponse);
36+
}
37+
38+
/**
39+
* Build the appropriate credential for the user information passed.
40+
*
41+
* If the credential cannot be built (for example, empty email) then
42+
* will return {@code null}.
43+
*/
44+
@Nullable
45+
public static Credential buildCredential(@Nullable String email,
46+
@Nullable String password,
47+
@Nullable String name,
48+
@Nullable String profilePictureUri,
49+
@Nullable IdpResponse idpResponse) {
50+
if (TextUtils.isEmpty(email)) {
51+
return null;
52+
}
53+
54+
Credential.Builder builder = new Credential.Builder(email);
55+
builder.setPassword(password);
56+
if (password == null && idpResponse != null) {
57+
String translatedProvider =
58+
ProviderUtils.providerIdToAccountType(idpResponse.getProviderType());
59+
if (translatedProvider != null) {
60+
builder.setAccountType(translatedProvider);
61+
} else {
62+
return null;
63+
}
64+
}
65+
66+
if (name != null) {
67+
builder.setName(name);
68+
}
69+
70+
if (profilePictureUri != null) {
71+
builder.setProfilePictureUri(Uri.parse(profilePictureUri));
72+
}
73+
74+
return builder.build();
75+
}
76+
77+
}

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

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)