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
}
}
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
}
}
})
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With