Issue
I am writing a Kotlin app and using Firebase for authentication.
As onActivityResult
is now depraceted, I am trying to migrate my app to use registerForActivityResult
. I have a link to Google account feature, that starts with the Google sign-in flow, as shown here. My code:
private fun initGoogleSignInClient() =
activity?.let {
// Configure Google Sign In
val gso =
GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
// Build a GoogleSignInClient with the options specified by gso.
viewModel.googleSignInClient = GoogleSignIn.getClient(it, gso)
}
private fun showLinkWithGoogle() =
startActivityForResult(viewModel.googleSignInClient.signInIntent, RC_LINK_GOOGLE)
Where initGoogleSignInClient
is called in the fragment's onCreateView
, and showLinkWithGoogle
is called when the user taps the button on the screen. This workes perfectly.
I looked for an example using registerForActivityResult
, and the best one I found was at the bottom of this page. I added this code:
private val linkWithGoogle =
registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) {
viewModel.handleGoogleResult(it.data)
}
private fun showLinkWithGoogle() =
linkWithGoogle.launch(IntentSenderRequest.Builder(viewModel.googleSignInClient.signInIntent))
But realized that IntentSenderRequest.Builder
needs an IntentSender
and not an Intent
. I haven't found any example of how to build an IntentSender
from an Intent
, nor a way to get one from my GoogleSignInClient
.
Could anyone please provide a full example of using registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult())
?
Thank you very much!
Solution
For this use-case, you don't need an ActivityResultContracts
of type StartIntentSenderForResult
but one of type StartActivityForResult
. Here is an example (since you did not provide your full implementation):
Fragment
private val googleRegisterResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
result.checkResultAndExecute {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
val account = task.getResult(ApiException::class.java)
loginViewModel.onEvent(LoginRegistrationEvent.SignInWithGoogle(account))
}.onFailure { e -> toast("Error: ${e.message}") }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
myGoogleSignInButton.setOnClickListener {
googleRegisterResult.launch(viewModel.googleSignInClient.signInIntent)
}
}
Then, in your viewmodel, you can handle the login as you would usually do, the only difference is, that you no longer need an RC_SIGN_IN
ViewModel Example
class YourViewModel : ViewModel() {
fun onEvent(event: LoginRegistrationEvent) {
when(event) {
is LoginRegistrationEvent.SignInWithGoogle -> {
viewModelScope.launch {
val credential = GoogleAuthProvider.getCredential(event.account.idToken)
Firebase.auth.signInWithCredential(credential).await()
}
}
}
}
}
To make my life easier, I created an extension function, that checks, if the login was successful and then executes a block of code (in this case, getting the account), while caching any exceptions. Futhermore, inside your block, you have access to an instance of ActivityResult as this
:
inline fun ActivityResult.checkResultAndExecute(block: ActivityResult.() -> Unit) =
if (resultCode == Activity.RESULT_OK) runCatching(block)
else Result.failure(Exception("Something went wrong"))
Answered By - Andrew
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.