Skip to content

Commit 32022e3

Browse files
committed
refactor(email): move Firebase calls to ViewModel
1 parent 502961f commit 32022e3

File tree

2 files changed

+217
-190
lines changed

2 files changed

+217
-190
lines changed
Lines changed: 33 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,24 @@
11
package com.google.firebase.quickstart.auth.kotlin
22

3-
import android.content.Intent
43
import android.os.Bundle
5-
import android.text.TextUtils
6-
import android.util.Log
74
import android.view.LayoutInflater
85
import android.view.View
96
import android.view.ViewGroup
10-
import android.widget.Toast
11-
import androidx.core.os.bundleOf
12-
import androidx.navigation.fragment.findNavController
13-
import com.google.firebase.auth.FirebaseAuth
14-
import com.google.firebase.auth.FirebaseAuthMultiFactorException
15-
import com.google.firebase.auth.FirebaseUser
16-
import com.google.firebase.auth.ktx.auth
17-
import com.google.firebase.ktx.Firebase
18-
import com.google.firebase.quickstart.auth.R
7+
import androidx.core.view.isGone
8+
import androidx.fragment.app.viewModels
9+
import androidx.lifecycle.Lifecycle
10+
import androidx.lifecycle.lifecycleScope
11+
import androidx.lifecycle.repeatOnLifecycle
1912
import com.google.firebase.quickstart.auth.databinding.FragmentEmailpasswordBinding
13+
import kotlinx.coroutines.launch
2014

2115
class EmailPasswordFragment : BaseFragment() {
22-
23-
private lateinit var auth: FirebaseAuth
24-
2516
private var _binding: FragmentEmailpasswordBinding? = null
2617
private val binding: FragmentEmailpasswordBinding
2718
get() = _binding!!
2819

20+
private val viewModel by viewModels<EmailPasswordViewModel>()
21+
2922
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
3023
_binding = FragmentEmailpasswordBinding.inflate(inflater, container, false)
3124
return binding.root
@@ -41,200 +34,50 @@ class EmailPasswordFragment : BaseFragment() {
4134
emailSignInButton.setOnClickListener {
4235
val email = binding.fieldEmail.text.toString()
4336
val password = binding.fieldPassword.text.toString()
44-
signIn(email, password)
37+
38+
viewModel.signIn(email, password)
4539
}
4640
emailCreateAccountButton.setOnClickListener {
4741
val email = binding.fieldEmail.text.toString()
4842
val password = binding.fieldPassword.text.toString()
49-
createAccount(email, password)
50-
}
51-
signOutButton.setOnClickListener { signOut() }
52-
verifyEmailButton.setOnClickListener { sendEmailVerification() }
53-
reloadButton.setOnClickListener { reload() }
54-
}
5543

56-
// Initialize Firebase Auth
57-
auth = Firebase.auth
58-
}
59-
60-
public override fun onStart() {
61-
super.onStart()
62-
// Check if user is signed in (non-null) and update UI accordingly.
63-
val currentUser = auth.currentUser
64-
if(currentUser != null){
65-
reload();
66-
}
67-
}
68-
69-
private fun createAccount(email: String, password: String) {
70-
Log.d(TAG, "createAccount:$email")
71-
if (!validateForm()) {
72-
return
44+
viewModel.createAccount(email, password)
45+
}
46+
signOutButton.setOnClickListener { viewModel.signOut() }
47+
verifyEmailButton.setOnClickListener { viewModel.sendEmailVerification() }
48+
reloadButton.setOnClickListener { viewModel.reload() }
7349
}
7450

75-
showProgressBar()
76-
77-
auth.createUserWithEmailAndPassword(email, password)
78-
.addOnCompleteListener(requireActivity()) { task ->
79-
if (task.isSuccessful) {
80-
// Sign in success, update UI with the signed-in user's information
81-
Log.d(TAG, "createUserWithEmail:success")
82-
val user = auth.currentUser
83-
updateUI(user)
84-
} else {
85-
// If sign in fails, display a message to the user.
86-
Log.w(TAG, "createUserWithEmail:failure", task.exception)
87-
Toast.makeText(context, "Authentication failed.",
88-
Toast.LENGTH_SHORT).show()
89-
updateUI(null)
90-
}
91-
92-
hideProgressBar()
93-
}
94-
}
95-
96-
private fun signIn(email: String, password: String) {
97-
Log.d(TAG, "signIn:$email")
98-
if (!validateForm()) {
99-
return
100-
}
51+
lifecycleScope.launch {
52+
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
53+
viewModel.uiState.collect { uiState ->
54+
// Handle errors
55+
binding.fieldEmail.error = uiState.emailError
56+
binding.fieldPassword.error = uiState.passwordError
10157

102-
showProgressBar()
58+
// Display texts
59+
binding.status.text = uiState.userId
60+
binding.detail.text = uiState.userEmail
10361

104-
auth.signInWithEmailAndPassword(email, password)
105-
.addOnCompleteListener(requireActivity()) { task ->
106-
if (task.isSuccessful) {
107-
// Sign in success, update UI with the signed-in user's information
108-
Log.d(TAG, "signInWithEmail:success")
109-
val user = auth.currentUser
110-
updateUI(user)
62+
// Toggle progress bar
63+
if (uiState.isProgressBarVisible) {
64+
showProgressBar()
11165
} else {
112-
// If sign in fails, display a message to the user.
113-
Log.w(TAG, "signInWithEmail:failure", task.exception)
114-
Toast.makeText(context, "Authentication failed.",
115-
Toast.LENGTH_SHORT).show()
116-
updateUI(null)
117-
checkForMultiFactorFailure(task.exception!!)
66+
hideProgressBar()
11867
}
11968

120-
if (!task.isSuccessful) {
121-
binding.status.setText(R.string.auth_failed)
122-
}
123-
hideProgressBar()
69+
// Toggle button visibility
70+
binding.verifyEmailButton.isGone = !uiState.isVerifyEmailVisible
71+
binding.emailPasswordButtons.isGone = !uiState.isSignInEnabled
72+
binding.emailPasswordFields.isGone = !uiState.isSignInEnabled
73+
binding.signedInButtons.isGone = uiState.isSignInEnabled
12474
}
125-
}
126-
127-
private fun signOut() {
128-
auth.signOut()
129-
updateUI(null)
130-
}
131-
132-
private fun sendEmailVerification() {
133-
// Disable button
134-
binding.verifyEmailButton.isEnabled = false
135-
136-
// Send verification email
137-
val user = auth.currentUser!!
138-
user.sendEmailVerification()
139-
.addOnCompleteListener(requireActivity()) { task ->
140-
// Re-enable button
141-
binding.verifyEmailButton.isEnabled = true
142-
143-
if (task.isSuccessful) {
144-
Toast.makeText(context,
145-
"Verification email sent to ${user.email} ",
146-
Toast.LENGTH_SHORT).show()
147-
} else {
148-
Log.e(TAG, "sendEmailVerification", task.exception)
149-
Toast.makeText(context,
150-
"Failed to send verification email.",
151-
Toast.LENGTH_SHORT).show()
152-
}
153-
}
154-
}
155-
156-
private fun reload() {
157-
auth.currentUser!!.reload().addOnCompleteListener { task ->
158-
if (task.isSuccessful) {
159-
updateUI(auth.currentUser)
160-
Toast.makeText(context, "Reload successful!", Toast.LENGTH_SHORT).show()
161-
} else {
162-
Log.e(TAG, "reload", task.exception)
163-
Toast.makeText(context, "Failed to reload user.", Toast.LENGTH_SHORT).show()
16475
}
16576
}
16677
}
16778

168-
private fun validateForm(): Boolean {
169-
var valid = true
170-
171-
val email = binding.fieldEmail.text.toString()
172-
if (TextUtils.isEmpty(email)) {
173-
binding.fieldEmail.error = "Required."
174-
valid = false
175-
} else {
176-
binding.fieldEmail.error = null
177-
}
178-
179-
val password = binding.fieldPassword.text.toString()
180-
if (TextUtils.isEmpty(password)) {
181-
binding.fieldPassword.error = "Required."
182-
valid = false
183-
} else {
184-
binding.fieldPassword.error = null
185-
}
186-
187-
return valid
188-
}
189-
190-
private fun updateUI(user: FirebaseUser?) {
191-
hideProgressBar()
192-
if (user != null) {
193-
binding.status.text = getString(R.string.emailpassword_status_fmt,
194-
user.email, user.isEmailVerified)
195-
binding.detail.text = getString(R.string.firebase_status_fmt, user.uid)
196-
197-
binding.emailPasswordButtons.visibility = View.GONE
198-
binding.emailPasswordFields.visibility = View.GONE
199-
binding.signedInButtons.visibility = View.VISIBLE
200-
201-
if (user.isEmailVerified) {
202-
binding.verifyEmailButton.visibility = View.GONE
203-
} else {
204-
binding.verifyEmailButton.visibility = View.VISIBLE
205-
}
206-
} else {
207-
binding.status.setText(R.string.signed_out)
208-
binding.detail.text = null
209-
210-
binding.emailPasswordButtons.visibility = View.VISIBLE
211-
binding.emailPasswordFields.visibility = View.VISIBLE
212-
binding.signedInButtons.visibility = View.GONE
213-
}
214-
}
215-
216-
private fun checkForMultiFactorFailure(e: Exception) {
217-
// Multi-factor authentication with SMS is currently only available for
218-
// Google Cloud Identity Platform projects. For more information:
219-
// https://cloud.google.com/identity-platform/docs/android/mfa
220-
if (e is FirebaseAuthMultiFactorException) {
221-
Log.w(TAG, "multiFactorFailure", e)
222-
val resolver = e.resolver
223-
val args = bundleOf(
224-
MultiFactorSignInFragment.EXTRA_MFA_RESOLVER to resolver,
225-
MultiFactorFragment.RESULT_NEEDS_MFA_SIGN_IN to true
226-
)
227-
findNavController().navigate(R.id.action_emailpassword_to_mfa, args)
228-
}
229-
}
230-
23179
override fun onDestroyView() {
23280
super.onDestroyView()
23381
_binding = null
23482
}
235-
236-
companion object {
237-
private const val TAG = "EmailPassword"
238-
private const val RC_MULTI_FACTOR = 9005
239-
}
24083
}

0 commit comments

Comments
 (0)