I have created an app in Android Studio (Kotlin) that registers users:
User Interface
An email/password user is created (Authentication) along with a corresponding database record (Realtime Database):
Datbase Structure
I have the login procedure working and I'm able to display the logged-in user's email address on MainActivity, BUT, I don't know how to display the user's name and favourite colour (from the database).
Can anyone help?
Thanks in advance for your time.
RegisterActivity.kt
class RegisterActivity : AppCompatActivity() {
private lateinit var mAuth : FirebaseAuth
private lateinit var viewModel: UserViewModel
public val USER: String = "user"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_register)
mAuth = FirebaseAuth.getInstance()
viewModel = ViewModelProvider(this).get(UserViewModel::class.java)
register_button.setOnClickListener {
val email = register_email.text.toString().trim()
val password = register_password.text.toString().trim()
val name = register_name.text.toString().trim()
val colour = register_colour.text.toString().trim()
mAuth.createUserWithEmailAndPassword(email, password)
val user = User()
user.email = email
user.name = name
user.colour = colour
viewModel.addUser(user)
}
}
}
LoginActivity.kt
class LoginActivity : AppCompatActivity() {
private lateinit var mAuth : FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mAuth = FirebaseAuth.getInstance()
login_button.setOnClickListener {
val email = login_email.text.toString().trim()
val password = login_password.text.toString().trim()
mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
//Login Success
val intent = Intent (this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TASK
}
startActivity(intent)
}else{
//Login Failure
task.exception?.message?.let {
toast(it)
}
}
}
}
}
}
MainActivity.kt (to display User details)
class MainActivity : AppCompatActivity() {
private val currentUser = FirebaseAuth.getInstance().currentUser
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
currentUser?.let { user ->
main_email.setText(user.email)
main_name.setText("Don't Know")
main_colour.setText("Don't Know")
}
}
}
Edit 3
LoginActivity.kt
class LoginActivity : AppCompatActivity() {
private lateinit var mAuth : FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
mAuth = FirebaseAuth.getInstance()
login_button.setOnClickListener {
val email = login_email.text.toString().trim()
val password = login_password.text.toString().trim()
loginUser (email, password)
}
}
private fun loginUser(email: String, password: String) {
mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
//Login Success
val intent = Intent(this,MainActivity::class.java)
intent.putExtra("username",user.displayName)
intent.putExtra("email",user.email)
startActivity(intent)
}else{
//Login Failure
task.exception?.message?.let {
toast(it)
}
}
}
}
override fun onStart() {
super.onStart()
mAuth.currentUser?.let {
login()
}
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
//I have commented out the line below because it may not be required...
// private val currentUser = FirebaseAuth.getInstance().currentUser
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//I have commented out the lines below because they may not be required...
//currentUser?.let { user ->
//main_email.setText(user.email)
//main_name.setText("Don't Know")
//main_colour.setText("Don't Know")
//}
}
}
ERROR 1 : Overload resolution ambiguity on putExtra
ERROR 2 : Unresolved Reference on user.
When you launch at your login stage
mAuth.signInWithEmailAndPassword(email, password)
It has a callback that will be completed and successful when the user has been logged in, inside that callback, you are already logged in and that user now corresponds to one unique user ID, you can access that user ID by doing
val user = FirebaseAuth().getInstance().currentUser.uid
Then, here you can fetch that user data and pass it as an extra or bundle to your MainActivity, so the login process will take the time to log the user, fetch its data and send it to the MainActivity, this way you are not delaying any more time the user at your MainActivity
You can see that the user has been created once that successful callback is launch, and then in the Firebase console you can check for that user in the authentication tab
Edit
mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
//Login Success, here you can get currentUser
val user = FirebaseAuth().getInstance().currentUser
login(user.displayName,user.email)
}else{
//Login Failure
task.exception?.message?.let {
toast(it)
}
}
}
Then, just add these two parameters to the login() method and just do a putExtra with those values and send those values as an extra to the main activity, so you are not fetching in your mainactivity and just sending the values from the login
Keep in mind that after .addonCompleteListener() the result is your current logged in user, so from here, you can get the currentUser information or the currentUser id
Edit 2
Inside the onComplete , just pass the data to the MainActivity
val intent = Intent(this,MainActivity::class.java)
intent.putExtra("username",user.displayName)
intent.putExtra("email",user.email)
startActivity(intent)
Then in your MainActivity get this extras
val extras = getIntent().extras
val userName: String?
val email: String?
if (extras != null) {
userName = extras.getString("userame")
email = extras.getString("email")
}
Then you already have the data from login into your MainActivity
Edit 3
To change the color, just change it from the MainActivity
your_text_view.setTextColor(ContextCompat.getColor(this,R.color.yourColor)
Please read this
Related
My Authentication is working fine but I am not able to store and retrive data from realtime database. Below is the firebase rule(I am using locked mode and Usa server) and my signup Activity class. Please help me with this thing. I am taking database refernce and giving setValue method but still its not working. I have also tried different solution given on stackoverflow flow but still no luck.
Firebase Rule
{
"rules": {
".read": "true", // 2022-8-22
".write": "true"
}
}
SignUp Acitvity
class SignUpActivity : AppCompatActivity() {
private lateinit var mAuth : FirebaseAuth
private lateinit var mDBRef : DatabaseReference
private lateinit var binding: ActivitySignIntoBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySignIntoBinding.inflate(layoutInflater)
setContentView(binding.root)
mAuth = FirebaseAuth.getInstance()
binding.edtSignUp.setOnClickListener{
val name = binding.edtName.text.toString()
val email = binding.edtEmail.text.toString()
val password = binding.edtPassword.text.toString()
signUp(name, email, password)
}
binding.tvLogin.setOnClickListener {
val intent = Intent(this, Login::class.java)
finish()
startActivity(intent)
}
}
private fun signUp(name: String, email: String, password: String) {
mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
addUserToDatabase(name, email, mAuth.currentUser?.uid!!)
val intent = Intent(this#SignUpActivity, MainActivity::class.java)
startActivity(intent)
} else {
Toast.makeText(this#SignUpActivity, "Some Error Occurred. Try Again", Toast.LENGTH_LONG ).show()
}
}
}
private fun addUserToDatabase(name: String, email: String, uid: String) {
mDBRef = FirebaseDatabase.getInstance("https://chatapplication-ad542-default-rtdb.firebaseio.com/").getReference()
mDBRef.child("User").child(uid).push().setValue(User(name, email, uid))
}
}
i have 3 progress activities for signup. I take the informations at first activity and then trying to send directly third activity to signup with firebase. I tried every way that i research but i can't figure it out.
This is my first activity
signUpNextButton.setOnClickListener {
val name = signUpFullname.text.toString()
val email = signUpEmail.text.toString()
val password = signUpPassword.text.toString()
val repassword = signUpPasswordRepeat.text.toString()
//Sending datas with this method
val intent = Intent(this, SignUp_Page3::class.java)
intent.putExtra("user_email", email)
intent.putExtra("user_password", password)
startActivity(Intent(this, SignUp_Page2::class.java))
}
this is the second one
signUpGetCode.setOnClickListener {
startActivity(Intent(this, SignUp_Page3::class.java)) }
and this is the last one:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.signup3)
val intent = getIntent()
val email = intent.getStringExtra("user_email")
val password = intent.getStringExtra("user_password")
signUpComplete.setOnClickListener {
//Firebase Authentication to create user
FirebaseAuth.getInstance()
.createUserWithEmailAndPassword(
email,
password
)
.addOnCompleteListener {
if (it.isSuccessful) {
startActivity(Intent(this, LogIn_Page::class.java))
Toast.makeText(
this,
"Account successfully created, please log in.",
Toast.LENGTH_LONG
).show()
finish()
} else {
Toast.makeText(this, "Something went wrong.", Toast.LENGTH_SHORT).show()
return#addOnCompleteListener
}
}
}
It looks like you are passing to the second page via Intent, but never pass from second to third page.
First retrieve the info in your second and third page.
private var email: String? = null
private var password: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (intent != null) {
email = intent.getStringExtra("user_email")
password = intent.getStringExtra("user_password")
} else if (savedInstanceState != null){
email = savedInstanceState.getString("user_email")
password = savedInstanceState.getString("user_password")
}
//TODO rest of onCreate
}
Try passing the info into the third page Intent.
Be careful about storing this data in the Intent. If you don't implement onSaveInstanceState in your second and third page, then you are going to lose the data on a rotation.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("user_email", email)
outState.putString("user_password", password)
}
Also consider using Static String constants for your keys it will make everything much more manageable.
First1:
signUpNextButton.setOnClickListener {
val name = signUpFullname.text.toString()
val email = signUpEmail.text.toString()
val password = signUpPassword.text.toString()
//Sending datas with this method
val intent = Intent(this, SignUp_Page2::class.java)
intent.putExtra("user_email", email)
intent.putExtra("user_password", password)
//Starting another activity
startActivity(intent)
}
second1:
private var email: String? = null
private var password: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.signup2)
val intent = getIntent()
if (intent != null) {
email = intent.getStringExtra("user_email")
password = intent.getStringExtra("user_password")
} else if (savedInstanceState != null){
email = savedInstanceState.getString("user_email")
password = savedInstanceState.getString("user_password")
}
signUpGetCode.setOnClickListener {
val phone = signUpPhoneNumber.text.toString()
startActivity(Intent(this, SignUp_Page3::class.java))
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("user_email", email)
outState.putString("user_password", password)
}
third1:
private var email: String? = null
private var password: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.signup3)
val intent = getIntent()
if (intent != null) {
email = intent.getStringExtra("user_email")
password = intent.getStringExtra("user_password")
} else if (savedInstanceState != null) {
email = savedInstanceState.getString("user_email")
password = savedInstanceState.getString("user_password")
}
signUpComplete.setOnClickListener {
Toast.makeText(this, email + password, Toast.LENGTH_SHORT).show()
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("user_email", email)
outState.putString("user_password", password)
}
Toast message give me the null null output.
FirebaseAuth.getInstance()
.createUserWithEmailAndPassword(
email.toString(),
password.toString()
)
.addOnCompleteListener {
if (it.isSuccessful) {
startActivity(Intent(this, LogIn_Page::class.java))
Toast.makeText(
this,
"Account successfully created, please log in.",
Toast.LENGTH_LONG
).show()
} else {
Toast.makeText(this, "Something went wrong.", Toast.LENGTH_SHORT).show()
return#addOnCompleteListener
}
}
this returns "something went wrong" to me
I am trying to store data in my Firebase database but the app crashes when I call the function saveUserToFirebaseDatabase(). The performRegister() function works fine which stores the email and password using FirebaseAuth but when I call performRegister() then saveUserToFirebaseDatabase() right under it, the performRegister() doesn't execute all the way though. performRegister() is suppose to make a log.d tag which tells the uid and that it successfully stored the email and password. It does that but when I call saveUserToFirebaseDatabase() under performRegister() in the onCreate function, it doesn't log the uid like it did without the saveUserToFirebaseDatabase(). Instead the performRegister() Log displays the username and passowrd then skips to the saveUserToFirebaseDatabase() which logs display the username, first name, and last name from the previous activities the user had to enter when signing up for the account. Then it crashes. Here is my code. I want to be able to call performRegister() then saveUserToFirebaseDatabase() to save the user's email and password using FirebaseAuth then save the user's other information in the database using saveUserToFirebaseDatabase().
class Password_Activity : AppCompatActivity() {
lateinit var editPasswordText: EditText
lateinit var editConfirmPasswordText: EditText
lateinit var btnOpenActivity: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_password)
editPasswordText = findViewById(R.id.edit_password)
editConfirmPasswordText = findViewById(R.id.edit_confirm_password)
btnOpenActivity = findViewById(R.id.password_continue_btn)
// btnOpenActivity.isEnabled=false
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
var passwordinput: String = editPasswordText.text.toString().trim()
var confirmpasswordinput: String = editConfirmPasswordText.text.toString().trim()
btnOpenActivity.isEnabled = passwordinput.isNotEmpty() && confirmpasswordinput.isNotEmpty()
}
}
editPasswordText.addTextChangedListener(textWatcher)
editConfirmPasswordText.addTextChangedListener(textWatcher)
// var intent = intent
// val username = intent.getStringExtra("username")
// val firstname = intent.getStringExtra("firstname")
// val lastname = intent.getStringExtra("lastname")
// val emailaddress = intent.getStringExtra("emailaddress")
val btnOpenActivity: Button = findViewById(R.id.password_continue_btn)
btnOpenActivity.setOnClickListener {
performRegister()
saveUserToFirebaseDatabase()
// val intent = Intent(this, Main2Activity_welcome_after_signup_dashboard::class.java)
// startActivity(intent)
// overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left)
}
}
// Save user's email and password
private fun performRegister(){
var intent = intent
val email = intent.getStringExtra("email")
val password = editPasswordText.text.toString().trim()
if (email.isEmpty() && password.isEmpty()) {
Toast.makeText(this, "Please enter email and password", Toast.LENGTH_SHORT).show()
return
}
Log.d("SignUpActivity","Email is "+ email)
Log.d("SignUpActivity","Password is $password")
// Initialize Firebase Auth
FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password)
.addOnCompleteListener {
if (!it.isSuccessful) {
Log.d("SignUp", "Account creation unsuccessful")
return#addOnCompleteListener
}
//else if successful
Log.d("SignUp", "Successfully created user with uid: ${it.result?.user?.uid}")
}
.addOnFailureListener {
Log.d("SignUp", "Failed to create user: ${it.message}")
}
}
// Save user's username, first name, and last name to database
private fun saveUserToFirebaseDatabase(){
var intent = intent
val username = intent.getStringExtra("username")
val firstname = intent.getStringExtra("firstname")
val lastname = intent.getStringExtra("lastname")
Log.d("Signup","Storing user information in password activity: $username, $firstname, $lastname")
val uid = FirebaseAuth.getInstance().uid ?: ""
val ref = FirebaseDatabase.getInstance().getReference("/users/$uid")
val user = User(uid, username, firstname, lastname)
ref.setValue(user)
.addOnSuccessListener {
Log.d("Signup","We saved the user to Firebase Database")
}
.addOnFailureListener {
Log.d("Signup", "Failed to save user to Firebase Database")
return#addOnFailureListener
}
}
//Animation for back button
override fun finish() {
super.finish()
overridePendingTransition(R.anim.slide_in_left,R.anim.slide_out_right)
}
}
data class User(val uid: String, val username: String, val firstname: String, val lastname: String)
Your performRegister call happens aysnc in the background. This is why saveUserToFirebaseDatabase is getting called before it completes. Instead, just call it when performRegister is complete in the onCompleteListener.
Change this:
btnOpenActivity.setOnClickListener {
performRegister()
saveUserToFirebaseDatabase()
}
To:
btnOpenActivity.setOnClickListener {
performRegister()
}
and add saveUserToFirebaseDatabase() to:
.addOnCompleteListener {
if (!it.isSuccessful) {
Log.d("SignUp", "Account creation unsuccessful")
return#addOnCompleteListener
}
//else if successful
saveUserToFirebaseDatabase()
Log.d("SignUp", "Successfully created user with uid: ${it.result?.user?.uid}")
}
So I have followed the advice of 'yoursTruly' and created an AuthStateListener in my Activity (as described here: https://www.youtube.com/watch?v=6CXUNcsQPgQ&feature=youtu.be).
This works really well, in the fact that the Activity is simply listening to Authentication and will divert to next Activity if someone logs in and the Fragment simply calls signIn(email, password) via the ViewModel.
However, can not see a way to look for failed login attempts (so the UI will just look like it is unresponsive).
My structure is as follows: Activity -> Fragment -> ViewModel -> Repository.
I'm using DataBinding & Navigation.
Activity
class LoginActivity : AppCompatActivity(), FirebaseAuth.AuthStateListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.login_activity)
}
override fun onStart() {
super.onStart()
FirebaseAuth.getInstance().addAuthStateListener(this)
}
override fun onStop() {
super.onStop()
FirebaseAuth.getInstance().removeAuthStateListener(this)
}
override fun onAuthStateChanged(firebaseAuth: FirebaseAuth) {
// Will only fire if state has changed!
if (FirebaseAuth.getInstance().currentUser == null) {
Toast.makeText(this,"Welcome to the Locators App!\n\nPlease login to continue", Toast.LENGTH_LONG).show()
return
}
firebaseAuth.currentUser?.getIdToken(true)
?.addOnSuccessListener { result ->
val idToken = result.token
Toast.makeText(this,"User Signed In", Toast.LENGTH_LONG).show()
Log.d(TAG, "GetTokenResult result (check this at https://jwt.io/ = $idToken")
goToSiteActivity()
}
}
private fun goToSiteActivity() {
val intent = Intent(this, SiteActivity::class.java)
startActivity(intent)
finish()
}
}
Fragment
class LoginFragment : Fragment() {
private lateinit var loginViewModel: LoginViewModel
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val binding: LoginFragmentBinding = DataBindingUtil.inflate(
inflater, R.layout.login_fragment, container, false)
binding.apply {
loginPasswordResetText.setOnClickListener{
findNavController().navigate(R.id.action_loginFragment_to_loginPasswordResetFragment)
}
loginButton.setOnClickListener{
loginProgressBar.visibility = View.VISIBLE
val email = loginEmailEditText.text.toString()
val password = loginPasswordEditText.text.toString()
if(validateForm(email, password)) {
loginViewModel.loginUser(email, password)
}
loginProgressBar.visibility = View.GONE
}
loginNewUserText.setOnClickListener{
findNavController().navigate(R.id.action_loginFragment_to_loginUserRegisterFragment)
}
}
return binding.root
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
loginViewModel = ViewModelProviders.of(requireActivity()).get(LoginViewModel::class.java)
}
private fun validateForm(email: String, password: String): Boolean {
var validForm = true
if (!Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
loginEmailEditText.error = "Please enter valid email address!"
validForm = false
} else loginEmailEditText.error = null
if (password.isEmpty()) {
loginPasswordEditText.error = "Please enter password!"
validForm = false
} else loginPasswordEditText.error = null
Log.d(TAG,"validateForm: (email = $email, password = $password, validateForm = $validForm)")
return validForm
}
}
ViewModel
class LoginViewModel : ViewModel() {
fun loginUser (email: String, password: String) {
firestoreRepository.loginUser(email, password)
}
}
Repository
class FirestoreRepository {
var firebaseAuth = FirebaseAuth.getInstance()
var firebaseUser = firebaseAuth.currentUser
var failedLogin: Boolean = false
fun loginUser(email: String, password: String) {
failedLogin = false
firebaseAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener {
if (it.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithEmail:success")
} else {
// If sign in fails, display a message to the user.
Log.d(TAG, "signInWithEmail:failure", it.exception)
failedLogin = true
}
}
}
}
This question ties in with a larger query of, if you create LiveData in Repo, how do you Observe from ViewModel (i.e. what do you use as the LifeCycleOwner?), but I will ask on a seperate question..
An AuthStateListener is not sufficient to determine when a sign-in fails. It will only tell you when the state of the user changes between signed in and signed out.
You will have to use the result of firebaseAuth.signInWithEmailAndPassword to determine if the sign-in failed. It looks like you already have some code there to handle that case, but you're not doing much with the error other than setting a property.
What you should do instead is have your loginUser ViewModel method return a LiveData that gets notified when the sign-in succeeds or fails. You will have to wire up the call to signInWithEmailAndPassword to change the state of that LiveData, and your view will have to observe that LiveData to show a message to the user if necessary.
I tried to get the data from the firebase cloud database, but the data didn't show out.
This is the code I try to retrieve data
private fun updateUI(currentUser: FirebaseUser?){
if(currentUser!=null){
fstore.collection("users")
.document(auth.currentUser!!.uid)
.get()
.addOnCompleteListener { task->
task.result!!.get("name") == name.text.toString();
task.result!!.get("email") == email.text.toString();
Toast.makeText(baseContext, "Haha", Toast.LENGTH_LONG).show()
}
}else{
Toast.makeText(baseContext, "fail", Toast.LENGTH_LONG).show()
}
}
Calling UpdateUI() form activity
class MainActivity : AppCompatActivity() {
lateinit var name: TextView
lateinit var email: TextView
private lateinit var fstore: FirebaseFirestore
private lateinit var auth : FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
name = findViewById(R.id.name)
email = findViewById(R.id.email)
auth = FirebaseAuth.getInstance()
fstore = FirebaseFirestore.getInstance()
updateUI(auth.currentUser)
}
firebase cloud database:
android simulator after I tried:
Your document id assigning is wrong
.document(doucumentID) //Not user ID
//Here your document id is
//JXoTcqxIVENTSF2.....
//As I can see your firestore image
and you are assigning data in a wrong way to TextView
name.text = "value you want to assign"
So edit your code like this
private fun updateUI(currentUser: FirebaseUser?){
if(currentUser!=null){
fstore.collection("users")
.document("JXoTcqxIVENTSF2..")
//can not see full id in image so I use '....'
//replace it with full id from firestore
.get()
.addOnCompleteListener { task->
name.text = task.result!!.get("name").toString()
email.text = task.result!!.get("email").toString()
Toast.makeText(baseContext, "Haha", Toast.LENGTH_LONG).show()
}
}else{
Toast.makeText(baseContext, "fail", Toast.LENGTH_LONG).show()
}
}