I am trying to write some basic information about users to a Firebase realtime database, but when running setValue() nothing happens in the console and after many hours of trying I still can't figure out the problem.
This is the code in the fragment that calls the registerUser function:
registerButton.setOnClickListener {
firstNameLayout.error = null
lastNameLayout.error = null
emailFieldLayout.error = null
passwordFieldLayout.error = null
var fieldCheck = true
if(TextUtils.isEmpty(firstNameField.text)) {
firstNameLayout.error = "Please Enter First Name";
fieldCheck = false
}
if (TextUtils.isEmpty(lastNameField.text)) {
lastNameLayout.error = "Please Enter Last Name"
fieldCheck = false
}
if(TextUtils.isEmpty(emailField.text)) {
emailFieldLayout.error = "Please Enter Email Address";
fieldCheck = false
// Todo: Make sure format is correct
}
if(TextUtils.isEmpty(passwordField.text)){
passwordFieldLayout.error = "Please Choose a Password"
fieldCheck = false
// Todo: Stricter password requirements
}
if(!fieldCheck) {
return#setOnClickListener
}
registerButton.startAnimation()
val fName = firstNameField.text.toString().trim()
val lName = lastNameField.text.toString().trim()
val email = emailField.text.toString().trim()
val password = passwordField.text.toString().trim()
try {
registerButton.doneLoadingAnimation(2, bitmap)
(activity as LoginRegister).registerUser(email, password, fName, lName)
} catch (e: Exception) {
Toast.makeText(activity, e.message, Toast.LENGTH_SHORT).show()
registerButton.revertAnimation()
}
}
This is the registerUser function body from the parent activity of the fragment:
fun registerUser(email: String, password: String, fName: String, lName: String) {
//Registrerer med Firebase Authentication
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "createUserWithEmail:success")
val user : FirebaseUser = task.result!!.user!!
val uid: String = user.uid
val dbUser = User(fName, lName, email)
writeNewUser(uid, dbUser)
val intent = Intent(this#LoginRegister, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra("user_email", email)
intent.putExtra("user_first_name", fName)
intent.putExtra("user_last_name", lName)
startActivity(intent)
finish()
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "createUserWithEmail:failure", task.exception)
Toast.makeText(baseContext, "Authentication failed.", Toast.LENGTH_SHORT).show()
}
}
}
This is the writeNewUser function body
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.child("users").child(userId).setValue(user)
}
The User object is instantiated from this kotlin class:
data class User(val fName: String? = null, val lName: String? = null, val email: String? = null) {
val firstName = fName?.capitalize(Locale.ROOT)
val lastName = lName?.capitalize(Locale.ROOT)
val emailAddress = email
}
Firebase:
Firebase nodes
Anyone know the reason behind this?
put the URL of database in the getInstance() was the solution for me, now it works perfect
example:
private val database = FirebaseDatabase.getInstance("https://conect-c91b3-default-rtdb.firebaseio.com/").getReference()
// to test if is working
database.child("users").setValue("test")
I solved it, I was using another database location than the default, and I didn't catch the fact that I needed to pass the database URL in the getInstance() method. Passing the correct database url fixed the issue
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.child("users").child(userId).setValue(user)
}
I guess you duplicate a couple of child nodes in above snippet as it implies a group of users has a single user, and this particular user has a group of users, doesn't make sense, so you can remove these duplicates:
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("Users").child(userId)
database.setValue(user)
}
UPDATE:
The firebase nodes are case-sensitive, so change "Users" to lowercase "users"
fun writeNewUser(userId: String, user: User) {
database = FirebaseDatabase.getInstance().getReference("users").child(userId)
database.setValue(user)
}
Related
Entity class
#Entity(tableName = "user_table")
data class User(
#PrimaryKey(autoGenerate = true)
//....
) {
constructor(
userID: Int
) : this(
userID,
//...
)
}
Dao class
#Dao
interface UserDao {
#Insert(onConflict = IGNORE)
fun addUser(user: User): Long
//.......
}
Repository Class
class RoomUserRepository(context: Context) {
//...
suspend fun addUser(user: User): Long = userDao.addUser(user)
//...
}
ViewMode Class
class UserViewModel(val context: Application) : AndroidViewModel(context) {
//...
var userID: Long = 0
fun addUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
userID = roomUserRepository.addUser(user)
}
}
//...
}
SIGN UP Button onclick{}
clicked = !clicked
if (confirmpassword != password) {
message = "Incorrect password! please try again"
} else if (inputChack(fullname, email, password)) {
message = "Information is incomplete"
} else {
// Create User
user.email = email
user.fullName = fullname
user.password = password
userVM.addUser(user)
// Create Cart
cart.userID = userVM.userID.toInt()
cartVM.addCart(cart)
Log.d("user & cart", "adding users & cart")
}
As you can see when the user click on SIGN UP I will Create User & Cart in my database, user creation work fine but when I create cart I have to pass user ID and I am getting it from userID variable you can find it in ViewMode Class the problem is the value is getting cart.userID = userVM.userID.toInt() then it been updated
Simply I want
After called userVM.addUser(user) I have to wait until the userID variable get updated (look at ViewMode Class) then I get the updated value cart.userID = userVM.userID.toInt()
Then try this:
fun addUser(user: User, callBack: (Long) -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
userID = roomUserRepository.addUser(user)
callBack(userID)
}
}
// Create User
user.email = email
user.fullName = fullname
user.password = password
userVM.addUser(user) { id ->
cart.userID = id
cartVM.addCart(cart)
Log.d("user & cart", "adding users & cart")
}
based on the docs,
the return value of the below function is the rowId, not the userID.
#Insert(onConflict = IGNORE)
fun addUser(user: User): Long
If you want to get the userID, you should write a get function like below.
#Query("SELECT * FROM user_table)
fun getUsers(): Flow<List<User>>
Then, add an observer like below
val users = roomUserRepository.getUsers().asLiveData()
Also don't forget to add the necessary dependencies.
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_version"
How do I retrieve all the fields of the current user logged?
I've watched many tutorials and questions, and some of them talk about the whole collection, others about similar topics, but found no info about this.
Thank you
UPDATE
Current Code:
fun getUserName_FireBase(){
if(userID==null){
println("The userID is null")
userID= getUserID()
println("The userId has been assigned and now is: " + userID.toString())
}
println("1")
val db = FirebaseFirestore.getInstance()
println("1a")
val usersRef = db.collection("users")
println("1b")
usersRef.document(userID.toString()).get().addOnCompleteListener { task ->
println("2")
if (task.isSuccessful) {
println("3")
val document = task.result
if(document!!.exists()){
println("4")
userName = document!!.getString("user").toString()
println("user is " + userName.toString())
}else {
println("5")
Log.d("Error", "This document does not exist")
}
}else {
println("6")
task.exception?.message?.let {
Log.d(TAG, it)
}
}
println("7")
}
println("8")
}
Console error
The error is given because later I need to acces to userName var that is supposed to be filled in that function
To be able to get user data, you have to create a reference that points to that document, perform a get() call and attach a listener, as seen in the following lines of code:
val db = FirebaseFirestore.getInstance()
val usersRef = db.collection("users")
usersRef.document("gA4z1AhkQpQ6J47sIMmCGIZRKDK2").get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document.exists()) {
val email = document.getString("email")
val pass = document.getString("pass")
val user = document.getString("user")
Log.d(TAG,"$email/$pass/$user")
} else {
Log.d(TAG, "The document doesn't exist.")
}
} else {
task.exception?.message?.let {
Log.d(TAG, it)
}
}
}
The result in the logcat will be:
barrooroor#gmail.com/paport/do3fe4232ef2
If "gA4z1AhkQpQ6J47sIMmCGIZRKDK2" is the ID of the user that comes from the authentication process, then instead of the hard coded ID, you can simply use:
val auth = FirebaseAuth.getInstance()
val uid = auth.currentUser?.uid
usersRef.document(uid).get().addOnCompleteListener {/* ... /*}
// 👆
Besides that, something more important, never store sensitive data as passwords in plain text. Malicious users might take advantage of that. Always use Firebase Authentication for that and secure the database using Firestore Security Rules.
I have a datastore in my android app where I am storing my profile details. and retrieving as follows
suspend fun saveUser(user: User) {
dataStore.edit {
it[USER_ID] = user.id
it[USER_NAME] = user.name
it[USER_MOBILE] = user.phone
it[USER_EMAIL] = user.email
it[USER_IMAGE] = user.image
it[USER_ADDRESS] = user.address
}
}
val userDate = dataStore.data
.catch { e ->
if (e is IOException) {
Log.e("PREFERENCE", "Error reading preferences", e)
emit(emptyPreferences())
} else {
throw e
}
}
.map { pref ->
val userId = pref[USER_ID] ?: ""
val userName = pref[USER_NAME] ?: ""
val userEmail = pref[USER_EMAIL] ?: ""
val userImage = pref[USER_IMAGE] ?: ""
val userPhone = pref[USER_MOBILE] ?: ""
val userAddress = pref[USER_ADDRESS] ?: ""
User(
name = userName,
image = userImage,
address = userAddress,
phone = userPhone,
id = userId,
email = userEmail
)
}
Along with it I am saving the availibility status of the User
suspend fun saveIsAvailable(boolean: Boolean) {
dataStore.edit {
it[USER_IS_AVAILABLE] = boolean
}
}
I am collecting user profile details like this in my viewmodel
viewModelScope.launch(Default) {
RiderDataStore.userDate.collect {
user.postValue(it)
}
}
Whenever I change the User availibility my user details flow also gets triggered which is unneccessary and causes ui jittering (image reloads). Why does this happen and how to enable the flow to only trigger if the data changes specifically of the user detail.
This is because you update a user property (in DataStore) and at the same time with userDate.collect you're observing all changes made to the user (in DataStore). Your current code has no way to distinguish between "good" and "bad" updates of the user.
Since you seem to ignore the availability in your DataStore Flow called userDate, your returned User objects should indeed stay identical after availability changes. The default behavior for Kotlin Flow is to emit on every change, even if the data is identical. But you can fix that simply by adding a .distinctUntilChanged() after the map operator like:
val userDate = dataStore.data
.catch { e ->
if (e is IOException) {
Log.e("PREFERENCE", "Error reading preferences", e)
emit(emptyPreferences())
} else {
throw e
}
}
.map { pref ->
val userId = pref[USER_ID] ?: ""
val userName = pref[USER_NAME] ?: ""
val userEmail = pref[USER_EMAIL] ?: ""
val userImage = pref[USER_IMAGE] ?: ""
val userPhone = pref[USER_MOBILE] ?: ""
val userAddress = pref[USER_ADDRESS] ?: ""
User(
name = userName,
image = userImage,
address = userAddress,
phone = userPhone,
id = userId,
email = userEmail
)
}.distinctUntilChanged()
See also docs. It makes sure identical data is not emited over and over.
NOTE: I was able to figure this out. There is no need to change the rules in Firebase. See code below.
ORIGINAL POST
I have an IOS app and I decided to build the Android/Kotlin version and I'm having a hard time with Firebase/isEmailVerify. I'm able to register a new user and send the email for verification, but, if I don't verify, I'm still able to login. I'm new at Kotlin. Any help is greatly appreciated.
UPDATED CODE
class LoginActivity : AppCompatActivity() {
lateinit var auth: FirebaseAuth
private var emailVerifier: Boolean = true
private val emailVerificationAlert = { _: DialogInterface, _: Int ->
Toast.makeText(this.applicationContext, android.R.string.yes, Toast.LENGTH_SHORT).show()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
auth = FirebaseAuth.getInstance()
}
private fun verifyEmail() {
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
emailVerifier = user.isEmailVerified()
}
if (emailVerifier) {
finish()
} else {
userDidNotVerify()
auth.signOut()
}
}
fun loginBtnClicked(view: View) {
val email = loginEmailTxt.text.toString()
val password = loginPasswordTxt.text.toString()
auth.signInWithEmailAndPassword(email, password)
.addOnSuccessListener { exception ->
println("USER LOGGED IN")
verifyEmail()
}
.addOnFailureListener { exception ->
Log.e("Exception", "Could not sign in user - ${exception.localizedMessage}")
}
}
private fun userDidNotVerify() {
val builder = android.app.AlertDialog.Builder(this)
with(builder) {
this.setTitle("Confirm your email address.")
this.setMessage("A confirmation email has been sent to" + " " + (loginEmailTxt.text) + " " +
"." + " " + "Click on the confirmation link to activate your account")
this.setPositiveButton("OK", DialogInterface.OnClickListener(function = emailVerificationAlert))
this.show()
}
}
fun loginCreateClicked(view: View) {
val createIntent = Intent(this, CreateUserActivity::class.java)
startActivity(createIntent)
}
}
It's expected that the user can still sign in before the email is verified. This provides a way for your app to allow the user to request another verification email to be sent, in case something happened to the first one.
If you want to restrict what the user can do before the email is verified, you can check isEmailVerfied() on the UserInfo object, and you can use the auth.token.email_verified in security rules to limit their access to databases and storage also provided by Firebase.
I'm creating an app using Kotlin on Android Studio.
In the app, users will be allowed to add an image, username, and a phone number
to proceed to other activities. The mentioned info should be saved in the app Cloud Firestore (Firebase).
However, while coding the functions for firestore, data is not saved to the database
Can anyone help please?
When I built my app, this is what it showed:
Open the picture
This is my first post on stackoverflow, so let me know if you want to know any addtional infos.
I would appreciate any help from you, guys.
This is my code:
setupBtn.setOnClickListener {
val username: String = setupName.text.toString()
val phoneNumber: String = setupPhoneNumber.text.toString()
if (!TextUtils.isEmpty(username) &&
!TextUtils.isEmpty(phoneNumber)) { //if fields are not empty, proceed. Else,
tell user to fill both fields
setupProgressBar.visibility = View.VISIBLE
val userID = mAuth.currentUser!!.uid // saves user ID
val imagePATH: StorageReference =
storageRef.child("profile_images").child(userID + ".jpg") //store the image
as the user ID
imagePATH.putFile(mainImageURI).addOnCompleteListener {
task ->
if (task.isSuccessful) {
//get the downloadURI of the image and store it
val downloadURI =
task.result.metadata!!.reference!!.downloadUrl.toString()
//A collection stores in the database that has a
1)name .. 2)phone number .. 3)image
val data = HashMap<String, Any>()
data.put("name", username)
data.put("phone number", phoneNumber)
data.put("image", downloadURI)
val docRef =
mFirestore.collection("Users").document(userID).set(data)
docRef.addOnCompleteListener { task ->
if (task.isSuccessful) {
Toast.makeText(this, "User Setting are
updated", Toast.LENGTH_LONG).show()
val intent = Intent(this,
PhotoBlog::class.java)
startActivity(intent)
finish()
} else {
val errorMessage: String? =
task.exception!!.message
Toast.makeText(this, "Database Error:
$errorMessage", Toast.LENGTH_LONG).show()
}
}
} else {
val errorMessage: String? =
task.exception!!.message
Toast.makeText(this, "Image Error:
$errorMessage", Toast.LENGTH_LONG).show()
}
setupProgressBar.visibility = View.INVISIBLE
}
} else {
Toast.makeText(this, "Please, fill both fields",
Toast.LENGTH_LONG).show()
}
}
}
I also imported the needed libraries, and defined a firestore variable
private lateinit var mFirestore: FirebaseFirestore
mFirestore = FirebaseFirestore.getInstance()