Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Firebase Google SignIn with MVVM architecture?

Im building an app and Im currently facing a problem : I wrote all the events of my Google/Facebook SignIn buttons inside the LoginActivity by following the google guide. However, I don't want to keep business code inside the activity to follow MVVM architecture. Therefore how can I put the Firebase methods (which need an activity reference and lifecycle callbacks) ?

I've tried a LoginViewModel which is working BUT it has references to LoginActivity

class LoginViewModel(application: Application) : BaseViewModelContext(application) {

    lateinit var auth: FirebaseAuth
    val Tag: String = LoginViewModel::class.java.simpleName;
    val gso: GoogleSignInOptions
    val googleSignInClient: GoogleSignInClient

    init {
        auth = FirebaseAuth.getInstance()
        gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestIdToken(application.getString(R.string.default_client_id))
            .build()

        googleSignInClient = GoogleSignIn.getClient(application, gso)
    }



    fun activityForResultLogin(requestCode:Int, resultCode: Int, data: Intent?, activity: Activity){
        if(requestCode == Constants.RC_SIGN_IN){
            val task = GoogleSignIn.getSignedInAccountFromIntent(data)
            try {
                // Google Sign In was successful, authenticate with Firebase
                val account = task.getResult(ApiException::class.java)
                firebaseAuthWithGoogle(account!!, activity)
            } catch (e: ApiException) {
                // Google Sign In failed, update UI appropriately
                Log.w(Tag, "Google sign in failed", e)
                // ...
            }
        }
    }

    private fun firebaseAuthWithGoogle(acct: GoogleSignInAccount, activity: Activity) {
        Log.d(Tag, "firebaseAuthWithGoogle:" + acct.id!!)

        val credential = GoogleAuthProvider.getCredential(acct.idToken, null)
        auth.signInWithCredential(credential)
            .addOnCompleteListener(activity) { task ->
                if (task.isSuccessful) {
                    // Sign in success, update UI with the signed-in user's information
                    Log.d(Tag, "signInWithCredential:success")
                    val user = auth.currentUser
                    activity.shortToast("SignInSuccess")
                } else {
                    // If sign in fails, display a message to the user.
                    Log.w(Tag, "signInWithCredential:failure", task.exception)
                    activity.shortToast("SignInFailed")
                }

            }
    }

    fun signInGoogle(activity: Activity){
        val signInIntent = googleSignInClient.signInIntent
        activity.startActivityForResult(signInIntent, Constants.RC_SIGN_IN)
    }

    fun signOutGoogle(activity: Activity){
        googleSignInClient.signOut().addOnCompleteListener(activity){
            //Update ui
        }
    }
like image 692
Adama Traore Avatar asked Jun 05 '19 09:06

Adama Traore


1 Answers

Here is what I have done.

create user model:

data class User(
    val uid: String,
    val name: String?,
    val email: String?,
    var isAuthenticated: Boolean = false,
    var isNew: Boolean? = false,
    var isCreated: Boolean = false

) : Serializable{

    constructor() : this("","","") {

    }

}

create AuthRepository class:

class AuthRepository {

    private val firebaseAuth: FirebaseAuth = FirebaseAuth.getInstance()
    private val rootRef: FirebaseFirestore = FirebaseFirestore.getInstance()
    private val usersRef: CollectionReference = rootRef.collection(USERS)

    // Sign in using google
    fun firebaseSignInWithGoogle(googleAuthCredential: AuthCredential): MutableLiveData<ResponseState<User>> {
        val authenticatedUserMutableLiveData: MutableLiveData<ResponseState<User>> =
            MutableLiveData()
       
         firebaseAuth.signInWithCredential(googleAuthCredential).addOnCompleteListener { authTask ->
            if (authTask.isSuccessful) {
                var isNewUser = authTask.result?.additionalUserInfo?.isNewUser
                val firebaseUser: FirebaseUser? = firebaseAuth.currentUser
                if (firebaseUser != null) {
                    val uid = firebaseUser.uid
                    val name = firebaseUser.displayName
                    val email = firebaseUser.email
                    val user = User(uid = uid, name = name, email = email)
                    user.isNew = isNewUser
                    authenticatedUserMutableLiveData.value = ResponseState.Success(user)

                }


            } else {

                authenticatedUserMutableLiveData.value = authTask.exception?.message?.let {
                    ResponseState.Error(it)
                }

            }


        }
        return authenticatedUserMutableLiveData
    }

}

create LoginViewModel class:

class LoginViewModel constructor(private val authRepository: AuthRepository) :
    ViewModel() {

    private var _authenticateUserLiveData: MutableLiveData<ResponseState<User>> = MutableLiveData()
  val authenticateUserLiveData: LiveData<ResponseState<User>> get() = _authenticateUserLiveData

    fun signInWithGoogle(googleAuthCredential: AuthCredential) {
        _authenticateUserLiveData = authRepository.firebaseSignInWithGoogle(googleAuthCredential)
    }


}

here ResponseState class is as follows:

sealed class ResponseState<T>(
    val data: T? = null,
    val message: String? = null
) {


    class Success<T>(data: T) : ResponseState<T>(data)
    class Error<T>(message: String) : ResponseState<T>(message = message)
    class Loading<T> : ResponseState<T>()

}

Inside your activity or fragment you can use this as :

lateinit var googleSignInClient: GoogleSignInClient
private fun initGoogleSignInClient() {
    val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(getString(R.string.default_web_client_id))
        .requestEmail()
        .build()

    googleSignInClient = GoogleSignIn.getClient(this, gso)
}

private fun signInUsingGoolge() {
    val signInGoogleIntent = (activity as MainActivity).googleSignInClient.signInIntent
    startActivityForResult(signInGoogleIntent, RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent ? ) {
    super.onActivityResult(requestCode, resultCode, data)

    if (requestCode == RC_SIGN_IN) {
        val task = GoogleSignIn.getSignedInAccountFromIntent(data)
        try {
            // Google Sign In was successful, authenticate with Firebase
            val account = task.getResult(ApiException::class.java) !!
                if (account != null) {
                    getGoogleAuthCredential(account)
                }

        } catch (e: ApiException) {
            // Google Sign In failed, update UI appropriately
        }
    }
}

private fun getGoogleAuthCredential(account: GoogleSignInAccount) {
    binding.progressBar.visible()
    val googleTokeId = account.idToken
    val googleAuthCredential = GoogleAuthProvider.getCredential(googleTokeId, null)
    signInWithGoogleAuthCredential(googleAuthCredential)
}

private fun signInWithGoogleAuthCredential(googleAuthCredential: AuthCredential) {

    loginViewModel.signInWithGoogle(googleAuthCredential)
    loginViewModel.authenticateUserLiveData.observe(viewLifecycleOwner, {
        authenticatedUser - >
        when(authenticatedUser) {
            is ResponseState.Error - > {
                authenticatedUser.message ? .let {
                    context ? .toast(it)
                }
            }
            is ResponseState.Success - > {
                if (authenticatedUser.data != null)
                //update ui
            }
            is ResponseState.Loading - > {
                //show progress
            }
        }
    })

} 
      
like image 123
Pranav Choudhary Avatar answered Nov 11 '22 23:11

Pranav Choudhary