Skip to content

Commit e8386de

Browse files
committed
refactor(custom): move Firebase calls to ViewModel
1 parent 32022e3 commit e8386de

File tree

2 files changed

+112
-69
lines changed

2 files changed

+112
-69
lines changed

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

Lines changed: 31 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,30 @@ import android.util.Log
55
import android.view.LayoutInflater
66
import android.view.View
77
import android.view.ViewGroup
8-
import android.widget.Toast
98
import androidx.fragment.app.Fragment
10-
import com.google.firebase.auth.FirebaseAuth
11-
import com.google.firebase.auth.FirebaseUser
12-
import com.google.firebase.auth.ktx.auth
13-
import com.google.firebase.ktx.Firebase
14-
import com.google.firebase.quickstart.auth.R
9+
import androidx.fragment.app.viewModels
10+
import androidx.lifecycle.DefaultLifecycleObserver
11+
import androidx.lifecycle.Lifecycle
12+
import androidx.lifecycle.LifecycleOwner
13+
import androidx.lifecycle.lifecycleScope
14+
import androidx.lifecycle.repeatOnLifecycle
1515
import com.google.firebase.quickstart.auth.databinding.FragmentCustomBinding
16+
import kotlinx.coroutines.launch
1617

1718
/**
1819
* Demonstrate Firebase Authentication using a custom minted token. For more information, see:
1920
* https://firebase.google.com/docs/auth/android/custom-auth
2021
*/
2122
class CustomAuthFragment : Fragment() {
22-
23-
private lateinit var auth: FirebaseAuth
24-
2523
private var _binding: FragmentCustomBinding? = null
2624
private val binding: FragmentCustomBinding
2725
get() = _binding!!
2826

29-
private var customToken: String? = null
27+
private val viewModel by viewModels<CustomAuthViewModel>()
28+
3029
private lateinit var tokenReceiver: TokenBroadcastReceiver
3130

32-
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
31+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
3332
_binding = FragmentCustomBinding.inflate(inflater, container, false)
3433
return binding.root
3534
}
@@ -38,76 +37,39 @@ class CustomAuthFragment : Fragment() {
3837
super.onViewCreated(view, savedInstanceState)
3938

4039
// Button click listeners
41-
binding.buttonSignIn.setOnClickListener { startSignIn() }
40+
binding.buttonSignIn.setOnClickListener { viewModel.startSignIn() }
4241

4342
// Create token receiver (for demo purposes only)
4443
tokenReceiver = object : TokenBroadcastReceiver() {
4544
override fun onNewToken(token: String?) {
4645
Log.d(TAG, "onNewToken:$token")
47-
setCustomToken(token.toString())
46+
viewModel.setCustomToken(token)
4847
}
4948
}
5049

51-
// Initialize Firebase Auth
52-
auth = Firebase.auth
53-
}
54-
55-
override fun onStart() {
56-
super.onStart()
57-
// Check if user is signed in (non-null) and update UI accordingly.
58-
val currentUser = auth.currentUser
59-
updateUI(currentUser)
60-
}
61-
62-
override fun onResume() {
63-
super.onResume()
64-
requireActivity().registerReceiver(tokenReceiver, TokenBroadcastReceiver.filter)
65-
}
66-
67-
override fun onPause() {
68-
super.onPause()
69-
requireActivity().unregisterReceiver(tokenReceiver)
70-
}
71-
72-
private fun startSignIn() {
73-
// Initiate sign in with custom token
74-
customToken?.let {
75-
auth.signInWithCustomToken(it)
76-
.addOnCompleteListener(requireActivity()) { task ->
77-
if (task.isSuccessful) {
78-
// Sign in success, update UI with the signed-in user's information
79-
Log.d(TAG, "signInWithCustomToken:success")
80-
val user = auth.currentUser
81-
updateUI(user)
82-
} else {
83-
// If sign in fails, display a message to the user.
84-
Log.w(TAG, "signInWithCustomToken:failure", task.exception)
85-
Toast.makeText(context, "Authentication failed.",
86-
Toast.LENGTH_SHORT).show()
87-
updateUI(null)
88-
}
89-
}
90-
}
91-
}
50+
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
51+
override fun onResume(owner: LifecycleOwner) {
52+
super.onResume(owner)
53+
requireActivity().registerReceiver(tokenReceiver, TokenBroadcastReceiver.filter)
54+
}
9255

93-
private fun updateUI(user: FirebaseUser?) {
94-
if (user != null) {
95-
binding.textSignInStatus.text = getString(R.string.custom_auth_signin_status_user, user.uid)
96-
} else {
97-
binding.textSignInStatus.text = getString(R.string.custom_auth_signin_status_failed)
56+
override fun onPause(owner: LifecycleOwner) {
57+
super.onPause(owner)
58+
requireActivity().unregisterReceiver(tokenReceiver)
59+
}
60+
})
61+
62+
lifecycleScope.launch {
63+
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
64+
viewModel.uiState.collect { uiState ->
65+
binding.buttonSignIn.isEnabled = uiState.isSignInEnabled
66+
binding.textSignInStatus.text = uiState.signInStatus
67+
binding.textTokenStatus.text = uiState.tokenStatus
68+
}
69+
}
9870
}
9971
}
10072

101-
private fun setCustomToken(token: String) {
102-
customToken = token
103-
104-
val status = "Token:$customToken"
105-
106-
// Enable/disable sign-in button and show the token
107-
binding.buttonSignIn.isEnabled = true
108-
binding.textTokenStatus.text = status
109-
}
110-
11173
override fun onDestroyView() {
11274
super.onDestroyView()
11375
_binding = null
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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.google.firebase.auth.FirebaseAuth
7+
import com.google.firebase.auth.FirebaseUser
8+
import com.google.firebase.auth.ktx.auth
9+
import com.google.firebase.ktx.Firebase
10+
import kotlinx.coroutines.flow.MutableStateFlow
11+
import kotlinx.coroutines.flow.StateFlow
12+
import kotlinx.coroutines.flow.update
13+
import kotlinx.coroutines.launch
14+
import kotlinx.coroutines.tasks.await
15+
16+
class CustomAuthViewModel(
17+
private val firebaseAuth: FirebaseAuth = Firebase.auth
18+
) : ViewModel() {
19+
private val _uiState = MutableStateFlow(UiState())
20+
val uiState: StateFlow<UiState> = _uiState
21+
22+
private var customToken: String? = null
23+
24+
data class UiState(
25+
var signInStatus: String = "",
26+
var tokenStatus: String = "Token:null",
27+
var isSignInEnabled: Boolean = false,
28+
)
29+
30+
init {
31+
// Check if user is signed in (non-null) and update UI accordingly.
32+
val firebaseUser = firebaseAuth.currentUser
33+
updateUiState(firebaseUser)
34+
}
35+
36+
fun setCustomToken(customToken: String?) {
37+
this.customToken = customToken
38+
39+
// Enable/disable sign-in button and show the token
40+
_uiState.update { it.copy(isSignInEnabled = true, tokenStatus = "Token:$customToken") }
41+
}
42+
43+
fun startSignIn() {
44+
customToken?.let { token ->
45+
// Initiate sign in with custom token
46+
viewModelScope.launch {
47+
try {
48+
val authResult = firebaseAuth.signInWithCustomToken(token).await()
49+
// Sign in success, update UI with the signed-in user's information
50+
Log.d(TAG, "signInWithCustomToken:success")
51+
updateUiState(authResult.user)
52+
} catch (e: Exception) {
53+
// If sign in fails, display a message to the user.
54+
Log.w(TAG, "signInWithCustomToken:failure", e)
55+
// TODO(thatfiredev): Show "Authentication failed" snackbar
56+
updateUiState(null)
57+
}
58+
}
59+
}
60+
}
61+
62+
private fun updateUiState(user: FirebaseUser?) {
63+
if (user != null) {
64+
_uiState.update { currentUiState ->
65+
currentUiState.copy(
66+
signInStatus = "User ID: ${user.uid}"
67+
)
68+
}
69+
} else {
70+
_uiState.update { currentUiState ->
71+
currentUiState.copy(
72+
signInStatus = "Error: sign in failed"
73+
)
74+
}
75+
}
76+
}
77+
78+
companion object {
79+
private const val TAG = "CustomAuthViewModel"
80+
}
81+
}

0 commit comments

Comments
 (0)