Skip to content

Commit dbcfb82

Browse files
committed
refactor(facebook): move Firebase calls to ViewModel
1 parent e8386de commit dbcfb82

File tree

2 files changed

+122
-66
lines changed

2 files changed

+122
-66
lines changed

auth/app/src/main/java/com/google/firebase/quickstart/auth/kotlin/FacebookLoginFragment.kt

Lines changed: 23 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@ import android.util.Log
55
import android.view.LayoutInflater
66
import android.view.View
77
import android.view.ViewGroup
8-
import android.widget.Toast
9-
import com.facebook.AccessToken
8+
import androidx.core.view.isGone
9+
import androidx.fragment.app.viewModels
10+
import androidx.lifecycle.Lifecycle
11+
import androidx.lifecycle.lifecycleScope
12+
import androidx.lifecycle.repeatOnLifecycle
1013
import com.facebook.CallbackManager
1114
import com.facebook.FacebookCallback
1215
import com.facebook.FacebookException
13-
import com.facebook.login.LoginManager
1416
import com.facebook.login.LoginResult
15-
import com.google.firebase.auth.FacebookAuthProvider
1617
import com.google.firebase.auth.FirebaseAuth
17-
import com.google.firebase.auth.FirebaseUser
1818
import com.google.firebase.auth.ktx.auth
1919
import com.google.firebase.ktx.Firebase
20-
import com.google.firebase.quickstart.auth.R
2120
import com.google.firebase.quickstart.auth.databinding.FragmentFacebookBinding
21+
import kotlinx.coroutines.launch
2222

2323
/**
2424
* Demonstrate Firebase Authentication using a Facebook access token.
@@ -31,7 +31,7 @@ class FacebookLoginFragment : BaseFragment() {
3131
private val binding: FragmentFacebookBinding
3232
get() = _binding!!
3333

34-
private lateinit var callbackManager: CallbackManager
34+
private val viewModel by viewModels<FacebookLoginViewModel>()
3535

3636
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
3737
_binding = FragmentFacebookBinding.inflate(layoutInflater, container, false)
@@ -42,85 +42,42 @@ class FacebookLoginFragment : BaseFragment() {
4242
super.onViewCreated(view, savedInstanceState)
4343
setProgressBar(binding.progressBar)
4444

45-
binding.buttonFacebookSignout.setOnClickListener { signOut() }
45+
binding.buttonFacebookSignout.setOnClickListener { viewModel.signOut() }
4646

4747
// Initialize Firebase Auth
4848
auth = Firebase.auth
4949

5050
// Initialize Facebook Login button
51-
callbackManager = CallbackManager.Factory.create()
51+
val callbackManager = CallbackManager.Factory.create()
5252

5353
binding.buttonFacebookLogin.setPermissions("email", "public_profile")
5454
binding.buttonFacebookLogin.registerCallback(callbackManager, object : FacebookCallback<LoginResult> {
55-
override fun onSuccess(loginResult: LoginResult) {
56-
Log.d(TAG, "facebook:onSuccess:$loginResult")
57-
handleFacebookAccessToken(loginResult.accessToken)
55+
override fun onSuccess(result: LoginResult) {
56+
Log.d(TAG, "facebook:onSuccess:$result")
57+
viewModel.handleFacebookAccessToken(result.accessToken)
5858
}
5959

6060
override fun onCancel() {
6161
Log.d(TAG, "facebook:onCancel")
62-
updateUI(null)
62+
viewModel.showInitialState()
6363
}
6464

6565
override fun onError(error: FacebookException) {
6666
Log.d(TAG, "facebook:onError", error)
67-
updateUI(null)
67+
viewModel.showInitialState()
6868
}
6969
})
70-
}
7170

72-
override fun onStart() {
73-
super.onStart()
74-
// Check if user is signed in (non-null) and update UI accordingly.
75-
val currentUser = auth.currentUser
76-
updateUI(currentUser)
77-
}
71+
lifecycleScope.launch {
72+
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
73+
viewModel.uiState.collect { uiState ->
74+
binding.status.text = uiState.status
75+
binding.detail.text = uiState.detail
7876

79-
private fun handleFacebookAccessToken(token: AccessToken) {
80-
Log.d(TAG, "handleFacebookAccessToken:$token")
81-
showProgressBar()
82-
83-
val credential = FacebookAuthProvider.getCredential(token.token)
84-
auth.signInWithCredential(credential)
85-
.addOnCompleteListener(requireActivity()) { task ->
86-
if (task.isSuccessful) {
87-
// Sign in success, update UI with the signed-in user's information
88-
Log.d(TAG, "signInWithCredential:success")
89-
val user = auth.currentUser
90-
updateUI(user)
91-
} else {
92-
// If sign in fails, display a message to the user.
93-
Log.w(TAG, "signInWithCredential:failure", task.exception)
94-
Toast.makeText(context, "Authentication failed.",
95-
Toast.LENGTH_SHORT).show()
96-
updateUI(null)
97-
}
98-
99-
hideProgressBar()
77+
binding.buttonFacebookLogin.isGone = !uiState.isSignInVisible
78+
binding.buttonFacebookSignout.isGone = uiState.isSignInVisible
10079
}
101-
}
102-
103-
fun signOut() {
104-
auth.signOut()
105-
LoginManager.getInstance().logOut()
106-
107-
updateUI(null)
108-
}
109-
110-
private fun updateUI(user: FirebaseUser?) {
111-
hideProgressBar()
112-
if (user != null) {
113-
binding.status.text = getString(R.string.facebook_status_fmt, user.displayName)
114-
binding.detail.text = getString(R.string.firebase_status_fmt, user.uid)
115-
116-
binding.buttonFacebookLogin.visibility = View.GONE
117-
binding.buttonFacebookSignout.visibility = View.VISIBLE
118-
} else {
119-
binding.status.setText(R.string.signed_out)
120-
binding.detail.text = null
121-
122-
binding.buttonFacebookLogin.visibility = View.VISIBLE
123-
binding.buttonFacebookSignout.visibility = View.GONE
80+
}
12481
}
12582
}
12683

@@ -130,6 +87,6 @@ class FacebookLoginFragment : BaseFragment() {
13087
}
13188

13289
companion object {
133-
private const val TAG = "FacebookLogin"
90+
private const val TAG = "FacebookLoginFragment"
13491
}
13592
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.google.firebase.quickstart.auth.kotlin
2+
3+
import android.util.Log
4+
import androidx.lifecycle.ViewModel
5+
import androidx.lifecycle.viewModelScope
6+
import com.facebook.AccessToken
7+
import com.facebook.login.LoginManager
8+
import com.google.firebase.auth.FacebookAuthProvider
9+
import com.google.firebase.auth.FirebaseAuth
10+
import com.google.firebase.auth.FirebaseUser
11+
import com.google.firebase.auth.ktx.auth
12+
import com.google.firebase.ktx.Firebase
13+
import kotlinx.coroutines.flow.MutableStateFlow
14+
import kotlinx.coroutines.flow.StateFlow
15+
import kotlinx.coroutines.flow.update
16+
import kotlinx.coroutines.launch
17+
import kotlinx.coroutines.tasks.await
18+
19+
class FacebookLoginViewModel(
20+
private val firebaseAuth: FirebaseAuth = Firebase.auth
21+
) : ViewModel() {
22+
private val _uiState = MutableStateFlow(UiState())
23+
val uiState: StateFlow<UiState> = _uiState
24+
25+
data class UiState(
26+
var status: String = "",
27+
var detail: String? = null,
28+
var isSignInVisible: Boolean = true,
29+
var isProgressBarVisible: Boolean = false
30+
)
31+
32+
init {
33+
// Check if user is signed in (non-null) and update UI accordingly.
34+
val firebaseUser = firebaseAuth.currentUser
35+
updateUiState(firebaseUser)
36+
}
37+
38+
fun handleFacebookAccessToken(token: AccessToken) {
39+
Log.d(TAG, "handleFacebookAccessToken:$token")
40+
toggleProgressbar(isVisible = true)
41+
42+
val credential = FacebookAuthProvider.getCredential(token.token)
43+
viewModelScope.launch {
44+
try {
45+
val authResult = firebaseAuth.signInWithCredential(credential).await()
46+
// Sign in success, update UI with the signed-in user's information
47+
Log.d(TAG, "signInWithCredential:success")
48+
updateUiState(authResult.user)
49+
} catch (e: Exception) {
50+
// If sign in fails, display a message to the user.
51+
Log.w(TAG, "signInWithCredential:failure", e)
52+
// TODO(thatfiredev): Show snackbar "Authentication failed."
53+
updateUiState(null)
54+
} finally {
55+
toggleProgressbar(isVisible = false)
56+
}
57+
}
58+
}
59+
60+
fun showInitialState() {
61+
updateUiState(null)
62+
}
63+
64+
fun signOut() {
65+
firebaseAuth.signOut()
66+
LoginManager.getInstance().logOut()
67+
updateUiState(null)
68+
}
69+
70+
fun updateUiState(user: FirebaseUser?) {
71+
if (user != null) {
72+
_uiState.update { currentUiState ->
73+
currentUiState.copy(
74+
status = "Facebook User: ${user.displayName}",
75+
detail = "Firebase UID: ${user.uid}",
76+
isSignInVisible = false,
77+
isProgressBarVisible = false
78+
)
79+
}
80+
} else {
81+
_uiState.update { currentUiState ->
82+
currentUiState.copy(
83+
status = "Signed out",
84+
detail = null,
85+
isSignInVisible = true,
86+
isProgressBarVisible = false
87+
)
88+
}
89+
}
90+
}
91+
92+
private fun toggleProgressbar(isVisible: Boolean) {
93+
_uiState.update { it.copy(isProgressBarVisible = isVisible) }
94+
}
95+
96+
companion object {
97+
const val TAG = "FacebookLoginViewModel"
98+
}
99+
}

0 commit comments

Comments
 (0)