-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Completely rewrite provider login with new architecture #1189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
@@ -0,0 +1,153 @@ | |||
package com.firebase.ui.auth.data.remote; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO(me): add tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think just for review sanity you should do the tests in another PR. This one is already 1k+ lines and tests will probably double it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM for the individual provider handlers since they work AFAIK, but I've actually found a bunch of issues with my ProvidersHandler
implementation thanks to some lovely TDD (so I'll just add that one for now).
@samtstern This is going to be the biggest PR by far since it requires major refactors across a large portion of the FUI API surface. The basic ideas are as follows:
|
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
@SUPERCILEX well this is up next, after merges are resolved. Right? |
# Conflicts: # auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java # auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java # auth/src/main/java/com/firebase/ui/auth/util/signincontainer/IdpSignInContainer.java # auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java # auth/src/main/java/com/firebase/ui/auth/viewmodel/AuthViewModelBase.java # auth/src/main/java/com/firebase/ui/auth/viewmodel/smartlock/SmartLockHandler.java
@samtstern Yep, I'm working on tests right now. |
Signed-off-by: Alex Saveau <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started reviewing but then i got pulled away into something else. Sending the comments I had so far, will probably pick this up on Monday.
@@ -276,7 +276,7 @@ public static int getDefaultTheme() { | |||
.disableAutoSignIn() | |||
.continueWithTask(new Continuation<Void, Task<Void>>() { | |||
@Override | |||
public Task<Void> then(@NonNull Task<Void> task) throws Exception { | |||
public Task<Void> then(@NonNull Task<Void> task) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did some linter tell you not to have throws Exception
here? Does it actually change anything to remove it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, IntelliJ has been removing these, not sure why, but it doesn't seem to matter.
import com.firebase.ui.auth.viewmodel.idp.ProviderHandler; | ||
import com.firebase.ui.auth.viewmodel.idp.ProvidersHandler; | ||
|
||
public final class FacebookParams extends ProviderHandler.ParamsBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RestrictTo
? Also class comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm actually hoping to get rid of these. They're in a separate class because the compiler won't let me make them a static inner...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems extracting the base class fixes it.
import com.firebase.ui.auth.viewmodel.idp.ProviderHandler; | ||
import com.firebase.ui.auth.viewmodel.idp.ProvidersHandler; | ||
|
||
public final class GoogleParams extends ProviderHandler.ParamsBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here and on all other new classes:
- Double-check visibility restrictions.
- Add class comment.
GoogleSignIn.getClient(getApplication(), getSignInOptions()).getSignInIntent())); | ||
} | ||
|
||
public LiveData<Resource<Intent>> getRequest() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason why this is a Resource
? It only ever seems to be in the SUCCESS
state (or null
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm using it for the isUsed()
check, do you think we should extract it into a seperate, contained class? The main pain-point is that I'm trying to keep the previous behavior which included starting the sign-in process from onActivityResult
if it's an invalid account.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be a Resource<Void>
with the Intent
being part of an error like in other cases then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, why not? It seems a little better than what I have.
.getResult(ApiException.class); | ||
setResult(createIdpResponse(account)); | ||
} catch (ApiException e) { | ||
if (e.getStatusCode() == CommonStatusCodes.INVALID_ACCOUNT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This took me a minute to understand, so maybe add:
// If we get INVALID_ACCOUNT it means the pre-set account was not available on the device, set email to null
// and try to launch sign-in again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
import com.firebase.ui.auth.data.model.FlowParameters; | ||
import com.firebase.ui.auth.ui.email.EmailActivity; | ||
|
||
public class EmailProvider implements Provider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are deleting a formerly public
class. Should this have always been annotated with @RestrictTo
or this is a real breaking change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment for all providers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was from before we used the restrict annotations, but it's always been considered private API AFAIK:
FirebaseUI-Android/auth/src/main/java/com/firebase/ui/auth/provider/package-info.java
Lines 15 to 18 in a381e13
/** | |
* IDP-specific interactions for signing in users. The contents of this package should be considered | |
* an implementation detail and not part of the main API. | |
*/ |
Also, we just broke people so these should really be marked as restricted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM let's just mark them all as restricted now
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
…e provider Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
@samtstern Alrighty! Hope you had a great weekend, this PR is officially no longer a WIP. 👏 I've tidied a bunch of stuff up, finished implementing everything that needed to get done, and thoroughly tested everything until all those 🐛 were 💥 into oblivion. The next PR is only a few hundred lines of code so once we get passed this, we'll be on a roll! 🥂 (My poor attempt at a motivational speech and apology for making this PR so big. 😆) |
Thanks for the great feedback, I don't mind at all! 😊 I'll address your flow questions here. Things should hopefully seem much clearer now:
I'd recommend looking at these classes from a package perspective:
Once everything clicks, I think you'll appreciate the clean separation we have rockin! 😁 |
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
@@ -357,22 +359,12 @@ public static int getDefaultTheme() { | |||
} | |||
|
|||
private Task<Void> signOutIdps(@NonNull Context context) { | |||
try { | |||
if (FacebookSignInHandler.IS_AVAILABLE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is much less scary
@@ -64,20 +80,16 @@ public State getState() { | |||
|
|||
@Nullable | |||
public final Exception getException() { | |||
mUsed = true; | |||
if (mUsable) { mState = State.USED; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is sort of interesting. Previously, a Resource
was immutable. You'd listen to a stream and get a new resource every time the state changed. Now the state can change from the inside.
How does this affect the pattern of listening to LiveData<Resource>
and checking state? Is it a problem that Observer
s won't get a new callback when this happens?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, technically, the "resource" part is still immutable since the value and exception can't change. Also, the class is final so only we control the state's mutability, and for now, it can only go from SUCCESS/FAILURE
-> USED
so I don't think it'll be a problem unless we want to listen for USED
events? I'm not sure when that would ever be applicable... I think of the state as more for helping us define our conditionals rather than defining the resource—it should be focused on the data, not the state. That's my opinion though, any digressions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems OK for now ... just something we may have to change in the future.
|
||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | ||
public abstract class ProviderBase implements Provider { | ||
private final ProviderResponseHandlerBase mHandler; | ||
public abstract class ProviderBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your github comments explaining the class hierarchy were very helpful! Let's also add class-level JavaDoc comments to these new base/handler classes to explain it to future me when I inevitably confuse myself again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha, yep!
setResult(Resource.<IdpResponse>forFailure(e)); | ||
public Task<Void> then(@NonNull Task<AuthResult> task) { | ||
if (mRequestedSignInCredential == null) { | ||
return Tasks.forResult(null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a comment here explaining why in this case we don't care at all if the signInWithCredential
operation was successful or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, that was a mistake, thanks for catching it!
.addOnCompleteListener(new OnCompleteListener<Void>() { | ||
@Override | ||
public void onComplete(@NonNull Task<Void> task) { | ||
if (task.isSuccessful()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the way you have this set up the task is guaranteed to be successful (in the if
branch you go straight to success and in the else
branch you drop errors). Is that your intention?
I think this class would be improved with a healthy dose of comments since I am a little lost in what you're trying to achieve and why some error cases are ignored.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above, my B.
@@ -47,7 +47,6 @@ | |||
<string name="fui_error_unknown">發生不明錯誤。</string> | |||
<string name="fui_error_invalid_password">密碼不正確。</string> | |||
<string name="fui_accessibility_logo">應用程式標誌</string> | |||
<string name="fui_signed_in_with_specific_account">以 %1$s 的身分登入</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for reverting the tools
declaration, but can we just revert all changes to the strings
files? Is there some lint about an unused resource you're trying to get around?
I would much rather ignore the lint for now than have all of these strings changes cluttering up this very important PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh gotya, I'll tell lint to keep calm and let the resources be unused. 😆
return false; | ||
} | ||
|
||
protected abstract void signIn( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems duplicative to have both this method and startSignIn
? Couldn't this method just check the state and set the loading state in the base class and be annotated with @CallSuper
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I've looked at this again and stuff has changed so whatever I was doing is now uncessessary. I'm going to simplify it and remove the abstractions.
import com.firebase.ui.auth.ui.HelperActivityBase; | ||
|
||
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | ||
public abstract class ProviderBase { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this class is only abstract methods ... could it be an interface
instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I didn't notice!
} | ||
|
||
@Override | ||
public void startLogin(@NonNull HelperActivityBase activity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-
nit, but for consistency this should probably be called
startSignIn
since we use the term 'sign in' not 'log in' pretty much everywhere. -
I find it a bit strange that this class talks directly to
LoginManager
. Shouldn't all of the Facebook SDK code be contained within theFacebookSignInHandler
and this class merely acts as a pass-through?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the second part, I'm not doing that so we don't send the activity over to the view model... do you think we should?
Ok I have spent an hour staring at things and I think we can beat a little more sense into the class hierarchy still. Here are the bits that still seem somewhat off to me.
|
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Signed-off-by: Alex Saveau <[email protected]>
Thanks for the really thoughtful feedback!
Ok, what do you think of https://github.com/SUPERCILEX/FirebaseUI-Android/commit/e9470309dbfb68caf0b45216afed3f13d6abe17b? I think we've finally reached critical mass and there's nothing left to remove. 😆 |
Signed-off-by: Alex Saveau <[email protected]>
@SUPERCILEX wooo! I have to head out a bit early today but on first pass 👍 I think this is really clear now, and also more lines deleted than added! Not bad for a refactor that makes everything work better. |
Signed-off-by: Alex Saveau <[email protected]>
Alrighty, SGTM! Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
L.
G.
T.
M.
💥 🎉 🌮 💸
@SUPERCILEX thanks for all the back and forth, all of this code is really clear now (or as clear as the tangled auth mess that is the AuthUI flow will ever be!). I am going to merge this PR now, if you had any TODOs we can add them to your next one. Congrats on making this happen, get yourself a beer (or two, or orange juice, or whatever, do you). |
YASSSSSSSS! Stellar use of emojis BTW. 👌😆 You enjoy a beverage too, all of that reviewing must turn your brain into mush! 😳😊 |
Built on top of #1188
Fixes #1200, fixes #1201