-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Kill smartlock fragments #1124
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
Kill smartlock fragments #1124
Conversation
Change-Id: Id8ff386e4e2f3b567d351b644cd0fa8a7fb1c5be
Change-Id: Idd5aa9d75041a665273531ae76a369b27bc35e6e
Change-Id: Iae2b2cd5232dce19c2436145448cd1effd972659
Change-Id: I814b28ec989b86904c5914fbbe57345eaea7e412
Change-Id: I6d25fb985a35a8d3e090fa85e9f2d835c5e8b404
|
@SUPERCILEX ok I just got into exactly the situation you said would happen: I've got identical code in two ViewModels. How would you unify |
|
I basically had a God class that took in an Then again, do you have any better ideas? π |
Change-Id: Ibe01e7a4341e8a9c0544981a92dcebec69151b94
Change-Id: Id25fc69430ad830dfd71dcdcf0c48bfd5055b982
Change-Id: I2191225ea9db5a1efd5c68816781259c24ce1f20
Change-Id: I18817b538c556516070f1be0c956a11c93e712d2
|
@SUPERCILEX I got rid of the duplicated logic. There aren't really any new abstractions here, the view just talks to multiple view models and calls one when the other is finished. I'd like to remove all the logic from the view, but I couldn't think of a good way to do that right now. At least this continues the refactor without yet having any duplicated logic. Imo that's the minimum we can do at the moment. |
Change-Id: I447b6838e0e9f61af80d868ba252d19e1788fdc8
|
@SUPERCILEX any thoughts on the recent changes? |
|
Will take a look at this tomorrow. π |
SUPERCILEX
left a 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.
I like where this is going! My implementation failed in that it tried to have one God class do everything, so having a bunch of small, reusable ViewModels is refreshing.
Just a few comments, but I think this is good to go! π
| private final Exception mException; | ||
|
|
||
| /** | ||
| * Creates a successful Resource<Void>. |
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.
Creates a successful, empty Resource. would be clearer to read in the source code.
| * @param exception error in computing the result | ||
| */ | ||
| public Resource(@NonNull Exception exception) { | ||
| Resource(@NonNull Exception exception) { |
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.
Those static factory methods look great! Is there any reason to keep these constructors at all?
| mException = Preconditions.checkNotNull(exception, "Failure state cannot have null error."); | ||
| } | ||
|
|
||
| Resource(State state, T value, Exception exception) { |
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 this be private?
| null, 0, 0, 0); | ||
| } catch (IntentSender.SendIntentException e) { | ||
| Log.e(TAG, "STATUS: Failed to send resolution.", e); | ||
| finish(RESULT_OK, mPendingIdpResponse.toIntent()); |
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.
Because this implementation is specific to Smart Lock being failure tolerant, I feel like we should just inline it or add a comment specifying that it will finish the activity should an error occur. What do you think?
| break; | ||
| case SUCCESS: | ||
| case FAILURE: | ||
| finish(RESULT_OK, mPendingIdpResponse.toIntent()); |
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 here, this seems too specific to Smart Lock to be in HelperActivityBase π€.
|
|
||
| if (savedInstanceState != null) { | ||
| mPendingPassword = savedInstanceState.getString(ExtraConstants.EXTRA_PASSWORD, 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.
Maybe I'm not fully understanding the idea behind these pending data, but wouldn't it make more sense to keep those around in the ViewModel? That seems to be what you're trying to do... π€·ββοΈ
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.
Doh. Thank you. I still have some bad old instincts from the pre-ViewModel dark ages.
| case SUCCESS: | ||
| getDialogHolder().dismissDialog(); | ||
|
|
||
| // TODO: Should this logic be in the View? If not how do we link the view models? |
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 it makes sense to have this in the View since it's acting as the glue binding all the different UI components together: we're moving from sign-in to credential input which are two different "screens".
| import com.google.firebase.auth.FirebaseUser; | ||
|
|
||
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | ||
| public class SaveSmartLock extends SmartLockBase<Void> { |
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.
YAAAASSSSSSS! Thank you! ππππ₯π
| */ | ||
| @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) | ||
| public class SignInDelegate extends SmartLockBase<CredentialRequestResponse> { | ||
| public class SignInDelegate extends FragmentBase |
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.
Tick, tok, tick, tok, tick, tok... π
| mResultLiveData.setValue(Resource.forVoidSuccess()); | ||
| } else { | ||
| Log.e(TAG, "SAVE: Canceled by user."); | ||
| mResultLiveData.setValue(Resource.<Void>forFailure(new Exception("Save canceled by user."))); |
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 should probably use FirebaseUiException.
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.
Bump.
Change-Id: I1637e6670d6d4b7b3eab43f6d7fe4ad256a35fdf
Change-Id: I482e5a3d837bd88d85c9eb0e4299d23660c96185
Change-Id: I14a839e8acf99499de69a5d5d628adfccabe0f86
|
@SUPERCILEX thanks for taking a look. Still trying to untangle all this. You just made me think: should smartlock just actually be another view? It could be a transparent Activity that we start for result whenever the flow needs to end in a save. It seems like that would clean up everything, but want to get your thoughts before I do anything that crazy. |
|
@SUPERCILEX I prototyped something here: |
|
@samtstern Actually, yeah, #1151 makes a lot of sense: if we're going to have everything be cleanly separated by component, then Smart Lock should be in its own activity. #1151 certainly makes |
|
@SUPERCILEX ok I am going to go forward with that approach then, thanks for the sanity check. And sorry this PR is getting so big π€·ββοΈ |
|
Haha, no prob! |
Change-Id: Ibc686e40c998717eadc12c868e81857d5c71d097
Change-Id: I2fccf30aca42391d9317c33f2a65aab7ee092f7b
|
@SUPERCILEX ok this one is almost done except for some rotational issues with the Edit: now I can't get that to happen. Maybe I was running an old app version? |
SUPERCILEX
left a 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.
Almost ready to go!
| android:authorities="${applicationId}.authuiinitprovider" | ||
| android:exported="false" | ||
| android:initOrder="90" /> | ||
|
|
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.
Could you add these blank lines back?
| private Resource(State state, T value, Exception exception) { | ||
| mState = state; | ||
| mValue = value; | ||
| mException = exception; |
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: the constructor should be above the static methods.
auth/src/main/AndroidManifest.xml
Outdated
| android:label="" | ||
| android:exported="false" | ||
| android:theme="@style/FirebaseUI.Transparent"> | ||
| </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: this should just be />.
| IdpResponse response) { | ||
|
|
||
| String accountType = ProviderUtils.idpResponseToAccountType(response); | ||
| Credential credential = CredentialsUtil.buildCredential(firebaseUser, password, accountType); |
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 it makes more sense to pass in a credential indead of doing all that logic in here.
| startSaveCredentials(user, mHandler.getPendingPassword(), resource.getValue()); | ||
| break; | ||
| case FAILURE: | ||
| // TODO: Is this message what we want? |
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.
Don't we have an "incorrect password" string? We could see if the exception is an instance of the FirebaseAuthInvalidCredentialsException and use that string or else show the localized message. That's better than what we have. π
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 actually don't ... I added a TODO to make one since I don't want to add one here and get a red build for a non-translated string.
|
|
||
| // TODO: | ||
| // FirebaseUser firebaseUser = task.getResult().getUser(); | ||
| // mActivity.saveCredentialsOrFinish(firebaseUser, null, mResponse); |
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 can be removed, right?
| mResultLiveData.setValue(Resource.forVoidSuccess()); | ||
| } else { | ||
| Log.e(TAG, "SAVE: Canceled by user."); | ||
| mResultLiveData.setValue(Resource.<Void>forFailure(new Exception("Save canceled by user."))); |
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.
Bump.
Change-Id: I757fc2b42ba72d1820750e046f62912ef888f877
|
@SUPERCILEX I think I got it all, minus one |
SUPERCILEX
left a 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.
Awesome! LGTM as long as the build is green. π
WIP for #1123 π