1
\$\begingroup\$

I had this problem: https://stackoverflow.com/questions/78528358/how-to-prevent-circularprogressindicator-from-freezing-during-authentication-in

Now after fixing it, I want to make sure that this is the best approach to solve the problem for Kotlin Compose.

Google Auth:

@Composable
fun activityGoogleResultLauncher(): ActivityResultLauncher<Intent> {
    val loginViewModel = LocalUserState.current
    val startAutocomplete: ActivityResultLauncher<Intent> = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        val completedTask: Task<GoogleSignInAccount> =
            GoogleSignIn.getSignedInAccountFromIntent(result.data)
        try {
            if (completedTask.isSuccessful) {
                val account: GoogleSignInAccount? =
                    completedTask.getResult(ApiException::class.java)
                val email = account?.email
                val token: String = account?.idToken!!
                loginViewModel.singInWithGoogle(token)
                if (email != null) {
                    loginViewModel.updateEmail(email)
                }
            } else {
                println("AUTH Google Auth failed: ${completedTask.exception}")
            }
        } catch (e: ApiException) {
            println("AUTH Failed to authenticate using Google OAuth: " + e.message)
        }
    }
    return startAutocomplete
}

Facebook Auth:

LoginManager.getInstance().registerCallback(
            callbackManager,
            object : FacebookCallback<LoginResult> {
                override fun onSuccess(result: LoginResult) {
                    val accessToken = result.accessToken
                    loginViewModel.signInWithFacebookLogin(accessToken.token)
                }

                override fun onCancel() {
                    println("AUTH Cancelled Facebook login")
                    loginViewModel.loadingLogin = false
                }

                override fun onError(error: FacebookException) {
                    println("AUTH Failed to authenticate with Facebook: ${error.message}")
                    loginViewModel.loadingLogin = false
                }
            })

Login View Model:

fun singInWithGoogle(googleUserIDToken: String)
    {
        viewModelScope.launch(Dispatchers.IO) {
            repo.signInWithGoogleLoginAndroid(googleUserIDToken)
            getUserType = true
            loginStatus = "in"
            FirebaseMessaging.getInstance().token.addOnCompleteListener {
                insertFCMWhenLogin(it.result)
            }
        }
    }

    fun signInWithFacebookLogin(accessToken: String)
    {
        viewModelScope.launch(Dispatchers.IO) {
            repo.signInWithFacebookLogin(accessToken)
            getUserType = true
            loginStatus = "in"
            FirebaseMessaging.getInstance().token.addOnCompleteListener {
                insertFCMWhenLogin(it.result)
            }
        }
    }

Could you please tell me if there is a better way to do it?

\$\endgroup\$
0

1 Answer 1

2
\$\begingroup\$

First, GoogleSignIn is deprecated. You now have to use Credential Manager.

That being said...

I recommend you to move all login logic to the ViewModel or data layer:

For Google Sign in:

@Composable
fun activityGoogleResultLauncher(): ActivityResultLauncher<Intent> {
    val loginViewModel = LocalUserState.current
    val startAutocomplete: ActivityResultLauncher<Intent> = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.StartActivityForResult()
    ) { result ->
        
        loginViewModel.singInWithGoogleFromIntent(result.data)
    }
    return startAutocomplete
}

In the ViewModel, it would be like this:

fun singInWithGoogleFromIntent(intent: Intent?) {

    viewModelScope.launch(Dispatchers.IO) {
        
        if (intent == null) {

            TODO("Process error and notify UI")
            return@launch
        }

        val completedTask = GoogleSignIn.getSignedInAccountFromIntent(intent)

        try {
            if (completedTask.isSuccessful) {

                TODO("Process login and notify UI when it is finished and you have all necessary data.")

            } else {
                TODO("Process error and notify UI")
                return@launch
            }
        } catch (e: ApiException) {
            TODO("Process error and notify UI")
            return@launch
        }

        
    }
}

Reasons I recommend this:

  1. It frees UI thread from login processing. This way, you won't have lag on your UI even if the login takes some time.
  2. Once you move login processing to the ViewModel, it becomes "activity independent". This means even if your activity gets recreated, the login processing will survive, along with whatever uiState you have at that moment in your ViewModel.

In the case of Facebook sign in, you already have it like that.

If you wish to further improve this, I recommend you to move this to the data layer.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.